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