diff --git a/evaluator/evaluator.go b/evaluator/evaluator.go new file mode 100644 index 0000000..2a8c44a --- /dev/null +++ b/evaluator/evaluator.go @@ -0,0 +1,34 @@ +package evaluator + +import ( + "monkey/ast" + "monkey/object" +) + +func Eval(node ast.Node) object.Object { + switch node := node.(type) { + + // Statements + case *ast.Program: + return evalStatements(node.Statements) + + case *ast.ExpressionStatement: + return Eval(node.Expression) + + // Expressions + case *ast.IntegerLiteral: + return &object.Integer{Value: node.Value} + } + + return nil +} + +func evalStatements(stmts []ast.Statement) object.Object { + var result object.Object + + for _, statement := range stmts { + result = Eval(statement) + } + + return result +} diff --git a/evaluator/evaluator_test.go b/evaluator/evaluator_test.go new file mode 100644 index 0000000..0c6779c --- /dev/null +++ b/evaluator/evaluator_test.go @@ -0,0 +1,45 @@ +package evaluator + +import ( + "monkey/lexer" + "monkey/object" + "monkey/parser" + "testing" +) + +func TestEvalIntegerExpression(t *testing.T) { + tests := []struct { + input string + expected int64 + }{ + {"5", 5}, + {"10", 10}, + } + + for _, tt := range tests { + evaluated := testEval(tt.input) + testIntegerObject(t, evaluated, tt.expected) + } +} + +func testEval(input string) object.Object { + l := lexer.New(input) + p := parser.New(l) + program := p.ParseProgram() + + return Eval(program) +} + +func testIntegerObject(t *testing.T, obj object.Object, expected int64) bool { + result, ok := obj.(*object.Integer) + if !ok { + t.Errorf("object is not Integer. got=%T (%+v)", obj, obj) + return false + } + if result.Value != expected { + t.Errorf("object has wrong value. got=%d, want=%d", result.Value, expected) + return false + } + + return true +} diff --git a/object/object.go b/object/object.go new file mode 100644 index 0000000..ea095bc --- /dev/null +++ b/object/object.go @@ -0,0 +1,48 @@ +package object + +import "fmt" + +type ObjectType string + +const ( + INTEGER_OBJ = "INTEGER" + BOOLEAN_OBJ = "BOOLEAN" + NULL_OBJ = "NULL" +) + +type Object interface { + Type() ObjectType + Inspect() string +} + +type Integer struct { + Value int64 +} + +func (i *Integer) Type() ObjectType { + return INTEGER_OBJ +} +func (i *Integer) Inspect() string { + return fmt.Sprintf("%d", i.Value) +} + +type Boolean struct { + Value bool +} + +func (b *Boolean) Type() ObjectType { + return BOOLEAN_OBJ +} + +func (b *Boolean) Inspect() string { + return fmt.Sprintf("%t", b.Value) +} + +type Null struct{} + +func (n *Null) Type() ObjectType { + return NULL_OBJ +} +func (n *Null) Inspect() string { + return "null" +} diff --git a/repl/repl.go b/repl/repl.go index c0dc6d4..94d10e7 100644 --- a/repl/repl.go +++ b/repl/repl.go @@ -4,6 +4,7 @@ import ( "bufio" "fmt" "io" + "monkey/evaluator" "monkey/lexer" "monkey/parser" ) @@ -30,8 +31,11 @@ func Start(in io.Reader, out io.Writer) { continue } - io.WriteString(out, program.String()) - io.WriteString(out, "\n") + evaluated := evaluator.Eval(program) + if evaluated != nil { + io.WriteString(out, evaluated.Inspect()) + io.WriteString(out, "\n") + } } }