From 268c04bb47dbce5f6c091b89b10d1757c38726f7 Mon Sep 17 00:00:00 2001 From: Marc Capell Date: Mon, 4 Nov 2024 23:31:22 +0100 Subject: [PATCH] evaluator: basic interface and implement the integer object - Implement the basic interface on object/object.go - Basic object types for integers, booleans, and null object. - Implement the integer object. - Use the new objects as outputs of the REPL. --- evaluator/evaluator.go | 34 +++++++++++++++++++++++++++ evaluator/evaluator_test.go | 47 +++++++++++++++++++++++++++++++++++++ object/object.go | 35 +++++++++++++++++++++++++++ repl/repl.go | 9 +++++-- 4 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 evaluator/evaluator.go create mode 100644 evaluator/evaluator_test.go create mode 100644 object/object.go diff --git a/evaluator/evaluator.go b/evaluator/evaluator.go new file mode 100644 index 0000000..1f1375b --- /dev/null +++ b/evaluator/evaluator.go @@ -0,0 +1,34 @@ +package evaluator + +import ( + "github.com/mcapell/go-monkey/ast" + "github.com/mcapell/go-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(statements []ast.Statement) object.Object { + var result object.Object + + for _, statement := range statements { + result = Eval(statement) + } + + return result +} diff --git a/evaluator/evaluator_test.go b/evaluator/evaluator_test.go new file mode 100644 index 0000000..3af8d7c --- /dev/null +++ b/evaluator/evaluator_test.go @@ -0,0 +1,47 @@ +package evaluator + +import ( + "testing" + + "github.com/mcapell/go-monkey/lexer" + "github.com/mcapell/go-monkey/object" + "github.com/mcapell/go-monkey/parser" +) + +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..50fb9a1 --- /dev/null +++ b/object/object.go @@ -0,0 +1,35 @@ +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 (i *Boolean) Type() ObjectType { return BOOLEAN_OBJ } +func (i *Boolean) Inspect() string { return fmt.Sprintf("%t", i.Value) } + +type Null struct{} + +func (i *Null) Type() ObjectType { return NULL_OBJ } +func (i *Null) Inspect() string { return "null" } diff --git a/repl/repl.go b/repl/repl.go index 200df22..227dad1 100644 --- a/repl/repl.go +++ b/repl/repl.go @@ -5,6 +5,7 @@ import ( "fmt" "io" + "github.com/mcapell/go-monkey/evaluator" "github.com/mcapell/go-monkey/lexer" "github.com/mcapell/go-monkey/parser" ) @@ -32,8 +33,12 @@ func Start(in io.Reader, out io.Writer) { continue } - io.WriteString(out, program.String()) // nolint: errcheck - io.WriteString(out, "\n") // nolint: errcheck + evaluated := evaluator.Eval(program) + if evaluated != nil { + io.WriteString(out, evaluated.Inspect()) // nolint: errcheck + io.WriteString(out, "\n") // nolint: errcheck + } + } }