Evaluate if and return, error validation.

Signed-off-by: jmug <u.g.a.mariano@gmail.com>
This commit is contained in:
Mariano Uvalle 2025-01-07 18:15:12 -08:00
parent cf8b0033c9
commit 8514ead895
3 changed files with 244 additions and 9 deletions

View file

@ -1,6 +1,8 @@
package evaluator
import (
"fmt"
"code.jmug.me/jmug/interpreter-in-go/pkg/ast"
"code.jmug.me/jmug/interpreter-in-go/pkg/object"
)
@ -15,7 +17,7 @@ func Eval(node ast.Node) object.Object {
switch node := node.(type) {
// Statements.
case *ast.Program:
return evalStatements(node.Statements)
return evalProgram(node.Statements)
case *ast.ExpressionStatement:
return Eval(node.Expression)
// Expressions.
@ -30,14 +32,37 @@ func Eval(node ast.Node) object.Object {
left := Eval(node.Left)
right := Eval(node.Right)
return evalInfixExpression(node.Operator, left, right)
case *ast.BlockStatement:
return evalBlockStatement(node.Statements)
case *ast.IfExpression:
return evalIfExpression(node)
case *ast.ReturnStatement:
return &object.ReturnValue{Value: Eval(node.ReturnValue)}
}
return nil
}
func evalStatements(stmts []ast.Statement) object.Object {
func evalProgram(stmts []ast.Statement) object.Object {
var res object.Object
for _, stmt := range stmts {
res = Eval(stmt)
switch res := res.(type) {
case *object.ReturnValue:
return res.Value
case *object.Error:
return res
}
}
return res
}
func evalBlockStatement(stmts []ast.Statement) object.Object {
var res object.Object
for _, stmt := range stmts {
res = Eval(stmt)
if res != nil && res.Type() == object.RETURN_VALUE_OBJ {
return res
}
}
return res
}
@ -48,8 +73,9 @@ func evalPrefixExpression(op string, right object.Object) object.Object {
return evalBangOperatorExpression(right)
case "-":
return evalMinusPrefixOperatorExpression(right)
default:
return newError("unknown operator: %s%s", op, right.Type())
}
return _NULL
}
func evalBangOperatorExpression(obj object.Object) object.Object {
@ -67,17 +93,27 @@ func evalBangOperatorExpression(obj object.Object) object.Object {
func evalMinusPrefixOperatorExpression(obj object.Object) object.Object {
if obj.Type() != object.INTEGER_OBJ {
return _NULL
return newError("unknown operator: -%s", obj.Type())
}
val := obj.(*object.Integer).Value
return &object.Integer{Value: -val}
}
func evalInfixExpression(op string, left, right object.Object) object.Object {
if left.Type() == object.INTEGER_OBJ && right.Type() == object.INTEGER_OBJ {
switch {
case left.Type() == object.INTEGER_OBJ && right.Type() == object.INTEGER_OBJ:
return evalIntegerInfixExpression(op, left, right)
case op == "==":
return nativeBoolToBooleanObject(left == right)
case op == "!=":
return nativeBoolToBooleanObject(left != right)
case left.Type() != right.Type():
return newError("type mismatch: %s %s %s",
left.Type(), op, right.Type())
default:
return newError("unknown operator: %s %s %s",
left.Type(), op, right.Type())
}
return _NULL
}
func evalIntegerInfixExpression(op string, left, right object.Object) object.Object {
@ -92,13 +128,49 @@ func evalIntegerInfixExpression(op string, left, right object.Object) object.Obj
return &object.Integer{Value: l * r}
case "/":
return &object.Integer{Value: l / r}
case "<":
return nativeBoolToBooleanObject(l < r)
case ">":
return nativeBoolToBooleanObject(l > r)
case "==":
return nativeBoolToBooleanObject(l == r)
case "!=":
return nativeBoolToBooleanObject(l != r)
default:
return newError("unknown operator: %s %s %s",
left.Type(), op, right.Type())
}
}
func evalIfExpression(ifExp *ast.IfExpression) object.Object {
cond := Eval(ifExp.Condition)
if isTruthy(cond) {
return Eval(ifExp.Consequence)
} else if ifExp.Alternative != nil {
return Eval(ifExp.Alternative)
}
return _NULL
}
func isTruthy(obj object.Object) bool {
switch obj {
case _TRUE:
return true
case _FALSE:
return false
case _NULL:
return false
}
return true
}
func nativeBoolToBooleanObject(b bool) object.Object {
if b {
return _TRUE
}
return _FALSE
}
func newError(format string, a ...any) *object.Error {
return &object.Error{Message: fmt.Sprintf(format, a...)}
}