basic expression interpreter

This commit is contained in:
basil 2025-06-08 16:03:02 -04:00
parent eebaadc16e
commit b244f7e3b2
Signed by: basil
SSH key fingerprint: SHA256:y04xIFL/yqNaG9ae9Vl95vELtHfApGAIoOGLeVLP/fE
5 changed files with 186 additions and 16 deletions

View file

@ -16,7 +16,7 @@ type ExprVisitor interface {
} }
type Expr interface { type Expr interface {
accept(v ExprVisitor) any Accept(v ExprVisitor) any
} }
type BinaryExpr struct { type BinaryExpr struct {
@ -25,7 +25,7 @@ type BinaryExpr struct {
Right Expr Right Expr
} }
func (b *BinaryExpr) accept(v ExprVisitor) any { func (b *BinaryExpr) Accept(v ExprVisitor) any {
return v.VisitBinaryExpr(b) return v.VisitBinaryExpr(b)
} }
@ -33,7 +33,7 @@ type GroupingExpr struct {
Expr Expr Expr Expr
} }
func (g *GroupingExpr) accept(v ExprVisitor) any { func (g *GroupingExpr) Accept(v ExprVisitor) any {
return v.VisitGroupingExpr(g) return v.VisitGroupingExpr(g)
} }
@ -41,7 +41,7 @@ type LiteralExpr struct {
Value any Value any
} }
func (l *LiteralExpr) accept(v ExprVisitor) any { func (l *LiteralExpr) Accept(v ExprVisitor) any {
return v.VisitLiteralExpr(l) return v.VisitLiteralExpr(l)
} }
@ -50,6 +50,6 @@ type UnaryExpr struct {
Right Expr Right Expr
} }
func (u *UnaryExpr) accept(v ExprVisitor) any { func (u *UnaryExpr) Accept(v ExprVisitor) any {
return v.VisitUnaryExpr(u) return v.VisitUnaryExpr(u)
} }

View file

@ -45,7 +45,7 @@ func (p *Printer) Parenthesize(name string, expressions ...Expr) string {
val := "(" + name val := "(" + name
for _, e := range expressions { for _, e := range expressions {
exprStr, ok := (e.accept(p)).(string) exprStr, ok := (e.Accept(p)).(string)
if !ok { if !ok {
panic("badly implemented visitor") panic("badly implemented visitor")
} }
@ -58,7 +58,7 @@ func (p *Printer) Parenthesize(name string, expressions ...Expr) string {
} }
func (p *Printer) Print(e Expr) string { func (p *Printer) Print(e Expr) string {
str, ok := (e.accept(p)).(string) str, ok := (e.Accept(p)).(string)
if !ok { if !ok {
panic("badly implemented visitor") panic("badly implemented visitor")
} }

6
run.go
View file

@ -4,10 +4,10 @@ import (
"errors" "errors"
"os" "os"
"git.red-panda.pet/pandaware/lox-go/ast"
"git.red-panda.pet/pandaware/lox-go/lexer" "git.red-panda.pet/pandaware/lox-go/lexer"
"git.red-panda.pet/pandaware/lox-go/parser" "git.red-panda.pet/pandaware/lox-go/parser"
"git.red-panda.pet/pandaware/lox-go/reporter" "git.red-panda.pet/pandaware/lox-go/reporter"
"git.red-panda.pet/pandaware/lox-go/runtime"
) )
func RunFile(filename string) error { func RunFile(filename string) error {
@ -36,8 +36,8 @@ func Run(source string) error {
return errors.New("parser error") return errors.New("parser error")
} }
printer := &ast.Printer{} interpreter := runtime.NewInterpreter()
reporter.Debug(0, "astPrinter", "stdin", printer.Print(expr)) reporter.Debug(0, "interpreter", "stdin", interpreter.Evaluate(expr).Debug())
return nil return nil
} }

View file

@ -1,6 +1,9 @@
package runtime 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{} type Interpreter struct{}
@ -10,22 +13,121 @@ func NewInterpreter() *Interpreter {
return new(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. // VisitBinaryExpr implements ast.ExprVisitor.
func (i *Interpreter) VisitBinaryExpr(b *ast.BinaryExpr) any { 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. // VisitGroupingExpr implements ast.ExprVisitor.
func (i *Interpreter) VisitGroupingExpr(g *ast.GroupingExpr) any { func (i *Interpreter) VisitGroupingExpr(g *ast.GroupingExpr) any {
panic("unimplemented") return i.Evaluate(g.Expr)
} }
// VisitLiteralExpr implements ast.ExprVisitor. // VisitLiteralExpr implements ast.ExprVisitor.
func (i *Interpreter) VisitLiteralExpr(g *ast.LiteralExpr) any { func (i *Interpreter) VisitLiteralExpr(g *ast.LiteralExpr) any {
panic("unimplemented") return Value(g.Value)
} }
// VisitUnaryExpr implements ast.ExprVisitor. // VisitUnaryExpr implements ast.ExprVisitor.
func (i *Interpreter) VisitUnaryExpr(g *ast.UnaryExpr) any { 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)
} }

View file

@ -1,5 +1,7 @@
package runtime package runtime
import "fmt"
//go:generate stringer -type LoxType -trimprefix LoxType //go:generate stringer -type LoxType -trimprefix LoxType
type LoxType int type LoxType int
@ -19,15 +21,81 @@ func (v *LoxValue) Type() LoxType {
switch v.raw.(type) { switch v.raw.(type) {
case string: case string:
return LoxTypeString return LoxTypeString
case float64, float32: case float64:
return LoxTypeNumber return LoxTypeNumber
case bool: case bool:
return LoxTypeBoolean return LoxTypeBoolean
case nil:
return LoxTypeNil
default: default:
return LoxTypeUndefined 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 { func Value(v any) *LoxValue {
return &LoxValue{v} 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))
}