basic expression interpreter
This commit is contained in:
parent
eebaadc16e
commit
b244f7e3b2
5 changed files with 186 additions and 16 deletions
10
ast/ast.go
10
ast/ast.go
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
6
run.go
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue