diff --git a/ast/ast.go b/ast/ast.go index 69a620b..73d925f 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -16,7 +16,7 @@ type ExprVisitor interface { } type Expr interface { - accept(v ExprVisitor) any + Accept(v ExprVisitor) any } type BinaryExpr struct { @@ -25,7 +25,7 @@ type BinaryExpr struct { Right Expr } -func (b *BinaryExpr) accept(v ExprVisitor) any { +func (b *BinaryExpr) Accept(v ExprVisitor) any { return v.VisitBinaryExpr(b) } @@ -33,7 +33,7 @@ type GroupingExpr struct { Expr Expr } -func (g *GroupingExpr) accept(v ExprVisitor) any { +func (g *GroupingExpr) Accept(v ExprVisitor) any { return v.VisitGroupingExpr(g) } @@ -41,7 +41,7 @@ type LiteralExpr struct { Value any } -func (l *LiteralExpr) accept(v ExprVisitor) any { +func (l *LiteralExpr) Accept(v ExprVisitor) any { return v.VisitLiteralExpr(l) } @@ -50,6 +50,6 @@ type UnaryExpr struct { Right Expr } -func (u *UnaryExpr) accept(v ExprVisitor) any { +func (u *UnaryExpr) Accept(v ExprVisitor) any { return v.VisitUnaryExpr(u) } diff --git a/ast/astprinter.go b/ast/astprinter.go index 7cd5cf0..3ebd326 100644 --- a/ast/astprinter.go +++ b/ast/astprinter.go @@ -45,7 +45,7 @@ func (p *Printer) Parenthesize(name string, expressions ...Expr) string { val := "(" + name for _, e := range expressions { - exprStr, ok := (e.accept(p)).(string) + exprStr, ok := (e.Accept(p)).(string) if !ok { panic("badly implemented visitor") } @@ -58,7 +58,7 @@ func (p *Printer) Parenthesize(name string, expressions ...Expr) string { } func (p *Printer) Print(e Expr) string { - str, ok := (e.accept(p)).(string) + str, ok := (e.Accept(p)).(string) if !ok { panic("badly implemented visitor") } diff --git a/run.go b/run.go index 126f46d..dfbf639 100644 --- a/run.go +++ b/run.go @@ -4,10 +4,10 @@ import ( "errors" "os" - "git.red-panda.pet/pandaware/lox-go/ast" "git.red-panda.pet/pandaware/lox-go/lexer" "git.red-panda.pet/pandaware/lox-go/parser" "git.red-panda.pet/pandaware/lox-go/reporter" + "git.red-panda.pet/pandaware/lox-go/runtime" ) func RunFile(filename string) error { @@ -36,8 +36,8 @@ func Run(source string) error { return errors.New("parser error") } - printer := &ast.Printer{} - reporter.Debug(0, "astPrinter", "stdin", printer.Print(expr)) + interpreter := runtime.NewInterpreter() + reporter.Debug(0, "interpreter", "stdin", interpreter.Evaluate(expr).Debug()) return nil } diff --git a/runtime/interpreter.go b/runtime/interpreter.go index 5cbee36..44a11e0 100644 --- a/runtime/interpreter.go +++ b/runtime/interpreter.go @@ -1,6 +1,9 @@ package runtime -import "git.red-panda.pet/pandaware/lox-go/ast" +import ( + "git.red-panda.pet/pandaware/lox-go/ast" + "git.red-panda.pet/pandaware/lox-go/lexer" +) type Interpreter struct{} @@ -10,22 +13,121 @@ 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 { - panic("unimplemented") + 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.Operator.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 { - panic("unimplemented") + return i.Evaluate(g.Expr) } // VisitLiteralExpr implements ast.ExprVisitor. func (i *Interpreter) VisitLiteralExpr(g *ast.LiteralExpr) any { - panic("unimplemented") + return Value(g.Value) } // VisitUnaryExpr implements ast.ExprVisitor. func (i *Interpreter) VisitUnaryExpr(g *ast.UnaryExpr) any { - panic("unimplemented") + right := i.Evaluate(g.Right) + + switch g.Operator.Type { + case lexer.TokenTypeMinus: + if right.Type() != LoxTypeNumber { + // todo: error here + } + return Value(right) + case lexer.TokenTypeBang: + return Value(!right.IsTruthy()) + } + + return Value(nil) } diff --git a/runtime/types.go b/runtime/types.go index 23e501b..7862634 100644 --- a/runtime/types.go +++ b/runtime/types.go @@ -1,5 +1,7 @@ package runtime +import "fmt" + //go:generate stringer -type LoxType -trimprefix LoxType type LoxType int @@ -19,15 +21,81 @@ func (v *LoxValue) Type() LoxType { switch v.raw.(type) { case string: return LoxTypeString - case float64, float32: + case float64: return LoxTypeNumber case bool: return LoxTypeBoolean + case nil: + return LoxTypeNil default: return LoxTypeUndefined } } +func (v *LoxValue) Debug() string { + return fmt.Sprintf("%+v", v.raw) +} + +func (v *LoxValue) String() string { + if string, ok := v.raw.(string); ok { + return string + } + return "" +} + +func (v *LoxValue) Number() float64 { + if number, ok := v.raw.(float64); ok { + return number + } + return 0.0 +} + +func (v *LoxValue) IsString() bool { + return v.Type() == LoxTypeString +} + +func (v *LoxValue) IsNumber() bool { + return v.Type() == LoxTypeNumber +} +func (v *LoxValue) IsTruthy() bool { + switch v.Type() { + case LoxTypeBoolean: + b, ok := v.raw.(bool) + return ok && b + case LoxTypeNumber, LoxTypeString: + return true + default: + return false + } +} + +func (v *LoxValue) IsNil() bool { + return v.Type() == LoxTypeNil +} + +func (v *LoxValue) IsUndefined() bool { + return v.Type() == LoxTypeUndefined +} + +func (v *LoxValue) Equals(other *LoxValue) bool { + return AreEqual(v, other) +} + func Value(v any) *LoxValue { return &LoxValue{v} } + +func AreEqual(a, b *LoxValue) bool { + typeA := a.Type() + typeB := b.Type() + + if typeA != typeB { + return false + } + + return a.raw == b.raw +} + +func AreEqualValue(a, b *LoxValue) *LoxValue { + return Value(AreEqual(a, b)) +}