|
1
|
package main |
|
2
|
|
|
3
|
import ( |
|
4
|
"errors" |
|
5
|
"fmt" |
|
6
|
"strconv" |
|
7
|
"strings" |
|
8
|
"unicode" |
|
9
|
) |
|
10
|
|
|
11
|
type TokenType int |
|
12
|
|
|
13
|
const ( |
|
14
|
TokenInvalid TokenType = iota |
|
15
|
TokenSym |
|
16
|
TokenNum |
|
17
|
TokenStr |
|
18
|
TokenOParen |
|
19
|
TokenCParen |
|
20
|
TokenComma |
|
21
|
) |
|
22
|
|
|
23
|
var TokenTypeName = map[TokenType]string{ |
|
24
|
TokenInvalid: "TokenInvalid", |
|
25
|
TokenSym: "TokenSym", |
|
26
|
TokenNum: "TokenNum", |
|
27
|
TokenStr: "TokenStr", |
|
28
|
TokenOParen: "TokenOParen", |
|
29
|
TokenCParen: "TokenCParen", |
|
30
|
TokenComma: "TokenComma", |
|
31
|
} |
|
32
|
|
|
33
|
type Token struct { |
|
34
|
Type TokenType |
|
35
|
Text []rune |
|
36
|
Loc Loc |
|
37
|
} |
|
38
|
|
|
39
|
type Loc struct { |
|
40
|
Filepath string |
|
41
|
Row, Col int |
|
42
|
} |
|
43
|
|
|
44
|
func (l Loc) String() string { return fmt.Sprintf("%s:%d:%d", l.Filepath, l.Row, l.Col) } |
|
45
|
|
|
46
|
type DiagError struct { |
|
47
|
Loc Loc |
|
48
|
Err error |
|
49
|
} |
|
50
|
|
|
51
|
func (e *DiagError) Unwrap() error { return e.Err } |
|
52
|
func (e *DiagError) Error() string { |
|
53
|
return fmt.Sprintf("%s: ERROR: %s", e.Loc, e.Err) |
|
54
|
} |
|
55
|
|
|
56
|
var ( |
|
57
|
ErrLexerEOF = errors.New("Lexer: End of file") |
|
58
|
ErrLexerUnclosedStr = errors.New("Lexer: Unclosed String") |
|
59
|
ErrLexerInvalidEscape = errors.New("Lexer: Invalid Escape Sequence") |
|
60
|
ErrLexerInvalidToken = errors.New("Lexer: Invalid Token") |
|
61
|
) |
|
62
|
|
|
63
|
type Lexer struct { |
|
64
|
Filepath string |
|
65
|
Content []rune // TODO: bytes |
|
66
|
Row int |
|
67
|
Cur int |
|
68
|
Bol int |
|
69
|
PeekTok Token |
|
70
|
PeekErr error |
|
71
|
PeekFull bool |
|
72
|
} |
|
73
|
|
|
74
|
func NewLexer(content []rune, filePath string) Lexer { |
|
75
|
return Lexer{ |
|
76
|
Filepath: filePath, |
|
77
|
Content: content, |
|
78
|
} |
|
79
|
} |
|
80
|
|
|
81
|
func (l *Lexer) ChopChar() { |
|
82
|
if l.Cur < len(l.Content) { |
|
83
|
x := l.Content[l.Cur] |
|
84
|
l.Cur += 1 |
|
85
|
if x == '\n' { |
|
86
|
l.Bol = l.Cur |
|
87
|
l.Row += 1 |
|
88
|
} |
|
89
|
} |
|
90
|
} |
|
91
|
|
|
92
|
func (l *Lexer) TrimLeft() { |
|
93
|
for l.Cur < len(l.Content) && unicode.IsSpace(l.Content[l.Cur]) { |
|
94
|
l.ChopChar() |
|
95
|
} |
|
96
|
} |
|
97
|
|
|
98
|
func (l Lexer) StartsWith(prefix []rune) bool { |
|
99
|
if l.Cur+len(prefix) > len(l.Content) { |
|
100
|
return false |
|
101
|
} |
|
102
|
|
|
103
|
for i := range prefix { |
|
104
|
if prefix[i] != l.Content[l.Cur+i] { |
|
105
|
return false |
|
106
|
} |
|
107
|
} |
|
108
|
|
|
109
|
return true |
|
110
|
} |
|
111
|
|
|
112
|
func (l *Lexer) Loc() Loc { |
|
113
|
return Loc{ |
|
114
|
Filepath: l.Filepath, |
|
115
|
Row: l.Row + 1, |
|
116
|
Col: l.Cur - l.Bol + 1, |
|
117
|
} |
|
118
|
} |
|
119
|
|
|
120
|
func (l *Lexer) ChopWhile(p func(rune) bool) (result []rune) { |
|
121
|
for l.Cur < len(l.Content) && p(l.Content[l.Cur]) { |
|
122
|
result = append(result, l.Content[l.Cur]) |
|
123
|
l.ChopChar() |
|
124
|
} |
|
125
|
return |
|
126
|
} |
|
127
|
|
|
128
|
func (l *Lexer) ChopToken() (Token, error) { |
|
129
|
t := Token{} |
|
130
|
|
|
131
|
l.TrimLeft() |
|
132
|
for l.Cur < len(l.Content) && l.StartsWith([]rune("--")) { |
|
133
|
for l.Cur < len(l.Content) && l.Content[l.Cur] != '\n' { |
|
134
|
l.ChopChar() |
|
135
|
} |
|
136
|
l.TrimLeft() |
|
137
|
} |
|
138
|
|
|
139
|
t.Loc = l.Loc() |
|
140
|
|
|
141
|
if l.Cur >= len(l.Content) { |
|
142
|
return t, ErrLexerEOF |
|
143
|
} |
|
144
|
|
|
145
|
first := l.Content[l.Cur] |
|
146
|
|
|
147
|
ps := []rune("(),") |
|
148
|
ts := []TokenType{TokenOParen, TokenCParen, TokenComma} |
|
149
|
for i := range ps { |
|
150
|
if first == ps[i] { |
|
151
|
t.Type = ts[i] |
|
152
|
t.Text = []rune{ps[i]} |
|
153
|
l.ChopChar() |
|
154
|
return t, nil |
|
155
|
} |
|
156
|
} |
|
157
|
|
|
158
|
if unicode.IsDigit(first) { |
|
159
|
t.Type = TokenNum |
|
160
|
t.Text = l.ChopWhile(unicode.IsDigit) |
|
161
|
return t, nil |
|
162
|
} |
|
163
|
|
|
164
|
if unicode.IsLetter(first) || first == '_' { |
|
165
|
t.Type = TokenSym |
|
166
|
t.Text = l.ChopWhile(func(x rune) bool { |
|
167
|
return unicode.IsLetter(x) || unicode.IsDigit(x) || x == '_' |
|
168
|
}) |
|
169
|
return t, nil |
|
170
|
} |
|
171
|
|
|
172
|
if first == '"' { |
|
173
|
l.ChopChar() |
|
174
|
|
|
175
|
t.Type = TokenStr |
|
176
|
|
|
177
|
for l.Cur < len(l.Content) && l.Content[l.Cur] != '"' { |
|
178
|
if l.Content[l.Cur] == '\\' { |
|
179
|
l.ChopChar() |
|
180
|
if l.Cur >= len(l.Content) { |
|
181
|
return t, &DiagError{ |
|
182
|
Loc: l.Loc(), |
|
183
|
Err: ErrLexerUnclosedStr, |
|
184
|
} |
|
185
|
} |
|
186
|
if l.Content[l.Cur] == '"' { |
|
187
|
t.Text = append(t.Text, '"') |
|
188
|
l.ChopChar() |
|
189
|
} else { |
|
190
|
loc := l.Loc() |
|
191
|
l.ChopChar() |
|
192
|
return t, &DiagError{ |
|
193
|
Loc: loc, |
|
194
|
Err: fmt.Errorf("%w: %c", ErrLexerInvalidEscape, l.Content[l.Cur]), |
|
195
|
} |
|
196
|
} |
|
197
|
} else { |
|
198
|
t.Text = append(t.Text, l.Content[l.Cur]) |
|
199
|
l.ChopChar() |
|
200
|
} |
|
201
|
} |
|
202
|
|
|
203
|
if l.Cur >= len(l.Content) { |
|
204
|
return t, &DiagError{ |
|
205
|
Loc: l.Loc(), |
|
206
|
Err: ErrLexerUnclosedStr, |
|
207
|
} |
|
208
|
} |
|
209
|
|
|
210
|
l.ChopChar() |
|
211
|
return t, nil |
|
212
|
} |
|
213
|
|
|
214
|
l.ChopChar() |
|
215
|
return t, &DiagError{ |
|
216
|
Loc: l.Loc(), |
|
217
|
Err: fmt.Errorf("%w: %c", ErrLexerInvalidToken, first), |
|
218
|
} |
|
219
|
} |
|
220
|
|
|
221
|
func (l *Lexer) Peek() (Token, error) { |
|
222
|
if !l.PeekFull { |
|
223
|
l.PeekTok, l.PeekErr = l.ChopToken() |
|
224
|
l.PeekFull = true |
|
225
|
} |
|
226
|
return l.PeekTok, l.PeekErr |
|
227
|
} |
|
228
|
|
|
229
|
func (l *Lexer) Next() (Token, error) { |
|
230
|
if l.PeekFull { |
|
231
|
l.PeekFull = false |
|
232
|
} else { |
|
233
|
l.PeekTok, l.PeekErr = l.ChopToken() |
|
234
|
} |
|
235
|
return l.PeekTok, l.PeekErr |
|
236
|
} |
|
237
|
|
|
238
|
type ExprType int |
|
239
|
|
|
240
|
const ( |
|
241
|
ExprVoid ExprType = iota |
|
242
|
ExprInt |
|
243
|
ExprStr |
|
244
|
ExprVar |
|
245
|
ExprFuncall |
|
246
|
) |
|
247
|
|
|
248
|
type Expr struct { |
|
249
|
Type ExprType |
|
250
|
Loc Loc |
|
251
|
|
|
252
|
AsInt int |
|
253
|
AsStr string |
|
254
|
AsVar string |
|
255
|
AsFuncall Funcall |
|
256
|
} |
|
257
|
|
|
258
|
type Funcall struct { |
|
259
|
Name string |
|
260
|
Args []Expr |
|
261
|
} |
|
262
|
|
|
263
|
func (funcall *Funcall) String() string { |
|
264
|
var result strings.Builder |
|
265
|
fmt.Fprintf(&result, "%s(", funcall.Name) |
|
266
|
for i, arg := range funcall.Args { |
|
267
|
if i > 0 { |
|
268
|
fmt.Fprintf(&result, ", ") |
|
269
|
} |
|
270
|
fmt.Fprintf(&result, "%s", arg.String()) |
|
271
|
} |
|
272
|
fmt.Fprintf(&result, ")") |
|
273
|
return result.String() |
|
274
|
} |
|
275
|
|
|
276
|
func (expr *Expr) Dump(level int) { |
|
277
|
for i := 0; i < level; i += 1 { |
|
278
|
fmt.Printf(" ") |
|
279
|
} |
|
280
|
|
|
281
|
switch expr.Type { |
|
282
|
case ExprVoid: |
|
283
|
fmt.Printf("Void\n") |
|
284
|
case ExprInt: |
|
285
|
fmt.Printf("Int: %d\n", expr.AsInt) |
|
286
|
case ExprStr: |
|
287
|
fmt.Printf("Str: \"%s\"\n", expr.AsStr) // TODO: escape strings |
|
288
|
case ExprVar: |
|
289
|
fmt.Printf("Var: %s\n", expr.AsVar) |
|
290
|
case ExprFuncall: |
|
291
|
fmt.Printf("Funcall: %s\n", expr.AsFuncall.Name) |
|
292
|
for _, arg := range expr.AsFuncall.Args { |
|
293
|
arg.Dump(level + 1) |
|
294
|
} |
|
295
|
} |
|
296
|
} |
|
297
|
|
|
298
|
func (expr *Expr) String() string { |
|
299
|
switch expr.Type { |
|
300
|
case ExprVoid: |
|
301
|
return "" |
|
302
|
case ExprInt: |
|
303
|
return fmt.Sprintf("%d", expr.AsInt) |
|
304
|
case ExprStr: |
|
305
|
return fmt.Sprintf("\"%s\"", expr.AsStr) // TODO: escape string |
|
306
|
case ExprVar: |
|
307
|
return expr.AsVar |
|
308
|
case ExprFuncall: |
|
309
|
return expr.AsFuncall.String() |
|
310
|
default: |
|
311
|
panic("unreachable") |
|
312
|
} |
|
313
|
} |
|
314
|
|
|
315
|
var ( |
|
316
|
ErrParserUnexpectedToken = errors.New("Parser: Unexpected Token") |
|
317
|
ErrParserUnclosedFuncall = errors.New("Parser: Unclosed Funcall") |
|
318
|
) |
|
319
|
|
|
320
|
func ParseExpr(l *Lexer) (Expr, error) { |
|
321
|
t, err := l.Next() |
|
322
|
if err != nil { |
|
323
|
return Expr{}, err |
|
324
|
} |
|
325
|
|
|
326
|
switch t.Type { |
|
327
|
case TokenSym: |
|
328
|
oparen, err := l.Peek() |
|
329
|
if err != nil || oparen.Type != TokenOParen { |
|
330
|
return Expr{ |
|
331
|
Type: ExprVar, |
|
332
|
Loc: t.Loc, |
|
333
|
AsVar: string(t.Text), |
|
334
|
}, nil |
|
335
|
} else { |
|
336
|
l.Next() |
|
337
|
cparen, err := l.Peek() |
|
338
|
|
|
339
|
if err == nil && cparen.Type == TokenCParen { |
|
340
|
l.Next() |
|
341
|
return Expr{ |
|
342
|
Type: ExprFuncall, |
|
343
|
Loc: t.Loc, |
|
344
|
AsFuncall: Funcall{Name: string(t.Text)}, |
|
345
|
}, nil |
|
346
|
} |
|
347
|
|
|
348
|
arg, err := ParseExpr(l) |
|
349
|
if err != nil { |
|
350
|
return Expr{}, err |
|
351
|
} |
|
352
|
|
|
353
|
args := []Expr{arg} |
|
354
|
|
|
355
|
comma, err := l.Next() |
|
356
|
for err == nil && comma.Type == TokenComma { |
|
357
|
arg, err = ParseExpr(l) |
|
358
|
if err != nil { |
|
359
|
return Expr{}, err |
|
360
|
} |
|
361
|
args = append(args, arg) |
|
362
|
comma, err = l.Next() |
|
363
|
} |
|
364
|
|
|
365
|
if err == ErrLexerEOF || comma.Type != TokenCParen { |
|
366
|
return Expr{}, &DiagError{Loc: comma.Loc, Err: ErrParserUnclosedFuncall} |
|
367
|
} |
|
368
|
|
|
369
|
return Expr{ |
|
370
|
Type: ExprFuncall, |
|
371
|
Loc: t.Loc, |
|
372
|
AsFuncall: Funcall{ |
|
373
|
Name: string(t.Text), |
|
374
|
Args: args, |
|
375
|
}, |
|
376
|
}, nil |
|
377
|
} |
|
378
|
|
|
379
|
case TokenNum: |
|
380
|
s := string(t.Text) |
|
381
|
x, err := strconv.Atoi(s) |
|
382
|
if err != nil { |
|
383
|
return Expr{}, &DiagError{Loc: t.Loc, Err: err} |
|
384
|
} |
|
385
|
return Expr{ |
|
386
|
Type: ExprInt, |
|
387
|
Loc: t.Loc, |
|
388
|
AsInt: x, |
|
389
|
}, nil |
|
390
|
|
|
391
|
case TokenStr: |
|
392
|
return Expr{ |
|
393
|
Type: ExprStr, |
|
394
|
Loc: t.Loc, |
|
395
|
AsStr: string(t.Text), |
|
396
|
}, nil |
|
397
|
} |
|
398
|
|
|
399
|
return Expr{}, &DiagError{ |
|
400
|
Loc: t.Loc, |
|
401
|
Err: fmt.Errorf("%w: %s", ErrParserUnexpectedToken, TokenTypeName[t.Type]), |
|
402
|
} |
|
403
|
} |
|
404
|
|
|
405
|
func ParseExprs(l *Lexer) ([]Expr, error) { |
|
406
|
exprs := []Expr{} |
|
407
|
for { |
|
408
|
expr, err := ParseExpr(l) |
|
409
|
if err != nil { |
|
410
|
if errors.Is(err, ErrLexerEOF) { |
|
411
|
err = nil |
|
412
|
} |
|
413
|
return exprs, err |
|
414
|
} |
|
415
|
exprs = append(exprs, expr) |
|
416
|
} |
|
417
|
} |
|
418
|
|
|
419
|
type ( |
|
420
|
Func = func(context *EvalContext, args []Expr) (Expr, error) |
|
421
|
|
|
422
|
EvalContext struct{ Scopes []EvalScope } |
|
423
|
EvalScope struct { |
|
424
|
Vars map[string]Expr |
|
425
|
Funcs map[string]Func |
|
426
|
} |
|
427
|
) |
|
428
|
|
|
429
|
func (e EvalContext) LookupVar(name string) (Expr, bool) { |
|
430
|
scopes := e.Scopes |
|
431
|
for len(scopes) > 0 { |
|
432
|
n := len(scopes) |
|
433
|
varr, ok := scopes[n-1].Vars[name] |
|
434
|
if ok { |
|
435
|
return varr, true |
|
436
|
} |
|
437
|
scopes = scopes[:n-1] |
|
438
|
} |
|
439
|
return Expr{}, false |
|
440
|
} |
|
441
|
|
|
442
|
func (e EvalContext) LookupFunc(name string) (Func, bool) { |
|
443
|
scopes := e.Scopes |
|
444
|
for len(scopes) > 0 { |
|
445
|
n := len(scopes) |
|
446
|
fun, ok := scopes[n-1].Funcs[name] |
|
447
|
if ok { |
|
448
|
return fun, true |
|
449
|
} |
|
450
|
scopes = scopes[:n-1] |
|
451
|
} |
|
452
|
return nil, false |
|
453
|
} |
|
454
|
|
|
455
|
func (e *EvalContext) PushScope(scope EvalScope) { e.Scopes = append(e.Scopes, scope) } |
|
456
|
func (e *EvalContext) PopScope() { e.Scopes = e.Scopes[0 : len(e.Scopes)-1] } |
|
457
|
|
|
458
|
func (e *EvalContext) TopScope() *EvalScope { |
|
459
|
length := len(e.Scopes) |
|
460
|
if length <= 0 { |
|
461
|
panic("No scopes found") |
|
462
|
} |
|
463
|
return &e.Scopes[length-1] |
|
464
|
} |
|
465
|
|
|
466
|
var ( |
|
467
|
ErrRuntimeUnknownVar = errors.New("unknown runtime variable") |
|
468
|
ErrRuntimeUnknownFun = errors.New("unknown runtime function") |
|
469
|
) |
|
470
|
|
|
471
|
func (e *EvalContext) EvalExpr(expr Expr) (Expr, error) { |
|
472
|
switch expr.Type { |
|
473
|
case ExprVoid, ExprInt, ExprStr: |
|
474
|
return expr, nil |
|
475
|
|
|
476
|
case ExprVar: |
|
477
|
v, ok := e.LookupVar(expr.AsVar) |
|
478
|
if !ok { |
|
479
|
return Expr{}, fmt.Errorf("%s: ERROR: %w: %s", expr.Loc, ErrRuntimeUnknownVar, expr.AsVar) |
|
480
|
} |
|
481
|
return e.EvalExpr(v) |
|
482
|
|
|
483
|
case ExprFuncall: |
|
484
|
fn, ok := e.LookupFunc(expr.AsFuncall.Name) |
|
485
|
if !ok { |
|
486
|
return Expr{}, fmt.Errorf("%s: ERROR: %w: %s", expr.Loc, ErrRuntimeUnknownFun, expr.AsFuncall.Name) |
|
487
|
} |
|
488
|
return fn(e, expr.AsFuncall.Args) |
|
489
|
|
|
490
|
default: |
|
491
|
panic("unreachable") |
|
492
|
} |
|
493
|
} |