You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

360 lines
7.4 KiB

  1. package test
  2. import (
  3. "bufio"
  4. "bytes"
  5. "fmt"
  6. "io"
  7. "sort"
  8. "strconv"
  9. "github.com/hermeznetwork/hermez-node/common"
  10. )
  11. var eof = rune(0)
  12. var errof = fmt.Errorf("eof in parseline")
  13. var ecomment = fmt.Errorf("comment in parseline")
  14. const (
  15. ILLEGAL token = iota
  16. WS
  17. EOF
  18. IDENT // val
  19. )
  20. type Instruction struct {
  21. Literal string
  22. From string
  23. To string
  24. Amount uint64
  25. Fee uint8
  26. TokenID common.TokenID
  27. Type common.TxType // D: Deposit, T: Transfer, E: ForceExit
  28. }
  29. type Instructions struct {
  30. Instructions []*Instruction
  31. Accounts []string
  32. TokenIDs []common.TokenID
  33. }
  34. func (i Instruction) String() string {
  35. buf := bytes.NewBufferString("")
  36. switch i.Type {
  37. case common.TxTypeCreateAccountDeposit:
  38. fmt.Fprintf(buf, "Type: Create&Deposit, ")
  39. case common.TxTypeTransfer:
  40. fmt.Fprintf(buf, "Type: Transfer, ")
  41. case common.TxTypeForceExit:
  42. fmt.Fprintf(buf, "Type: ForceExit, ")
  43. default:
  44. }
  45. fmt.Fprintf(buf, "From: %s, ", i.From)
  46. if i.Type == common.TxTypeTransfer {
  47. fmt.Fprintf(buf, "To: %s, ", i.To)
  48. }
  49. fmt.Fprintf(buf, "Amount: %d, ", i.Amount)
  50. if i.Type == common.TxTypeTransfer {
  51. fmt.Fprintf(buf, "Fee: %d, ", i.Fee)
  52. }
  53. fmt.Fprintf(buf, "TokenID: %d,\n", i.TokenID)
  54. return buf.String()
  55. }
  56. func (i Instruction) Raw() string {
  57. buf := bytes.NewBufferString("")
  58. fmt.Fprintf(buf, "%s", i.From)
  59. if i.Type == common.TxTypeTransfer {
  60. fmt.Fprintf(buf, "-%s", i.To)
  61. }
  62. fmt.Fprintf(buf, " (%d)", i.TokenID)
  63. if i.Type == common.TxTypeForceExit {
  64. fmt.Fprintf(buf, "E")
  65. }
  66. fmt.Fprintf(buf, ":")
  67. fmt.Fprintf(buf, " %d", i.Amount)
  68. if i.Type == common.TxTypeTransfer {
  69. fmt.Fprintf(buf, " %d", i.Fee)
  70. }
  71. return buf.String()
  72. }
  73. type token int
  74. type scanner struct {
  75. r *bufio.Reader
  76. }
  77. func isWhitespace(ch rune) bool {
  78. return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == '\v' || ch == '\f'
  79. }
  80. func isLetter(ch rune) bool {
  81. return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
  82. }
  83. func isComment(ch rune) bool {
  84. return ch == '/'
  85. }
  86. func isDigit(ch rune) bool {
  87. return (ch >= '0' && ch <= '9')
  88. }
  89. // NewScanner creates a new scanner with the given io.Reader
  90. func NewScanner(r io.Reader) *scanner {
  91. return &scanner{r: bufio.NewReader(r)}
  92. }
  93. func (s *scanner) read() rune {
  94. ch, _, err := s.r.ReadRune()
  95. if err != nil {
  96. return eof
  97. }
  98. return ch
  99. }
  100. func (s *scanner) unread() {
  101. _ = s.r.UnreadRune()
  102. }
  103. // scan returns the token and literal string of the current value
  104. func (s *scanner) scan() (tok token, lit string) {
  105. ch := s.read()
  106. if isWhitespace(ch) {
  107. // space
  108. s.unread()
  109. return s.scanWhitespace()
  110. } else if isLetter(ch) || isDigit(ch) {
  111. // letter/digit
  112. s.unread()
  113. return s.scanIndent()
  114. } else if isComment(ch) {
  115. // comment
  116. s.unread()
  117. return s.scanIndent()
  118. }
  119. if ch == eof {
  120. return EOF, ""
  121. }
  122. return ILLEGAL, string(ch)
  123. }
  124. func (s *scanner) scanWhitespace() (token token, lit string) {
  125. var buf bytes.Buffer
  126. buf.WriteRune(s.read())
  127. for {
  128. if ch := s.read(); ch == eof {
  129. break
  130. } else if !isWhitespace(ch) {
  131. s.unread()
  132. break
  133. } else {
  134. _, _ = buf.WriteRune(ch)
  135. }
  136. }
  137. return WS, buf.String()
  138. }
  139. func (s *scanner) scanIndent() (tok token, lit string) {
  140. var buf bytes.Buffer
  141. buf.WriteRune(s.read())
  142. for {
  143. if ch := s.read(); ch == eof {
  144. break
  145. } else if !isLetter(ch) && !isDigit(ch) {
  146. s.unread()
  147. break
  148. } else {
  149. _, _ = buf.WriteRune(ch)
  150. }
  151. }
  152. if len(buf.String()) == 1 {
  153. return token(rune(buf.String()[0])), buf.String()
  154. }
  155. return IDENT, buf.String()
  156. }
  157. // Parser defines the parser
  158. type Parser struct {
  159. s *scanner
  160. buf struct {
  161. tok token
  162. lit string
  163. n int
  164. }
  165. }
  166. // NewParser creates a new parser from a io.Reader
  167. func NewParser(r io.Reader) *Parser {
  168. return &Parser{s: NewScanner(r)}
  169. }
  170. func (p *Parser) scan() (tok token, lit string) {
  171. // if there is a token in the buffer return it
  172. if p.buf.n != 0 {
  173. p.buf.n = 0
  174. return p.buf.tok, p.buf.lit
  175. }
  176. tok, lit = p.s.scan()
  177. p.buf.tok, p.buf.lit = tok, lit
  178. return
  179. }
  180. func (p *Parser) scanIgnoreWhitespace() (tok token, lit string) {
  181. tok, lit = p.scan()
  182. if tok == WS {
  183. tok, lit = p.scan()
  184. }
  185. return
  186. }
  187. // parseLine parses the current line
  188. func (p *Parser) parseLine() (*Instruction, error) {
  189. // line can be Deposit:
  190. // A (1): 10
  191. // or Transfer:
  192. // A-B (1): 6
  193. // or Withdraw:
  194. // A (1) E: 4
  195. c := &Instruction{}
  196. tok, lit := p.scanIgnoreWhitespace()
  197. if tok == EOF {
  198. return nil, errof
  199. }
  200. c.Literal += lit
  201. if lit == "/" {
  202. _, _ = p.s.r.ReadString('\n')
  203. return nil, ecomment
  204. }
  205. c.From = lit
  206. _, lit = p.scanIgnoreWhitespace()
  207. c.Literal += lit
  208. if lit == "-" {
  209. // transfer
  210. _, lit = p.scanIgnoreWhitespace()
  211. c.Literal += lit
  212. c.To = lit
  213. c.Type = common.TxTypeTransfer
  214. _, lit = p.scanIgnoreWhitespace() // expect (
  215. c.Literal += lit
  216. if lit != "(" {
  217. line, _ := p.s.r.ReadString('\n')
  218. c.Literal += line
  219. return c, fmt.Errorf("Expected '(', found '%s'", lit)
  220. }
  221. } else {
  222. c.Type = common.TxTypeCreateAccountDeposit
  223. }
  224. _, lit = p.scanIgnoreWhitespace()
  225. c.Literal += lit
  226. tidI, err := strconv.Atoi(lit)
  227. if err != nil {
  228. line, _ := p.s.r.ReadString('\n')
  229. c.Literal += line
  230. return c, err
  231. }
  232. c.TokenID = common.TokenID(tidI)
  233. _, lit = p.scanIgnoreWhitespace() // expect )
  234. c.Literal += lit
  235. if lit != ")" {
  236. line, _ := p.s.r.ReadString('\n')
  237. c.Literal += line
  238. return c, fmt.Errorf("Expected ')', found '%s'", lit)
  239. }
  240. _, lit = p.scanIgnoreWhitespace() // expect ':' or 'E' (Exit type)
  241. c.Literal += lit
  242. if lit == "E" {
  243. c.Type = common.TxTypeForceExit
  244. _, lit = p.scanIgnoreWhitespace() // expect ':'
  245. c.Literal += lit
  246. }
  247. if lit != ":" {
  248. line, _ := p.s.r.ReadString('\n')
  249. c.Literal += line
  250. return c, fmt.Errorf("Expected ':', found '%s'", lit)
  251. }
  252. tok, lit = p.scanIgnoreWhitespace()
  253. c.Literal += lit
  254. amount, err := strconv.Atoi(lit)
  255. if err != nil {
  256. line, _ := p.s.r.ReadString('\n')
  257. c.Literal += line
  258. return c, err
  259. }
  260. c.Amount = uint64(amount)
  261. if c.Type == common.TxTypeTransfer {
  262. tok, lit = p.scanIgnoreWhitespace()
  263. c.Literal += lit
  264. fee, err := strconv.Atoi(lit)
  265. if err != nil {
  266. line, _ := p.s.r.ReadString('\n')
  267. c.Literal += line
  268. return c, err
  269. }
  270. if fee > 255 {
  271. line, _ := p.s.r.ReadString('\n')
  272. c.Literal += line
  273. return c, fmt.Errorf("Fee %d can not be bigger than 255", fee)
  274. }
  275. c.Fee = uint8(fee)
  276. }
  277. if tok == EOF {
  278. return nil, errof
  279. }
  280. return c, nil
  281. }
  282. func idxTokenIDToString(idx string, tid common.TokenID) string {
  283. return idx + strconv.Itoa(int(tid))
  284. }
  285. // Parse parses through reader
  286. func (p *Parser) Parse() (Instructions, error) {
  287. var instructions Instructions
  288. i := 0
  289. accounts := make(map[string]bool)
  290. tokenids := make(map[common.TokenID]bool)
  291. for {
  292. instruction, err := p.parseLine()
  293. if err == errof {
  294. break
  295. }
  296. if err == ecomment {
  297. i++
  298. continue
  299. }
  300. if err != nil {
  301. return instructions, fmt.Errorf("error parsing line %d: %s, err: %s", i, instruction.Literal, err.Error())
  302. }
  303. instructions.Instructions = append(instructions.Instructions, instruction)
  304. accounts[idxTokenIDToString(instruction.From, instruction.TokenID)] = true
  305. if instruction.Type == common.TxTypeTransfer { // type: Transfer
  306. accounts[idxTokenIDToString(instruction.To, instruction.TokenID)] = true
  307. }
  308. tokenids[instruction.TokenID] = true
  309. i++
  310. }
  311. for a := range accounts {
  312. instructions.Accounts = append(instructions.Accounts, a)
  313. }
  314. sort.Strings(instructions.Accounts)
  315. for tid := range tokenids {
  316. instructions.TokenIDs = append(instructions.TokenIDs, tid)
  317. }
  318. return instructions, nil
  319. }