interpreter-in-go/pkg/evaluator/evaluator.go
jmug 8514ead895 Evaluate if and return, error validation.
Signed-off-by: jmug <u.g.a.mariano@gmail.com>
2025-01-07 18:15:12 -08:00

176 lines
4.1 KiB
Go

package evaluator
import (
"fmt"
"code.jmug.me/jmug/interpreter-in-go/pkg/ast"
"code.jmug.me/jmug/interpreter-in-go/pkg/object"
)
var (
_NULL = &object.Null{}
_TRUE = &object.Boolean{Value: true}
_FALSE = &object.Boolean{Value: false}
)
func Eval(node ast.Node) object.Object {
switch node := node.(type) {
// Statements.
case *ast.Program:
return evalProgram(node.Statements)
case *ast.ExpressionStatement:
return Eval(node.Expression)
// Expressions.
case *ast.IntegerLiteral:
return &object.Integer{Value: node.Value}
case *ast.Boolean:
return nativeBoolToBooleanObject(node.Value)
case *ast.PrefixExpression:
right := Eval(node.Right)
return evalPrefixExpression(node.Operator, right)
case *ast.InfixExpression:
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 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
}
func evalPrefixExpression(op string, right object.Object) object.Object {
switch op {
case "!":
return evalBangOperatorExpression(right)
case "-":
return evalMinusPrefixOperatorExpression(right)
default:
return newError("unknown operator: %s%s", op, right.Type())
}
}
func evalBangOperatorExpression(obj object.Object) object.Object {
switch obj {
case _TRUE:
return _FALSE
case _FALSE:
return _TRUE
case _NULL:
return _TRUE
default:
return _FALSE
}
}
func evalMinusPrefixOperatorExpression(obj object.Object) object.Object {
if obj.Type() != object.INTEGER_OBJ {
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 {
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())
}
}
func evalIntegerInfixExpression(op string, left, right object.Object) object.Object {
l := left.(*object.Integer).Value
r := right.(*object.Integer).Value
switch op {
case "+":
return &object.Integer{Value: l + r}
case "-":
return &object.Integer{Value: l - r}
case "*":
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...)}
}