glox/interpreter.go

356 lines
6.3 KiB
Go
Raw Normal View History

2024-10-04 15:24:01 +03:00
package main
import (
"fmt"
2024-10-05 18:58:49 +03:00
"log"
2024-10-04 15:24:01 +03:00
"reflect"
2024-10-06 16:30:57 +03:00
"slices"
2024-10-04 15:24:01 +03:00
)
2024-10-05 18:58:49 +03:00
type Interpreter struct {
2024-10-09 19:57:52 +03:00
env *Environment
globals *Environment
errors []error
brk bool
2024-10-05 18:58:49 +03:00
}
2024-10-04 15:24:01 +03:00
type RuntimeError struct {
token Token
msg string
}
2024-10-11 17:01:12 +03:00
type Return struct {
val any
}
2024-10-05 18:58:49 +03:00
func (re *RuntimeError) Error() string {
return fmt.Sprintf("RuntimeError [%d][%s] Error: %s", re.token.line, re.token.typ, re.msg)
2024-10-04 15:24:01 +03:00
}
func newInterpreter() *Interpreter {
2024-10-09 19:57:52 +03:00
globals := newEnvironment(nil)
defineGlobals(globals)
return &Interpreter{
env: globals,
globals: globals,
errors: []error{},
brk: false,
}
2024-10-04 15:24:01 +03:00
}
2024-10-05 18:58:49 +03:00
func (i *Interpreter) interpret(stmts []Stmt) []error {
2024-10-04 15:24:01 +03:00
defer i.recover()
2024-10-05 18:58:49 +03:00
i.errors = []error{}
for _, stmt := range stmts {
stmt.accept(i)
}
return i.errors
2024-10-04 15:24:01 +03:00
}
func (i *Interpreter) recover() {
if err := recover(); err != nil {
2024-10-05 18:58:49 +03:00
_, ok := err.(*RuntimeError)
2024-10-04 15:24:01 +03:00
if !ok {
panic(err)
}
}
}
2024-10-05 18:58:49 +03:00
func (i *Interpreter) evaluate(e Expr) any {
return e.accept(i)
}
2024-10-04 15:24:01 +03:00
func (i *Interpreter) visitBinary(b *Binary) any {
left := i.evaluate(b.left)
right := i.evaluate(b.right)
switch b.op.typ {
case MINUS:
2024-10-05 18:58:49 +03:00
i.checkIfFloats(b.op, left, right)
2024-10-04 15:24:01 +03:00
return left.(float64) - right.(float64)
case SLASH:
2024-10-05 18:58:49 +03:00
i.checkIfFloats(b.op, left, right)
2024-10-04 15:24:01 +03:00
return left.(float64) / right.(float64)
case STAR:
2024-10-05 18:58:49 +03:00
i.checkIfFloats(b.op, left, right)
2024-10-04 15:24:01 +03:00
return left.(float64) * right.(float64)
case GREATER:
2024-10-05 18:58:49 +03:00
i.checkIfFloats(b.op, left, right)
2024-10-04 15:24:01 +03:00
return left.(float64) > right.(float64)
case LESS:
2024-10-05 18:58:49 +03:00
i.checkIfFloats(b.op, left, right)
2024-10-04 15:24:01 +03:00
return left.(float64) < right.(float64)
case GREATER_EQUAL:
2024-10-05 18:58:49 +03:00
i.checkIfFloats(b.op, left, right)
2024-10-04 15:24:01 +03:00
return left.(float64) >= right.(float64)
case LESS_EQUAL:
2024-10-05 18:58:49 +03:00
i.checkIfFloats(b.op, left, right)
2024-10-04 15:24:01 +03:00
return left.(float64) <= right.(float64)
case BANG_EQUAL:
return !reflect.DeepEqual(left, right)
case EQUAL_EQUAL:
return reflect.DeepEqual(left, right)
case PLUS:
if isFloats(left, right) {
return left.(float64) + right.(float64)
}
if isStrings(left, right) {
return left.(string) + right.(string)
}
}
2024-10-05 18:58:49 +03:00
i.panic(&RuntimeError{b.op, fmt.Sprintf("Operands must be numbers or strings: %v %s %v", left, b.op.lexeme, right)})
2024-10-04 15:24:01 +03:00
return nil
}
func (i *Interpreter) visitLiteral(l *Literal) any {
return l.value
}
func (i *Interpreter) visitGrouping(g *Grouping) any {
return i.evaluate(g.expression)
}
func (i *Interpreter) visitUnary(u *Unary) any {
val := i.evaluate(u.right)
switch u.op.typ {
case MINUS:
2024-10-05 18:58:49 +03:00
i.checkIfFloat(u.op, val)
2024-10-04 15:24:01 +03:00
return -val.(float64)
case BANG:
return !isTruthy(val)
}
return nil
}
2024-10-05 18:58:49 +03:00
func (i *Interpreter) visitVariable(v *Variable) any {
2024-10-11 17:01:12 +03:00
return i.env.get(v.name.lexeme)
2024-10-05 18:58:49 +03:00
}
func (i *Interpreter) visitAssignment(a *Assign) any {
2024-10-05 23:27:00 +03:00
val := i.evaluate(a.value)
2024-10-11 17:01:12 +03:00
err := i.env.assign(a.variable, val)
if err != nil {
i.panic(err)
}
2024-10-05 23:27:00 +03:00
return val
2024-10-05 18:58:49 +03:00
}
2024-10-06 17:15:50 +03:00
func (i *Interpreter) visitLogical(lo *Logical) any {
left := i.evaluate(lo.left)
shortOr := lo.operator.typ == OR && isTruthy(left)
shortAnd := lo.operator.typ == AND && !isTruthy(left)
if shortOr || shortAnd {
return left
}
2024-10-06 16:30:57 +03:00
2024-10-06 17:15:50 +03:00
return i.evaluate(lo.right)
2024-10-06 16:30:57 +03:00
}
2024-10-09 19:57:52 +03:00
func (i *Interpreter) visitCall(c *Call) any {
callee := i.evaluate(c.callee)
args := []any{}
2024-10-11 17:01:12 +03:00
for _, arg := range c.args {
2024-10-09 19:57:52 +03:00
args = append(args, i.evaluate(arg))
}
2024-10-12 00:09:25 +03:00
callable, ok := callee.(Callable)
2024-10-09 19:57:52 +03:00
if !ok {
i.panic(&RuntimeError{c.paren, "Can only call function and classes."})
}
2024-10-12 00:09:25 +03:00
if callable.arity() != len(args) {
2024-10-09 19:57:52 +03:00
i.panic(&RuntimeError{
c.paren,
fmt.Sprintf(
"Expected %d arguments but got %d",
2024-10-12 00:09:25 +03:00
callable.arity(),
2024-10-09 19:57:52 +03:00
len(args),
),
})
}
return callable.call(i, args...)
}
2024-10-09 23:36:18 +03:00
func (i *Interpreter) visitFunStmt(f *FunStmt) {
2024-10-12 14:48:27 +03:00
i.env.define(f.name.lexeme, newFunction(f.name, f.args, f.body, i.env))
}
func (i *Interpreter) visitLambda(l *Lambda) any {
return newFunction(l.name, l.args, l.body, i.env)
2024-10-11 17:01:12 +03:00
}
func (i *Interpreter) visitReturnStmt(r *ReturnStmt) {
var value any
if r.value != nil {
value = i.evaluate(r.value)
}
panic(Return{value})
2024-10-09 23:36:18 +03:00
}
2024-10-05 18:58:49 +03:00
func (i *Interpreter) visitPrintStmt(p *PrintStmt) {
fmt.Printf("%v\n", i.evaluate(p.val))
}
func (i *Interpreter) visitExprStmt(se *ExprStmt) {
i.evaluate(se.expr)
}
func (i *Interpreter) visitVarStmt(v *VarStmt) {
var val any = nil
if v.initializer != nil {
val = i.evaluate(v.initializer)
}
2024-10-11 17:01:12 +03:00
i.env.define(v.name.lexeme, val)
2024-10-05 23:27:00 +03:00
}
func (i *Interpreter) visitBlockStmt(b *BlockStmt) {
2024-10-09 23:36:18 +03:00
i.executeBlock(b, newEnvironment(i.env))
}
func (i *Interpreter) executeBlock(b *BlockStmt, current *Environment) {
2024-10-05 23:27:00 +03:00
parentEnv := i.env
2024-10-09 23:36:18 +03:00
i.env = current
2024-10-05 23:27:00 +03:00
2024-10-11 17:01:12 +03:00
// need to restore environment after
// panic(Return) in visitReturnStmt
defer func() {
i.env = parentEnv
}()
2024-10-05 23:27:00 +03:00
for _, stmt := range b.stmts {
2024-10-07 21:47:55 +03:00
if i.brk {
break
}
2024-10-05 23:27:00 +03:00
stmt.accept(i)
}
2024-10-05 18:58:49 +03:00
}
2024-10-07 21:47:55 +03:00
func (i *Interpreter) visitBreakStmt(b *BreakStmt) {
i.brk = true
}
2024-10-06 16:30:57 +03:00
func (i *Interpreter) visitIfStmt(iff *IfStmt) {
if isTruthy(i.evaluate(iff.expr)) {
iff.then.accept(i)
} else if iff.or != nil {
iff.or.accept(i)
}
}
func (i *Interpreter) visitEnvStmt(e *EnvStmt) {
walker := i.env
flatten := []*Environment{}
for walker != nil {
flatten = slices.Insert(flatten, 0, walker)
2024-10-11 17:01:12 +03:00
walker = walker.enclosing
2024-10-06 16:30:57 +03:00
}
for ident, e := range flatten {
fmt.Printf("%*s", ident, "")
fmt.Printf("%+v\n", *e)
}
2024-10-07 21:06:23 +03:00
}
2024-10-06 16:30:57 +03:00
2024-10-07 21:06:23 +03:00
func (i *Interpreter) visitWhileStmt(w *WhileStmt) {
for isTruthy(i.evaluate(w.cond)) {
2024-10-07 21:47:55 +03:00
if i.brk {
i.brk = false
break
}
2024-10-07 21:06:23 +03:00
w.body.accept(i)
}
2024-10-06 16:30:57 +03:00
}
2024-10-05 18:58:49 +03:00
func (i *Interpreter) panic(re *RuntimeError) {
i.errors = append(i.errors, re)
log.Println(re)
panic(re)
}
func (i *Interpreter) checkIfFloat(op Token, val any) {
2024-10-04 15:24:01 +03:00
if _, ok := val.(float64); ok {
return
}
2024-10-05 18:58:49 +03:00
i.panic(&RuntimeError{op, "value must be a number."})
2024-10-04 15:24:01 +03:00
}
2024-10-05 18:58:49 +03:00
func (i *Interpreter) checkIfFloats(op Token, a any, b any) {
2024-10-04 15:24:01 +03:00
if isFloats(a, b) {
return
}
2024-10-05 18:58:49 +03:00
i.panic(&RuntimeError{op, fmt.Sprintf("Operands must be numbers: %v %s %v", a, op.lexeme, b)})
2024-10-04 15:24:01 +03:00
}
func isFloats(a any, b any) bool {
2024-10-06 16:30:57 +03:00
if a == nil || b == nil {
return false
}
2024-10-04 15:24:01 +03:00
ltype := reflect.TypeOf(a)
rtype := reflect.TypeOf(b)
return ltype.Kind() == rtype.Kind() && ltype.Kind() == reflect.Float64
}
func isStrings(a any, b any) bool {
2024-10-06 16:30:57 +03:00
if a == nil || b == nil {
return false
}
2024-10-04 15:24:01 +03:00
ltype := reflect.TypeOf(a)
rtype := reflect.TypeOf(b)
return ltype.Kind() == rtype.Kind() && ltype.Kind() == reflect.String
}
func isTruthy(val any) bool {
if val == nil {
return false
}
if boolean, ok := val.(bool); ok {
return boolean
}
return true
}