aboutsummaryrefslogtreecommitdiff
path: root/evaluator
diff options
context:
space:
mode:
authorBobby <[email protected]>2024-03-04 09:07:29 -0500
committerBobby <[email protected]>2024-03-04 09:07:29 -0500
commitb52f4e9b4140f482ad966aa354b39cd305a212ec (patch)
treebaf7e8a78dae0fabca6f24cc10b0fa484771598b /evaluator
parentc430bfcae07489ad52ae65cadee581be72dd35d1 (diff)
downloadmana-b52f4e9b4140f482ad966aa354b39cd305a212ec.tar.xz
mana-b52f4e9b4140f482ad966aa354b39cd305a212ec.zip
LetStatements
Diffstat (limited to 'evaluator')
-rw-r--r--evaluator/evaluator.go53
-rw-r--r--evaluator/evaluator_test.go24
2 files changed, 59 insertions, 18 deletions
diff --git a/evaluator/evaluator.go b/evaluator/evaluator.go
index b9b8bb9..9cead89 100644
--- a/evaluator/evaluator.go
+++ b/evaluator/evaluator.go
@@ -13,17 +13,27 @@ var (
)
// Eval evaluates the given ast.Node and returns an object.Object.
-func Eval(node ast.Node) object.Object {
+func Eval(node ast.Node, env *object.Environment) object.Object {
switch node := node.(type) {
// Statements
case *ast.Program:
- return evalProgram(node)
+ return evalProgram(node, env)
case *ast.ExpressionStatement:
- return Eval(node.Expression)
+ return Eval(node.Expression, env)
case *ast.BlockStatement:
- return evalBlockStatement(node)
+ return evalBlockStatement(node, env)
+
+ case *ast.LetStatement:
+ val := Eval(node.Value, env)
+ if isError(val) {
+ return val
+ }
+ env.Set(node.Name.Value, val)
+
+ case *ast.Identifier:
+ return evalIdentifier(node, env)
// Expressions
case *ast.IntegerLiteral:
@@ -33,28 +43,28 @@ func Eval(node ast.Node) object.Object {
return nativeBoolToBooleanObject(node.Value)
case *ast.PrefixExpression:
- right := Eval(node.Right)
+ right := Eval(node.Right, env)
if isError(right) {
return right
}
return evalPrefixExpression(node.Operator, right)
case *ast.InfixExpression:
- left := Eval(node.Left)
+ left := Eval(node.Left, env)
if isError(left) {
return left
}
- right := Eval(node.Right)
+ right := Eval(node.Right, env)
if isError(right) {
return right
}
return evalInfixExpression(node.Operator, left, right)
case *ast.IfExpression:
- return evalIfExpression(node)
+ return evalIfExpression(node, env)
case *ast.ReturnStatement:
- val := Eval(node.ReturnValue)
+ val := Eval(node.ReturnValue, env)
if isError(val) {
return val
}
@@ -64,11 +74,11 @@ func Eval(node ast.Node) object.Object {
return nil
}
-func evalProgram(program *ast.Program) object.Object {
+func evalProgram(program *ast.Program, env *object.Environment) object.Object {
var result object.Object
for _, statement := range program.Statements {
- result = Eval(statement)
+ result = Eval(statement, env)
switch result := result.(type) {
case *object.ReturnValue:
@@ -81,11 +91,12 @@ func evalProgram(program *ast.Program) object.Object {
return result
}
-func evalBlockStatement(block *ast.BlockStatement) object.Object {
+
+func evalBlockStatement(block *ast.BlockStatement, env *object.Environment) object.Object {
var result object.Object
for _, statement := range block.Statements {
- result = Eval(statement)
+ result = Eval(statement, env)
if result != nil {
rt := result.Type()
@@ -182,20 +193,28 @@ func evalIntegerInfixExpression(operator string, left, right object.Object) obje
}
}
-func evalIfExpression(ie *ast.IfExpression) object.Object {
- condition := Eval(ie.Condition)
+func evalIfExpression(ie *ast.IfExpression, env *object.Environment) object.Object {
+ condition := Eval(ie.Condition, env)
if isError(condition) {
return condition
}
if isTruthy(condition) {
- return Eval(ie.Consequence)
+ return Eval(ie.Consequence, env)
} else if ie.Alternative != nil {
- return Eval(ie.Alternative)
+ return Eval(ie.Alternative, env)
} else {
return NULL
}
}
+func evalIdentifier(node *ast.Identifier, env *object.Environment) object.Object {
+ val, ok := env.Get(node.Value)
+ if !ok {
+ return newError("identifier not found: " + node.Value)
+ }
+ return val
+}
+
func isTruthy(obj object.Object) bool {
switch obj {
case NULL:
diff --git a/evaluator/evaluator_test.go b/evaluator/evaluator_test.go
index 6553f55..38fed91 100644
--- a/evaluator/evaluator_test.go
+++ b/evaluator/evaluator_test.go
@@ -177,6 +177,10 @@ func TestErrorhandling(t *testing.T) {
`,
"unknown operator: BOOLEAN + BOOLEAN",
},
+ {
+ "foobar",
+ "identifier not found: foobar",
+ },
}
for _, tt := range tests {
@@ -194,11 +198,29 @@ func TestErrorhandling(t *testing.T) {
}
}
+func TestLetStatements(t *testing.T) {
+ tests := []struct {
+ input string
+ expected int64
+ } {
+ {"let a = 5; a;", 5},
+ {"let a = 5 * 5; a;", 25},
+ {"let a = 5; let b = a; b;", 5},
+ {"let a = 5; let b = a; let c = a + b + 5; c;", 15},
+ }
+
+ for _, tt := range tests {
+ testIntegerObject(t, testEval(tt.input), tt.expected)
+ }
+}
+
func testEval(input string) object.Object {
l := lexer.New(input)
p := parser.New(l)
program := p.ParseProgram()
- return Eval(program)
+ env := object.NewEnvironment()
+
+ return Eval(program, env)
}
func testIntegerObject(t *testing.T, obj object.Object, expected int64) bool {