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.

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