go-snark/circuitcompiler/lexer.go
circuitcompiler/lexer.goRaw
1package circuitcompiler
2
3import (
4 "bufio"
5 "bytes"
6 "io"
7)
8
9type OperatorSymbol int
10type Token int
11
12const (
13 ILLEGAL Token = iota
14 WS
15 EOF
16
17 IDENT // val
18
19 VAR // var
20 CONST // const value
21
22 EQ // =
23 PLUS // +
24 MINUS // -
25 MULTIPLY // *
26 DIVIDE // /
27 EXP // ^
28
29 OUT
30)
31
32var eof = rune(0)
33
34func isWhitespace(ch rune) bool {
35 return ch == ' ' || ch == '\t' || ch == '\n'
36}
37
38func isLetter(ch rune) bool {
39 return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
40}
41func isDigit(ch rune) bool {
42 return (ch >= '0' && ch <= '9')
43}
44
45type Scanner struct {
46 r *bufio.Reader
47}
48
49func NewScanner(r io.Reader) *Scanner {
50 return &Scanner{r: bufio.NewReader(r)}
51}
52
53func (s *Scanner) read() rune {
54 ch, _, err := s.r.ReadRune()
55 if err != nil {
56 return eof
57 }
58 return ch
59}
60
61func (s *Scanner) unread() {
62 _ = s.r.UnreadRune()
63}
64
65func (s *Scanner) Scan() (tok Token, lit string) {
66 ch := s.read()
67
68 if isWhitespace(ch) {
69 // space
70 s.unread()
71 return s.scanWhitespace()
72 } else if isLetter(ch) {
73 // letter
74 s.unread()
75 return s.scanIndent()
76 } else if isDigit(ch) {
77 s.unread()
78 return s.scanIndent()
79 }
80
81 switch ch {
82 case eof:
83 return EOF, ""
84 case '=':
85 return EQ, "="
86 case '+':
87 return PLUS, "+"
88 case '-':
89 return MINUS, "-"
90 case '*':
91 return MULTIPLY, "*"
92 case '/':
93 return DIVIDE, "/"
94 case '^':
95 return EXP, "^"
96 }
97
98 return ILLEGAL, string(ch)
99}
100
101func (s *Scanner) scanWhitespace() (token Token, lit string) {
102 var buf bytes.Buffer
103 buf.WriteRune(s.read())
104
105 for {
106 if ch := s.read(); ch == eof {
107 break
108 } else if !isWhitespace(ch) {
109 s.unread()
110 break
111 } else {
112 _, _ = buf.WriteRune(ch)
113 }
114 }
115 return WS, buf.String()
116}
117
118func (s *Scanner) scanIndent() (tok Token, lit string) {
119 var buf bytes.Buffer
120 buf.WriteRune(s.read())
121
122 for {
123 if ch := s.read(); ch == eof {
124 break
125 } else if !isLetter(ch) && !isDigit(ch) {
126 s.unread()
127 break
128 } else {
129 _, _ = buf.WriteRune(ch)
130 }
131 }
132 switch buf.String() {
133 case "var":
134 return VAR, buf.String()
135 }
136
137 if len(buf.String()) == 1 {
138 return Token(rune(buf.String()[0])), buf.String()
139 }
140 if buf.String() == "out" {
141 return OUT, buf.String()
142 }
143 return IDENT, buf.String()
144}