package evaluator import ( "fmt" "github/runnignwater/monkey/ast" "github/runnignwater/monkey/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) case *ast.ExpressionStatement: return Eval(node.Expression) // Expression case *ast.IntegerLiteral: return &object.Integer{Value: node.Value} case *ast.Boolean: return nativeBooleanObject(node.Value) 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.BlockStatement: return evalBlockStatements(node) case *ast.IfExpression: return evalIfExpression(node) case *ast.ReturnStatement: val := Eval(node.ReturnValue) if isError(val) { return val } return &object.ReturnValue{Value: val} } return nil } func newError(format string, a ...interface{}) *object.Error { return &object.Error{Msg: fmt.Sprintf(format, a...)} } func evalProgram(node *ast.Program) object.Object { var result object.Object for _, stmt := range node.Statements { result = Eval(stmt) // skip return 后面的语句 switch result := result.(type) { case *object.ReturnValue: return result.Value case *object.Error: return result } } return result } func nativeBooleanObject(input bool) *object.Boolean { if input { return TRUE } else { return FALSE } } func evalPrefixExpression(operator string, right object.Object) object.Object { switch operator { case "!": return evalBangOperatorExpression(right) case "-": return evalMinusPreOperatorExpression(right) default: return newError("unknown operator: %s%s", operator, right.Type()) } } func evalInfixExpression(operator string, left object.Object, right object.Object) object.Object { switch { case left.Type() == object.IntegerObj && right.Type() == object.IntegerObj: return evalIntegerInfixExpression(operator, left, right) case operator == "==": return nativeBooleanObject(left == right) case operator == "!=": return nativeBooleanObject(left != right) case left.Type() != right.Type(): return newError("type mismatch: %s %s %s", left.Type(), operator, right.Type()) default: return newError("unknown operator: %s %s %s", left.Type(), operator, right.Type()) } } func evalIfExpression(node *ast.IfExpression) object.Object { condition := Eval(node.Condition) if isError(condition) { return condition } if isTruthy(condition) { return Eval(node.Consequence) } else if node.Alternative != nil { return Eval(node.Alternative) } else { return NULL } } func evalBlockStatements(block *ast.BlockStatement) object.Object { var result object.Object for _, stmt := range block.Statements { result = Eval(stmt) if result != nil { rt := result.Type() if rt == object.ReturnValueObj || rt == object.ErrorObj { return result } } } return result } func evalIntegerInfixExpression(operator string, left object.Object, right object.Object) object.Object { leftVal := left.(*object.Integer).Value rightVal := right.(*object.Integer).Value switch operator { case "+": return &object.Integer{Value: leftVal + rightVal} case "-": return &object.Integer{Value: leftVal - rightVal} case "*": return &object.Integer{Value: leftVal * rightVal} case "/": return &object.Integer{Value: leftVal / rightVal} case "<": return nativeBooleanObject(leftVal < rightVal) case ">": return nativeBooleanObject(leftVal > rightVal) case "==": return nativeBooleanObject(leftVal == rightVal) case "!=": return nativeBooleanObject(leftVal != rightVal) default: return newError("unknown operator: %s %s %s", left.Type(), operator, right.Type()) } } func evalMinusPreOperatorExpression(right object.Object) object.Object { if right.Type() != object.IntegerObj { return newError("unknown operator: -%s", right.Type()) } value := right.(*object.Integer).Value return &object.Integer{Value: -value} } func evalBangOperatorExpression(right object.Object) object.Object { switch right { case TRUE: return FALSE case FALSE: return TRUE default: return FALSE } } func isTruthy(obj object.Object) bool { switch obj { case NULL: return false case TRUE: return true case FALSE: return false default: return true } } func isError(obj object.Object) bool { if obj != nil { return obj.Type() == object.ErrorObj } return false }