-
Notifications
You must be signed in to change notification settings - Fork 0
/
lex.go
189 lines (173 loc) · 4.14 KB
/
lex.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
package main
import (
"fmt"
"math"
"strconv"
"strings"
"unicode"
"unicode/utf8"
)
const eof = -1
// Token is an alias for clarity but yacc requires
// the tokens to be defined in the grammar.
type Token int
var builtins = make(map[string]func(float64) float64)
var consts = make(map[string]float64)
// HocLex implements the HocLexer interface as defined
// by goyacc
type HocLex struct {
src string
sidx int
start int
width int
}
// initialize the builtin function symbol table
func init() {
builtins["sin"] = math.Sin
builtins["cos"] = math.Cos
builtins["tan"] = math.Tan
builtins["asin"] = math.Asin
builtins["acos"] = math.Acos
builtins["atan"] = math.Atan
builtins["sqrt"] = math.Sqrt
builtins["log"] = math.Log
builtins["log2"] = math.Log2
builtins["log10"] = math.Log10
consts["pi"] = math.Pi
consts["torad"] = math.Pi / 180.0
consts["todeg"] = 180.0 / math.Pi
}
// Lex is the entry point for the lexer. This func name signature and
// return type is expected by goyacc: implements HocLexer interface.
// yyparse calls Lex repeatedly for input tokens, returning a 0 signals
// "end of file" to yyparse.
func (lxr *HocLex) Lex(lval *HocSymType) int {
tok := lxr.Next(lval)
return int(tok)
}
// Error is a part of the HocLexer interface and prints syntax errors
func (lxr *HocLex) Error(s string) {
fmt.Printf("syntax error: %s\n", s)
}
// Next is the entry point to the lexical analyzer. The lexer reads the input
// stream and communicates tokens (with values, if desired) to the parser.
// Specifically, Next parses each incoming src string to rune and returns a
// corresponding Token.
func (lxr *HocLex) Next(lval *HocSymType) Token {
token := lxr.next()
value := lxr.src[lxr.start:lxr.sidx]
if token == DIGIT {
f, _ := strconv.ParseFloat(value, 64)
lval.val = f
} else if token == IDENT {
lval.index = value
// check if the identifier value is a builtin func or const
if _, ok := builtins[strings.ToLower(value)]; ok {
lval.fn = builtins[strings.ToLower(value)]
return BLTIN
} else if _, ok := consts[strings.ToLower(value)]; ok {
lval.val = consts[strings.ToLower(value)]
return DIGIT
}
return VAR
}
//fmt.Printf("token: %v, value: %v, lval.index: %s, lval.val: %0.2f\n",
// token, value, lval.index, lval.val)
return token
}
func (lxr *HocLex) next() Token {
lxr.start = lxr.sidx
c := lxr.read()
// consume any white space
lxr.whitespace(c)
lxr.start = lxr.sidx
c = lxr.read()
switch c {
//case eof:
// return Token(c)
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
return lxr.number()
case '.':
if unicode.IsDigit(lxr.peek()) {
return lxr.number()
}
default:
if unicode.IsLetter(c) || c == '_' {
return lxr.identifier()
}
}
return Token(c)
}
func (lxr *HocLex) read() rune {
if lxr.sidx >= len(lxr.src) {
lxr.width = 0
return eof
}
c, w := utf8.DecodeRuneInString(lxr.src[lxr.sidx:])
lxr.sidx += w
lxr.width = w
return c
}
func (lxr *HocLex) peek() rune {
c := lxr.read()
lxr.backup()
return c
}
func (lxr *HocLex) whitespace(c rune) {
// just consume white space and tab
for ; isSpace(c); c = lxr.read() {
/* handled by the grammar
if c == '\n' || c == '\r' {
return NEWLINE
}
*/
}
lxr.backup()
}
func (lxr *HocLex) identifier() Token {
// consume all identifier chars
lxr.matchWhile(isIdentChar)
/*
if !lxr.match('?') {
lxr.match('!')
}
*/
return IDENT
}
func (lxr *HocLex) number() Token {
// No hex support
digits := "0123456789"
lxr.matchRunOf(digits)
if lxr.match('.') {
lxr.matchRunOf(digits)
}
return DIGIT
}
func (lxr *HocLex) match(c rune) bool {
if c == lxr.read() {
return true
}
lxr.backup()
return false
}
func (lxr *HocLex) matchRunOf(valid string) {
for strings.ContainsRune(valid, lxr.read()) {
}
lxr.backup()
}
func (lxr *HocLex) matchWhile(f func(c rune) bool) {
for c := lxr.read(); f(c); c = lxr.read() {
}
lxr.backup()
}
func (lxr *HocLex) backup() {
lxr.sidx -= lxr.width
}
func isSpace(c rune) bool {
//return c == ' ' || c == '\t' || c == '\r' || c == '\n'
// newline handled by grammar
return c == ' ' || c == '\t'
}
func isIdentChar(r rune) bool {
return r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r)
}