aboutsummaryrefslogtreecommitdiff
path: root/evaluator
diff options
context:
space:
mode:
Diffstat (limited to 'evaluator')
-rw-r--r--evaluator/evaluator.go55
-rw-r--r--evaluator/evaluator_test.go59
2 files changed, 103 insertions, 11 deletions
diff --git a/evaluator/evaluator.go b/evaluator/evaluator.go
index 3a9e54a..b9b8bb9 100644
--- a/evaluator/evaluator.go
+++ b/evaluator/evaluator.go
@@ -3,6 +3,7 @@ package evaluator
import (
"mana/ast"
"mana/object"
+ "fmt"
)
var (
@@ -33,11 +34,20 @@ func Eval(node ast.Node) object.Object {
case *ast.PrefixExpression:
right := Eval(node.Right)
+ if isError(right) {
+ return right
+ }
return evalPrefixExpression(node.Operator, right)
case *ast.InfixExpression:
left := Eval(node.Left)
+ if isError(left) {
+ return left
+ }
right := Eval(node.Right)
+ if isError(right) {
+ return right
+ }
return evalInfixExpression(node.Operator, left, right)
case *ast.IfExpression:
@@ -45,6 +55,9 @@ func Eval(node ast.Node) object.Object {
case *ast.ReturnStatement:
val := Eval(node.ReturnValue)
+ if isError(val) {
+ return val
+ }
return &object.ReturnValue{Value: val}
}
@@ -57,9 +70,12 @@ func evalProgram(program *ast.Program) object.Object {
for _, statement := range program.Statements {
result = Eval(statement)
- if returnValue, ok := result.(*object.ReturnValue); ok {
- return returnValue.Value
- }
+ switch result := result.(type) {
+ case *object.ReturnValue:
+ return result.Value
+ case *object.Error:
+ return result
+ }
}
return result
@@ -71,9 +87,12 @@ func evalBlockStatement(block *ast.BlockStatement) object.Object {
for _, statement := range block.Statements {
result = Eval(statement)
- if result != nil && result.Type() == object.RETURN_VALUE_OBJ {
- return result
- }
+ if result != nil {
+ rt := result.Type()
+ if rt == object.RETURN_VALUE_OBJ || rt == object.ERROR_OBJ {
+ return result
+ }
+ }
}
return result
@@ -93,7 +112,7 @@ func evalPrefixExpression(operator string, right object.Object) object.Object {
case "-":
return evalMinusPrefixOperatorExpression(right)
default:
- return NULL
+ return newError("unknown operator: %s%s", operator, right.Type())
}
}
@@ -105,8 +124,10 @@ func evalInfixExpression(operator string, left, right object.Object) object.Obje
return nativeBoolToBooleanObject(left == right)
case operator == "!=":
return nativeBoolToBooleanObject(left != right)
+ case left.Type() != right.Type():
+ return newError("type mismatch: %s %s %s", left.Type(), operator, right.Type())
default:
- return NULL
+ return newError("unknown operator: %s %s %s", left.Type(), operator, right.Type())
}
}
@@ -125,7 +146,7 @@ func evalBangOperatorExpression(right object.Object) object.Object {
func evalMinusPrefixOperatorExpression(right object.Object) object.Object {
if right.Type() != object.INTEGER_OBJ {
- return NULL
+ return newError("unknown operator: -%s", right.Type())
}
value := right.(*object.Integer).Value
@@ -157,12 +178,15 @@ func evalIntegerInfixExpression(operator string, left, right object.Object) obje
case "!=":
return nativeBoolToBooleanObject(leftValue != rightValue)
default:
- return NULL
+ return newError("unknown operator: %s %s %s", left.Type(), operator, right.Type())
}
}
func evalIfExpression(ie *ast.IfExpression) object.Object {
condition := Eval(ie.Condition)
+ if isError(condition) {
+ return condition
+ }
if isTruthy(condition) {
return Eval(ie.Consequence)
} else if ie.Alternative != nil {
@@ -184,3 +208,14 @@ func isTruthy(obj object.Object) bool {
return true
}
}
+
+func newError(format string, a ...interface{}) *object.Error {
+ return &object.Error{Message: fmt.Sprintf(format, a...)}
+}
+
+func isError(obj object.Object) bool {
+ if obj != nil {
+ return obj.Type() == object.ERROR_OBJ
+ }
+ return false
+}
diff --git a/evaluator/evaluator_test.go b/evaluator/evaluator_test.go
index ca7738f..6553f55 100644
--- a/evaluator/evaluator_test.go
+++ b/evaluator/evaluator_test.go
@@ -5,7 +5,7 @@ import (
"mana/object"
"mana/parser"
- "testing"
+ "testing"
)
func TestEvalIntegerExpression(t *testing.T) {
@@ -137,6 +137,63 @@ func TestReturnStatements(t *testing.T) {
}
}
+func TestErrorhandling(t *testing.T) {
+ tests := [] struct {
+ input string
+ expectedMessage string
+ } {
+ {
+ "5 + true;",
+ "type mismatch: INTEGER + BOOLEAN",
+ },
+ {
+ "5 + true; 5;",
+ "type mismatch: INTEGER + BOOLEAN",
+ },
+ {
+ "-true",
+ "unknown operator: -BOOLEAN",
+ },
+ {
+ "true + false;",
+ "unknown operator: BOOLEAN + BOOLEAN",
+ },
+ {
+ "5; true + false; 5",
+ "unknown operator: BOOLEAN + BOOLEAN",
+ },
+ {
+ "if (10 > 1) { true + false; }",
+ "unknown operator: BOOLEAN + BOOLEAN",
+ },
+ {
+ `
+ if (10 > 1) {
+ if (10 > 1) {
+ return true + false;
+ }
+ return 1;
+ }
+ `,
+ "unknown operator: BOOLEAN + BOOLEAN",
+ },
+ }
+
+ for _, tt := range tests {
+ evaluated := testEval(tt.input)
+
+ errObj, ok := evaluated.(*object.Error)
+ if !ok {
+ t.Errorf("no error object returned. got=%T(%+v)", evaluated, evaluated)
+ continue
+ }
+
+ if errObj.Message != tt.expectedMessage {
+ t.Errorf("wrong error message. expected=%q, got=%q", tt.expectedMessage, errObj.Message)
+ }
+ }
+}
+
func testEval(input string) object.Object {
l := lexer.New(input)
p := parser.New(l)