Skip to content

Commit b9d8599

Browse files
committed
3.5 Evaluate Expression (basic)
1 parent bd039e1 commit b9d8599

File tree

2 files changed

+93
-0
lines changed

2 files changed

+93
-0
lines changed

evaluator/evaluator.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package evaluator
2+
3+
// Package evaluator implements the evaluator -- a tree-walker implementation
4+
// that recursively walks the parsed AST (Abstract Syntax Tree) and evaluates
5+
// the nodes according to their semantic meaning.
6+
7+
import (
8+
"github.com/cedrickchee/hou/ast"
9+
"github.com/cedrickchee/hou/object"
10+
)
11+
12+
// Eval evaluates the node and returns an object.
13+
func Eval(node ast.Node) object.Object {
14+
// Traverse the AST by starting at the top of the tree, receiving an
15+
// *ast.Program, and then traverse every node in it.
16+
17+
switch node := node.(type) {
18+
19+
// Statements
20+
case *ast.Program:
21+
// Traverse the tree and evaluate every statement of the *ast.Program.
22+
return evalStatements(node.Statements)
23+
24+
case *ast.ExpressionStatement:
25+
// If the statement is an *ast.ExpressionStatement we evaluate its
26+
// expression. An expression statement (not a return statement and not
27+
// a let statement).
28+
return Eval(node.Expression)
29+
30+
// Expressions
31+
case *ast.IntegerLiteral:
32+
return &object.Integer{Value: node.Value}
33+
}
34+
35+
return nil
36+
}
37+
38+
func evalStatements(stmts []ast.Statement) object.Object {
39+
var result object.Object
40+
41+
for _, statement := range stmts {
42+
result = Eval(statement)
43+
}
44+
45+
return result
46+
}

evaluator/evaluator_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package evaluator
2+
3+
import (
4+
"testing"
5+
6+
"github.com/cedrickchee/hou/lexer"
7+
"github.com/cedrickchee/hou/object"
8+
"github.com/cedrickchee/hou/parser"
9+
)
10+
11+
func TestEvalIntegerExpression(t *testing.T) {
12+
tests := []struct {
13+
input string
14+
expected int64
15+
}{
16+
{"5", 5},
17+
{"10", 10},
18+
}
19+
20+
for _, tt := range tests {
21+
evaluated := testEval(tt.input)
22+
testIntegerObject(t, evaluated, tt.expected)
23+
}
24+
}
25+
26+
func testEval(input string) object.Object {
27+
l := lexer.New(input)
28+
p := parser.New(l)
29+
program := p.ParseProgram()
30+
31+
// The heart of the test is the call to Eval.
32+
return Eval(program)
33+
}
34+
35+
func testIntegerObject(t *testing.T, obj object.Object, expected int64) bool {
36+
result, ok := obj.(*object.Integer)
37+
if !ok {
38+
t.Errorf("object is not Integer. got=%T (%+v)", obj, obj)
39+
return false
40+
}
41+
if result.Value != expected {
42+
t.Errorf("object has wrong value. got=%d, want=%d", result.Value, expected)
43+
return false
44+
}
45+
46+
return true
47+
}

0 commit comments

Comments
 (0)