optimizations
Some checks failed
Build / build (push) Successful in 10m47s
Publish Image / publish (push) Failing after 33s
Test / build (push) Failing after 6m42s

This commit is contained in:
Chuck Smith
2024-04-02 14:54:08 -04:00
parent 88e3330856
commit f08b458325
9 changed files with 313 additions and 355 deletions

View File

@@ -7,16 +7,10 @@ import (
"monkey/internal/context"
"monkey/internal/lexer"
"monkey/internal/object"
"monkey/internal/ops"
"monkey/internal/parser"
"monkey/internal/utils"
"os"
"strings"
)
var (
NULL = object.Null{}
TRUE = object.Boolean{Value: true}
FALSE = object.Boolean{Value: false}
)
func isError(obj object.Object) bool {
@@ -68,7 +62,7 @@ func Eval(ctx context.Context, node ast.Node, env *object.Environment) object.Ob
env.Set(ident.Value, value)
}
return NULL
return object.NULL
}
return newError("expected identifier on left got=%T", node.Left)
@@ -119,7 +113,7 @@ func Eval(ctx context.Context, node ast.Node, env *object.Environment) object.Ob
return newError("expected identifier or index expression got=%T", left)
}
return NULL
return object.NULL
case *ast.Identifier:
return evalIdentifier(node, env)
@@ -137,10 +131,10 @@ func Eval(ctx context.Context, node ast.Node, env *object.Environment) object.Ob
return object.Float{Value: node.Value}
case *ast.Boolean:
return nativeBoolToBooleanObject(node.Value)
return object.FromNativeBoolean(node.Value)
case *ast.Null:
return NULL
return object.NULL
case *ast.PrefixExpression:
right := Eval(ctx, node.Right, env)
@@ -226,7 +220,7 @@ func evalWhileExpression(ctx context.Context, we *ast.WhileExpression, env *obje
return condition
}
if isTruthy(condition) {
if object.IsTruthy(condition) {
result = Eval(ctx, we.Consequence, env)
} else {
break
@@ -237,7 +231,7 @@ func evalWhileExpression(ctx context.Context, we *ast.WhileExpression, env *obje
return result
}
return NULL
return object.NULL
}
func evalProgram(ctx context.Context, program *ast.Program, env *object.Environment) object.Object {
@@ -275,13 +269,6 @@ func evalBlockStatements(ctx context.Context, block *ast.BlockStatement, env *ob
return result
}
func nativeBoolToBooleanObject(input bool) object.Boolean {
if input {
return TRUE
}
return FALSE
}
func evalPrefixExpression(operator string, right object.Object) object.Object {
switch operator {
case "!":
@@ -292,155 +279,97 @@ func evalPrefixExpression(operator string, right object.Object) object.Object {
case "~", "-":
return evalIntegerPrefixOperatorExpression(operator, right)
default:
return newError("unknown operator: %s%s", operator, right.Type())
return newError("unsupported operator: %s%s", operator, right.Type())
}
}
func evalBooleanPrefixOperatorExpression(operator string, right object.Object) object.Object {
if right.Type() != object.BooleanType {
return newError("unknown operator: %s%s", operator, right.Type())
return newError("unsupported operator: %s%s", operator, right.Type())
}
switch right {
case TRUE:
return FALSE
case FALSE:
return TRUE
case NULL:
return TRUE
case object.TRUE:
return object.FALSE
case object.FALSE:
return object.TRUE
case object.NULL:
return object.TRUE
default:
return FALSE
return object.FALSE
}
}
func evalIntegerPrefixOperatorExpression(operator string, right object.Object) object.Object {
if right.Type() != object.IntegerType {
return newError("unknown operator: -%s", right.Type())
return newError("unsupported operator: -%s", right.Type())
}
value := right.(object.Integer).Value
switch operator {
case "!":
return FALSE
return object.FALSE
case "~":
return object.Integer{Value: ^value}
case "-":
return object.Integer{Value: -value}
default:
return newError("unknown operator: %s", operator)
return newError("unsupported operator: %s", operator)
}
}
func evalInfixExpression(operator string, left, right object.Object) object.Object {
switch {
// {"a": 1} + {"b": 2}
case operator == "+" && left.Type() == object.HashType && right.Type() == object.HashType:
leftVal := left.(*object.Hash).Pairs
rightVal := right.(*object.Hash).Pairs
pairs := make(map[object.HashKey]object.HashPair)
for k, v := range leftVal {
pairs[k] = v
}
for k, v := range rightVal {
pairs[k] = v
}
return &object.Hash{Pairs: pairs}
// [1] + [2]
case operator == "+" && left.Type() == object.ArrayType && right.Type() == object.ArrayType:
leftVal := left.(*object.Array).Elements
rightVal := right.(*object.Array).Elements
elements := make([]object.Object, len(leftVal)+len(rightVal))
elements = append(leftVal, rightVal...)
return &object.Array{Elements: elements}
// [1] * 3
case operator == "*" && left.Type() == object.ArrayType && right.Type() == object.IntegerType:
leftVal := left.(*object.Array).Elements
rightVal := int(right.(object.Integer).Value)
elements := leftVal
for i := rightVal; i > 1; i-- {
elements = append(elements, leftVal...)
}
return &object.Array{Elements: elements}
// 3 * [1]
case operator == "*" && left.Type() == object.IntegerType && right.Type() == object.ArrayType:
leftVal := int(left.(object.Integer).Value)
rightVal := right.(*object.Array).Elements
elements := rightVal
for i := leftVal; i > 1; i-- {
elements = append(elements, rightVal...)
}
return &object.Array{Elements: elements}
// " " * 4
case operator == "*" && left.Type() == object.StringType && right.Type() == object.IntegerType:
leftVal := left.(object.String).Value
rightVal := right.(object.Integer).Value
return object.String{Value: strings.Repeat(leftVal, int(rightVal))}
// 4 * " "
case operator == "*" && left.Type() == object.IntegerType && right.Type() == object.StringType:
leftVal := left.(object.Integer).Value
rightVal := right.(object.String).Value
return object.String{Value: strings.Repeat(rightVal, int(leftVal))}
case operator == "==":
return nativeBoolToBooleanObject(left.Compare(right) == 0)
return object.FromNativeBoolean(left.Compare(right) == 0)
case operator == "!=":
return nativeBoolToBooleanObject(left.Compare(right) != 0)
return object.FromNativeBoolean(left.Compare(right) != 0)
case operator == "<=":
return nativeBoolToBooleanObject(left.Compare(right) < 1)
return object.FromNativeBoolean(left.Compare(right) < 1)
case operator == ">=":
return nativeBoolToBooleanObject(left.Compare(right) > -1)
return object.FromNativeBoolean(left.Compare(right) > -1)
case operator == "<":
return nativeBoolToBooleanObject(left.Compare(right) == -1)
return object.FromNativeBoolean(left.Compare(right) == -1)
case operator == ">":
return nativeBoolToBooleanObject(left.Compare(right) == 1)
return object.FromNativeBoolean(left.Compare(right) == 1)
case left.Type() == object.BooleanType && right.Type() == object.BooleanType:
case left.Type() == right.Type() && left.Type() == object.BooleanType:
return evalBooleanInfixExpression(operator, left, right)
case left.Type() == object.IntegerType && right.Type() == object.IntegerType:
return evalIntegerInfixExpression(operator, left, right)
case left.Type() == right.Type() && left.Type() == object.FloatType:
return evalFloatInfixExpression(operator, left, right)
case left.Type() == object.StringType && right.Type() == object.StringType:
return evalStringInfixExpression(operator, left, right)
case operator == "+":
if val, err := ops.Add(left, right); err != nil {
return newError(err.Error())
} else {
return val
}
case operator == "-":
if val, err := ops.Sub(left, right); err != nil {
return newError(err.Error())
} else {
return val
}
case operator == "*":
if val, err := ops.Mul(left, right); err != nil {
return newError(err.Error())
} else {
return val
}
case operator == "/":
if val, err := ops.Div(left, right); err != nil {
return newError(err.Error())
} else {
return val
}
case operator == "%":
if val, err := ops.Mod(left, right); err != nil {
return newError(err.Error())
} else {
return val
}
default:
return newError("unknown operator: %s %s %s", left.Type(), operator, right.Type())
}
}
func evalFloatInfixExpression(operator string, left, right object.Object) object.Object {
leftVal := left.(object.Float).Value
rightVal := right.(object.Float).Value
switch operator {
case "+":
return object.Float{Value: leftVal + rightVal}
case "-":
return object.Float{Value: leftVal - rightVal}
case "*":
return object.Float{Value: leftVal * rightVal}
case "/":
return object.Float{Value: leftVal / rightVal}
case "<":
return nativeBoolToBooleanObject(leftVal < rightVal)
case "<=":
return nativeBoolToBooleanObject(leftVal <= rightVal)
case ">":
return nativeBoolToBooleanObject(leftVal > rightVal)
case ">=":
return nativeBoolToBooleanObject(leftVal >= rightVal)
case "==":
return nativeBoolToBooleanObject(leftVal == rightVal)
case "!=":
return nativeBoolToBooleanObject(leftVal != rightVal)
default:
return NULL
return newError("unsupported operator: %s %s %s",
left.Type(), operator, right.Type())
}
}
@@ -450,65 +379,11 @@ func evalBooleanInfixExpression(operator string, left, right object.Object) obje
switch operator {
case "&&":
return nativeBoolToBooleanObject(leftVal && rightVal)
return object.FromNativeBoolean(leftVal && rightVal)
case "||":
return nativeBoolToBooleanObject(leftVal || rightVal)
return object.FromNativeBoolean(leftVal || rightVal)
default:
return newError("unknown operator: %s %s %s", left.Type(), operator, right.Type())
}
}
func evalStringInfixExpression(operator string, left object.Object, right object.Object) object.Object {
leftVal := left.(object.String).Value
rightVal := right.(object.String).Value
switch operator {
case "+":
return object.String{Value: leftVal + rightVal}
default:
return newError("unknown operator: %s %s %s", left.Type(), operator, right.Type())
}
}
func evalIntegerInfixExpression(operator string, left, 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 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 object.Integer{Value: leftVal << uint64(rightVal)}
case ">>":
return object.Integer{Value: leftVal >> uint64(rightVal)}
case "<":
return nativeBoolToBooleanObject(leftVal < rightVal)
case "<=":
return nativeBoolToBooleanObject(leftVal <= rightVal)
case ">":
return nativeBoolToBooleanObject(leftVal > rightVal)
case ">=":
return nativeBoolToBooleanObject(leftVal >= rightVal)
case "==":
return nativeBoolToBooleanObject(leftVal == rightVal)
case "!=":
return nativeBoolToBooleanObject(leftVal != rightVal)
default:
return newError("unknown operator: %s %s %s", left.Type(), operator, right.Type())
return newError("unsupported operator: %s %s %s", left.Type(), operator, right.Type())
}
}
@@ -518,25 +393,12 @@ func evalIfExpression(ctx context.Context, ie *ast.IfExpression, env *object.Env
return condition
}
if isTruthy(condition) {
if object.IsTruthy(condition) {
return Eval(ctx, ie.Consequence, env)
} else if ie.Alternative != nil {
return Eval(ctx, ie.Alternative, env)
} else {
return NULL
}
}
func isTruthy(obj object.Object) bool {
switch obj {
case NULL:
return false
case TRUE:
return true
case FALSE:
return false
default:
return true
return object.NULL
}
}
@@ -605,7 +467,7 @@ func applyFunction(ctx context.Context, fn object.Object, args []object.Object)
if result := fn.Fn(ctx, args...); result != nil {
return result
}
return NULL
return object.NULL
default:
return newError("not a function: %s", fn.Type())
@@ -673,7 +535,7 @@ func evalHashIndexExpression(hash, index object.Object) object.Object {
pair, ok := hashObject.Pairs[key.Hash()]
if !ok {
return NULL
return object.NULL
}
return pair.Value
@@ -685,7 +547,7 @@ func evalArrayIndexExpression(array, index object.Object) object.Object {
maxInx := int64(len(arrayObject.Elements) - 1)
if idx < 0 || idx > maxInx {
return NULL
return object.NULL
}
return arrayObject.Elements[idx]

View File

@@ -85,6 +85,18 @@ func TestEvalExpressions(t *testing.T) {
{"1 << 2", 4},
{"4 >> 2", 1},
{"5.0 / 2.0", 2.5},
{"1 + 2.1", 3.1},
{"2 - 0.5", 1.5},
{"2 * 2.5", 5.0},
{"5 / 2.0", 2.5},
{"5 % 2.0", 1.0},
{"2.1 + 1", 3.1},
{"2.5 - 2", 0.5},
{"2.5 * 2", 5.0},
{"5.0 / 2", 2.5},
{"5.0 % 2", 1.0},
}
for _, tt := range tests {
@@ -231,27 +243,27 @@ func TestErrorHandling(t *testing.T) {
}{
{
"5 + true;",
"unknown operator: int + bool",
"unsupported operator: int + bool",
},
{
"5 + true; 5;",
"unknown operator: int + bool",
"unsupported operator: int + bool",
},
{
"-true",
"unknown operator: -bool",
"unsupported operator: -bool",
},
{
"true + false;",
"unknown operator: bool + bool",
"unsupported operator: bool + bool",
},
{
"5; true + false; 5",
"unknown operator: bool + bool",
"unsupported operator: bool + bool",
},
{
"if (10 > 1) { true + false; }",
"unknown operator: bool + bool",
"unsupported operator: bool + bool",
},
{
`
@@ -263,7 +275,7 @@ func TestErrorHandling(t *testing.T) {
return 1;
}
`,
"unknown operator: bool + bool",
"unsupported operator: bool + bool",
},
{
"foobar",
@@ -271,7 +283,7 @@ func TestErrorHandling(t *testing.T) {
},
{
`"Hello" - "World"`,
"unknown operator: str - str",
"unsupported operator: str - str",
},
{
`{"name": "Monkey"}[fn(x) { x }];`,
@@ -676,8 +688,8 @@ func TestHashLiterals(t *testing.T) {
(object.String{Value: "two"}).Hash(): 2,
(object.String{Value: "three"}).Hash(): 3,
(object.Integer{Value: 4}).Hash(): 4,
TRUE.Hash(): 5,
FALSE.Hash(): 6,
object.TRUE.Hash(): 5,
object.FALSE.Hash(): 6,
}
if len(result.Pairs) != len(expected) {
@@ -882,7 +894,7 @@ func testBooleanObject(t *testing.T, obj object.Object, expected bool) bool {
}
func testNullObject(t *testing.T, obj object.Object) bool {
if obj != NULL {
if obj != object.NULL {
t.Errorf("object is not NULL. got=%T (%+v)", obj, obj)
return false
}