diff options
Diffstat (limited to 'evaluator')
| -rw-r--r-- | evaluator/evaluator.go | 55 | ||||
| -rw-r--r-- | evaluator/evaluator_test.go | 59 |
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) |
