all repos

scratch @ 6fd17573ac7a1d0ffb3ce5749e3e6db32ab8f3ff

⭐ me doing recreational ~~drugs~~ programming

scratch/blo/blo.go (view raw)

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
}