package runtime import ( "git.red-panda.pet/pandaware/lox-go/ast" "git.red-panda.pet/pandaware/lox-go/lexer" ) type Interpreter struct{} var _ ast.ExprVisitor = new(Interpreter) func NewInterpreter() *Interpreter { return new(Interpreter) } func (i *Interpreter) Evaluate(exp ast.Expr) *LoxValue { result := exp.Accept(i) if value, ok := result.(*LoxValue); ok { return value } panic("badly implemented expression visitor") } // VisitBinaryExpr implements ast.ExprVisitor. func (i *Interpreter) VisitBinaryExpr(b *ast.BinaryExpr) any { left := i.Evaluate(b.Left) right := i.Evaluate(b.Right) leftType := left.Type() rightType := right.Type() areNumbers := leftType == LoxTypeNumber && rightType == LoxTypeNumber areStrings := leftType == LoxTypeString && rightType == LoxTypeString switch b.Op.Type { case lexer.TokenTypeMinus: if !areNumbers { // todo: error here return Value(nil) } return Value(left.Number() - right.Number()) case lexer.TokenTypeSlash: if !areNumbers { // todo: error here return Value(nil) } if right.Number() == 0 { // todo: error here return Value(nil) } return Value(left.Number() / right.Number()) case lexer.TokenTypeStar: if !areNumbers { return Value(nil) } return Value(left.Number() * right.Number()) case lexer.TokenTypePlus: if areNumbers { return Value(left.Number() + right.Number()) } if areStrings { return Value(left.String() + right.String()) } return Value(nil) case lexer.TokenTypeGreater: if !areNumbers { return Value(nil) } return Value(left.Number() > right.Number()) case lexer.TokenTypeGreaterEq: if !areNumbers { return Value(nil) } return Value(left.Number() >= right.Number()) case lexer.TokenTypeLess: if !areNumbers { return Value(nil) } return Value(left.Number() < right.Number()) case lexer.TokenTypeLessEq: if !areNumbers { return Value(nil) } return Value(left.Number() <= right.Number()) case lexer.TokenTypeEqualEqual: return Value(left.Equals(right)) case lexer.TokenTypeBangEq: return Value(!left.Equals(right)) } return Value(nil) } // VisitGroupingExpr implements ast.ExprVisitor. func (i *Interpreter) VisitGroupingExpr(g *ast.GroupingExpr) any { return i.Evaluate(g.Expr) } // VisitLiteralExpr implements ast.ExprVisitor. func (i *Interpreter) VisitLiteralExpr(g *ast.LiteralExpr) any { return Value(g.Value) } // VisitUnaryExpr implements ast.ExprVisitor. func (i *Interpreter) VisitUnaryExpr(g *ast.UnaryExpr) any { right := i.Evaluate(g.Right) switch g.Op.Type { case lexer.TokenTypeMinus: if right.Type() != LoxTypeNumber { // todo: error here } return Value(right) case lexer.TokenTypeBang: return Value(!right.IsTruthy()) } return Value(nil) }