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, env *object.Environment) object.Object { switch node := node.(type) { // Statements. case *ast.Program: return evalProgram(node.Statements, env) case *ast.ExpressionStatement: return Eval(node.Expression, env) // Expressions. case *ast.IntegerLiteral: return &object.Integer{Value: node.Value} case *ast.Boolean: return nativeBoolToBooleanObject(node.Value) case *ast.PrefixExpression: right := Eval(node.Right, env) if isError(right) { return right } return evalPrefixExpression(node.Operator, right) case *ast.InfixExpression: left := Eval(node.Left, env) if isError(left) { return left } right := Eval(node.Right, env) if isError(right) { return right } return evalInfixExpression(node.Operator, left, right) case *ast.BlockStatement: return evalBlockStatement(node.Statements, env) case *ast.IfExpression: return evalIfExpression(node, env) case *ast.ReturnStatement: ret := Eval(node.ReturnValue, env) if isError(ret) { return ret } return &object.ReturnValue{Value: ret} 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) } return nil } func evalProgram(stmts []ast.Statement, env *object.Environment) object.Object { var res object.Object for _, stmt := range stmts { res = Eval(stmt, env) switch res := res.(type) { case *object.ReturnValue: return res.Value case *object.Error: return res } } return res } func evalBlockStatement(stmts []ast.Statement, env *object.Environment) object.Object { var res object.Object for _, stmt := range stmts { res = Eval(stmt, env) if res != nil && (res.Type() == object.RETURN_VALUE_OBJ || res.Type() == object.ERROR_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, env *object.Environment) object.Object { cond := Eval(ifExp.Condition, env) if isError(cond) { return cond } if isTruthy(cond) { return Eval(ifExp.Consequence, env) } else if ifExp.Alternative != nil { return Eval(ifExp.Alternative, env) } return _NULL } func evalIdentifier(exp *ast.Identifier, env *object.Environment) object.Object { val, ok := env.Get(exp.Value) if !ok { return newError("identifier not found: " + exp.Value) } return val } 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...)} } func isError(obj object.Object) bool { return obj != nil && obj.Type() == object.ERROR_OBJ }