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.StringLiteral: return &object.String{Value: 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) case *ast.FunctionLiteral: params := node.Parameters body := node.Body return &object.Function{Parameters: params, Body: body, Env: env} case *ast.CallExpression: fn := Eval(node.Function, env) if isError(fn) { return fn } args := evalExpressions(node.Arguments, env) if len(args) == 1 && isError(args[0]) { return args[0] } return applyFunction(fn, args) case *ast.ArrayLiteral: els := evalExpressions(node.Elements, env) if len(els) == 1 && isError(els[0]) { return els[0] } return &object.Array{Elements: els} case *ast.IndexExpression: left := Eval(node.Left, env) if isError(left) { return left } index := Eval(node.Index, env) if isError(index) { return index } return evalIndexExpression(left, index) } 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 left.Type() == object.STRING_OBJ && right.Type() == object.STRING_OBJ: return evalStringInfixExpression(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 evalStringInfixExpression(op string, left, right object.Object) object.Object { if op != "+" { return newError( "unknown operator: %s %s %s", left.Type(), op, right.Type(), ) } l := left.(*object.String).Value r := right.(*object.String).Value return &object.String{Value: l + r} } 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 { if val, ok := env.Get(exp.Value); ok { return val } if val, ok := builtins[exp.Value]; ok { return val } return newError("identifier not found: " + exp.Value) } func evalExpressions( exps []ast.Expression, env *object.Environment, ) []object.Object { var res []object.Object for _, exp := range exps { ev := Eval(exp, env) if isError(ev) { return []object.Object{ev} } res = append(res, ev) } return res } func evalIndexExpression(left, index object.Object) object.Object { switch { case left.Type() == object.ARRAY_OBJ && index.Type() == object.INTEGER_OBJ: return evalArrayIndexExpression(left, index) default: return newError("index operator not supported: %s", left.Type()) } } func evalArrayIndexExpression(arrayObj, indexObj object.Object) object.Object { array := arrayObj.(*object.Array).Elements index := indexObj.(*object.Integer).Value if index < 0 || index >= int64(len(array)) { return _NULL } return array[index] } func applyFunction(fnObj object.Object, args []object.Object) object.Object { switch fn := fnObj.(type) { case *object.Function: env := extendFunctionEnv(fn, args) ret := Eval(fn.Body, env) return unwrapReturnValue(ret) case *object.Builtin: return fn.Fn(args...) } return newError("not a function: %s", fnObj.Type()) } func extendFunctionEnv(fn *object.Function, args []object.Object) *object.Environment { env := object.NewEnclosedEnvironment(fn.Env) for pi, param := range fn.Parameters { env.Set(param.Value, args[pi]) } return env } func unwrapReturnValue(obj object.Object) object.Object { if ret, ok := obj.(*object.ReturnValue); ok { return ret.Value } return obj } 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 }