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