133 lines
2.7 KiB
Go
133 lines
2.7 KiB
Go
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)
|
|
}
|