diff --git a/ast/types.go b/ast/types.go index 72a8655..a995b97 100644 --- a/ast/types.go +++ b/ast/types.go @@ -1,6 +1,8 @@ package ast import ( + "math/big" + "github.com/0xM-D/interpreter/token" ) @@ -58,7 +60,7 @@ type Identifier struct { type IntegerLiteral struct { Token token.Token - Value int64 + Value big.Int } type Float32Literal struct { diff --git a/evaluator/eval_infix_expression.go b/evaluator/eval_infix_expression.go index d9db414..d8a8bc4 100644 --- a/evaluator/eval_infix_expression.go +++ b/evaluator/eval_infix_expression.go @@ -1,6 +1,8 @@ package evaluator import ( + "math" + "github.com/0xM-D/interpreter/ast" "github.com/0xM-D/interpreter/object" "github.com/0xM-D/interpreter/token" @@ -20,12 +22,6 @@ var infixEvalFns = map[OperatorFnSignature]InfixEvalFn{ {"&&", string(object.BooleanKind), string(object.BooleanKind)}: booleanAnd, {"||", string(object.BooleanKind), string(object.BooleanKind)}: booleanOr, - {"&", string(object.IntegerKind), string(object.IntegerKind)}: integerBitwiseAnd, - {"|", string(object.IntegerKind), string(object.IntegerKind)}: integerBitwiseOr, - {"^", string(object.IntegerKind), string(object.IntegerKind)}: integerBitwiseXor, - {">>", string(object.IntegerKind), string(object.IntegerKind)}: integerBitwiseShiftRight, - {"<<", string(object.IntegerKind), string(object.IntegerKind)}: integerBitwiseShiftLeft, - {"+", string(object.StringKind), string(object.StringKind)}: stringAddition, {"+=", string(object.StringKind), string(object.StringKind)}: stringPlusEquals, } @@ -67,247 +63,302 @@ func evalInfixExpression(node *ast.InfixExpression, env *object.Environment) obj } func evalNumberInfixExpression(left object.Object, right object.Object, operator string, env *object.Environment) object.Object { + leftNum := object.UnwrapReferenceObject(left).(*object.Number) + rightNum := object.UnwrapReferenceObject(right).(*object.Number) switch operator { case string(token.PLUS): - return numberAddition(left, right, env) + return numberAddition(leftNum, rightNum, env) case string(token.MINUS): - return numberSubtraction(left, right, env) + return numberSubtraction(leftNum, rightNum, env) case string(token.ASTERISK): - return numberMultiplication(left, right, env) + return numberMultiplication(leftNum, rightNum, env) case string(token.SLASH): - return numberDivision(left, right, env) + return numberDivision(leftNum, rightNum, env) + case string(token.B_SHIFT_L): + return numberBitwiseShiftLeft(leftNum, rightNum, env) + case string(token.B_SHIFT_R): + return numberBitwiseShiftRight(leftNum, rightNum, env) + case string(token.B_AND): + return numberBitwiseAnd(leftNum, rightNum, env) + case string(token.B_OR): + return numberBitwiseOr(leftNum, rightNum, env) + case string(token.B_XOR): + return numberBitwiseXor(leftNum, rightNum, env) case string(token.EQ): - return numberEquals(left, right, env) + return numberEquals(leftNum, rightNum, env) case string(token.NOT_EQ): - return numberNotEquals(left, right, env) + return numberNotEquals(leftNum, rightNum, env) case string(token.GT): - return numberGreaterThan(left, right, env) + return numberGreaterThan(leftNum, rightNum, env) case string(token.LT): - return numberLessThan(left, right, env) + return numberLessThan(leftNum, rightNum, env) case string(token.LTE): - return numberLessThanEqual(left, right, env) + return numberLessThanEqual(leftNum, rightNum, env) case string(token.GTE): - return numberGreaterThanEqual(left, right, env) + return numberGreaterThanEqual(leftNum, rightNum, env) case string(token.PLUS_ASSIGN): - return numberPlusEquals(left, right, env) + return numberPlusEquals(left, rightNum, env) case string(token.MINUS_ASSIGN): - return numberMinusEquals(left, right, env) + return numberMinusEquals(left, rightNum, env) case string(token.ASTERISK_ASSIGN): - return numberTimesEquals(left, right, env) + return numberTimesEquals(left, rightNum, env) case string(token.SLASH_ASSIGN): - return numberDivideEquals(left, right, env) - + return numberDivideEquals(left, rightNum, env) default: return newError("operator %s not defined on types %s and %s", operator, left.Type().Signature(), right.Type().Signature()) } } -func add[T int64 | float32 | float64](a object.Object, b object.Object) *object.Number[T] { - return &object.Number[T]{Value: a.(*object.Number[T]).Value + b.(*object.Number[T]).Value} -} +func numberAddition(left *object.Number, right *object.Number, env *object.Environment) object.Object { + leftNum, rightNum, err := arithmeticCast(left, right) + if err != nil { + return newError(err.Error()) + } -func subtract[T int64 | float32 | float64](a object.Object, b object.Object) *object.Number[T] { - return &object.Number[T]{Value: a.(*object.Number[T]).Value - b.(*object.Number[T]).Value} -} + var sum *object.Number -func multiply[T int64 | float32 | float64](a object.Object, b object.Object) *object.Number[T] { - return &object.Number[T]{Value: a.(*object.Number[T]).Value * b.(*object.Number[T]).Value} -} + if object.IsInteger(leftNum) && leftNum.IsSigned() { + sum = &object.Number{Value: object.Int64Bits(leftNum.GetInt64() + rightNum.GetInt64()), Kind: object.Int64Kind} + } else if object.IsInteger(leftNum) && leftNum.IsUnsigned() { + sum = &object.Number{Value: leftNum.GetUInt64() + rightNum.GetUInt64(), Kind: object.UInt64Kind} + } else if object.IsFloat32(leftNum) { + sum = &object.Number{Value: uint64(math.Float32bits(leftNum.GetFloat32() + rightNum.GetFloat32())), Kind: object.Float32Kind} + } else if object.IsFloat64(leftNum) { + sum = &object.Number{Value: math.Float64bits(leftNum.GetFloat64() + rightNum.GetFloat64()), Kind: object.Float64Kind} + } -func divide[T int64 | float32 | float64](a object.Object, b object.Object) *object.Number[T] { - return &object.Number[T]{Value: a.(*object.Number[T]).Value / b.(*object.Number[T]).Value} -} + castedSum, err := numberCast(sum, leftNum.Kind, EXPLICIT_CAST) -func lessThan[T int64 | float32 | float64](a object.Object, b object.Object) *object.Boolean { - return nativeBoolToBooleanObject(a.(*object.Number[T]).Value < b.(*object.Number[T]).Value) -} + if err != nil { + return newError(err.Error()) + } -func greaterThan[T int64 | float32 | float64](a object.Object, b object.Object) *object.Boolean { - return nativeBoolToBooleanObject(a.(*object.Number[T]).Value > b.(*object.Number[T]).Value) + return castedSum } -func equals[T int64 | float32 | float64](a object.Object, b object.Object) *object.Boolean { - return nativeBoolToBooleanObject(a.(*object.Number[T]).Value == b.(*object.Number[T]).Value) -} +func numberSubtraction(left *object.Number, right *object.Number, env *object.Environment) object.Object { + leftNum, rightNum, err := arithmeticCast(left, right) -func notEquals[T int64 | float32 | float64](a object.Object, b object.Object) *object.Boolean { - return nativeBoolToBooleanObject(a.(*object.Number[T]).Value != b.(*object.Number[T]).Value) -} + if err != nil { + return newError(err.Error()) + } -func integerBitwiseAnd(a object.Object, b object.Object, env *object.Environment) object.Object { - return &object.Number[int64]{Value: a.(*object.Number[int64]).Value & b.(*object.Number[int64]).Value} -} + var difference *object.Number -func integerBitwiseOr(a object.Object, b object.Object, env *object.Environment) object.Object { - return &object.Number[int64]{Value: a.(*object.Number[int64]).Value | b.(*object.Number[int64]).Value} -} + if object.IsInteger(leftNum) && leftNum.IsSigned() { + difference = &object.Number{Value: object.Int64Bits(leftNum.GetInt64() - rightNum.GetInt64()), Kind: object.Int64Kind} + } else if object.IsInteger(leftNum) && leftNum.IsUnsigned() { + difference = &object.Number{Value: leftNum.GetUInt64() - rightNum.GetUInt64(), Kind: object.UInt64Kind} + } else if object.IsFloat32(leftNum) { + difference = &object.Number{Value: uint64(math.Float32bits(leftNum.GetFloat32() - rightNum.GetFloat32())), Kind: object.Float32Kind} + } else if object.IsFloat64(leftNum) { + difference = &object.Number{Value: math.Float64bits(leftNum.GetFloat64() - rightNum.GetFloat64()), Kind: object.Float64Kind} + } -func integerBitwiseXor(a object.Object, b object.Object, env *object.Environment) object.Object { - return &object.Number[int64]{Value: a.(*object.Number[int64]).Value ^ b.(*object.Number[int64]).Value} -} + castedDiffrence, err := numberCast(difference, leftNum.Kind, EXPLICIT_CAST) + if err != nil { + return newError(err.Error()) + } -func integerBitwiseShiftLeft(a object.Object, b object.Object, env *object.Environment) object.Object { - return &object.Number[int64]{Value: a.(*object.Number[int64]).Value << b.(*object.Number[int64]).Value} + return castedDiffrence } -func integerBitwiseShiftRight(a object.Object, b object.Object, env *object.Environment) object.Object { - return &object.Number[int64]{Value: a.(*object.Number[int64]).Value >> b.(*object.Number[int64]).Value} -} +func numberMultiplication(left *object.Number, right *object.Number, env *object.Environment) object.Object { + leftNum, rightNum, err := arithmeticCast(left, right) + + if err != nil { + return newError(err.Error()) + } -func numberAddition(left object.Object, right object.Object, env *object.Environment) object.Object { - leftNum, rightNum, kind := castToLargerNumberType(left, right) + var product *object.Number - switch kind { - case object.IntegerKind: - return add[int64](leftNum, rightNum) - case object.Float32Kind: - return add[float32](leftNum, rightNum) - case object.Float64Kind: - return add[float64](leftNum, rightNum) + if object.IsInteger(leftNum) && leftNum.IsSigned() { + product = &object.Number{Value: object.Int64Bits(leftNum.GetInt64() * rightNum.GetInt64()), Kind: object.Int64Kind} + } else if object.IsInteger(leftNum) && leftNum.IsUnsigned() { + product = &object.Number{Value: leftNum.GetUInt64() * rightNum.GetUInt64(), Kind: object.UInt64Kind} + } else if object.IsFloat32(leftNum) { + product = &object.Number{Value: uint64(math.Float32bits(leftNum.GetFloat32() * rightNum.GetFloat32())), Kind: object.Float32Kind} + } else if object.IsFloat64(leftNum) { + product = &object.Number{Value: math.Float64bits(leftNum.GetFloat64() * rightNum.GetFloat64()), Kind: object.Float64Kind} } - return nil + castedProduct, err := numberCast(product, leftNum.Kind, EXPLICIT_CAST) + + if err != nil { + return newError(err.Error()) + } + + return castedProduct } -func numberSubtraction(left object.Object, right object.Object, env *object.Environment) object.Object { - leftNum, rightNum, kind := castToLargerNumberType(left, right) +func numberDivision(left *object.Number, right *object.Number, env *object.Environment) object.Object { + leftNum, rightNum, err := arithmeticCast(left, right) - switch kind { - case object.IntegerKind: - return subtract[int64](leftNum, rightNum) - case object.Float32Kind: - return subtract[float32](leftNum, rightNum) - case object.Float64Kind: - return subtract[float64](leftNum, rightNum) + if err != nil { + return newError(err.Error()) } - return nil -} + var quotient *object.Number -func numberMultiplication(left object.Object, right object.Object, env *object.Environment) object.Object { - leftNum, rightNum, kind := castToLargerNumberType(left, right) + if object.IsInteger(leftNum) && leftNum.IsSigned() { + quotient = &object.Number{Value: object.Int64Bits(leftNum.GetInt64() / rightNum.GetInt64()), Kind: object.Int64Kind} + } else if object.IsInteger(leftNum) && leftNum.IsUnsigned() { + quotient = &object.Number{Value: leftNum.GetUInt64() / rightNum.GetUInt64(), Kind: object.UInt64Kind} + } else if object.IsFloat32(leftNum) { + quotient = &object.Number{Value: uint64(math.Float32bits(leftNum.GetFloat32() / rightNum.GetFloat32())), Kind: object.Float32Kind} + } else if object.IsFloat64(leftNum) { + quotient = &object.Number{Value: math.Float64bits(leftNum.GetFloat64() / rightNum.GetFloat64()), Kind: object.Float64Kind} + } - switch kind { - case object.IntegerKind: - return multiply[int64](leftNum, rightNum) - case object.Float32Kind: - return multiply[float32](leftNum, rightNum) - case object.Float64Kind: - return multiply[float64](leftNum, rightNum) + castedQuotient, err := numberCast(quotient, leftNum.Kind, EXPLICIT_CAST) + + if err != nil { + return newError(err.Error()) } - return nil + return castedQuotient } -func numberDivision(left object.Object, right object.Object, env *object.Environment) object.Object { - leftNum, rightNum, kind := castToLargerNumberType(left, right) - - switch kind { - case object.IntegerKind: - return divide[int64](leftNum, rightNum) - case object.Float32Kind: - return divide[float32](leftNum, rightNum) - case object.Float64Kind: - return divide[float64](leftNum, rightNum) +func numberBitwiseShiftLeft(left *object.Number, right *object.Number, env *object.Environment) object.Object { + if object.IsFloat32(right) || object.IsFloat64(right) { + return newError("Operator << not defined for %s and %s", left.Kind, right.Kind) + } else if right.IsSigned() && right.GetInt64() < 0 { + return newError("Operator << not defined on negative shift amount") } - return nil + return &object.Number{Value: left.Value << right.GetUInt64(), Kind: left.Kind} } -func numberLessThan(left object.Object, right object.Object, env *object.Environment) object.Object { - leftNum, rightNum, kind := castToLargerNumberType(left, right) - switch kind { - case object.IntegerKind: - return lessThan[int64](leftNum, rightNum) - case object.Float32Kind: - return lessThan[float32](leftNum, rightNum) - case object.Float64Kind: - return lessThan[float64](leftNum, rightNum) +func numberBitwiseShiftRight(left *object.Number, right *object.Number, env *object.Environment) object.Object { + if object.IsFloat32(right) || object.IsFloat64(right) { + return newError("Operator >> not defined for %s and %s", left.Kind, right.Kind) + } else if right.IsSigned() && right.GetInt64() < 0 { + return newError("Operator >> not defined on negative shift amount") } - return nil + return &object.Number{Value: left.Value >> right.GetUInt64(), Kind: left.Kind} } -func numberLessThanEqual(left object.Object, right object.Object, env *object.Environment) object.Object { - return evalBangOperatorExpression(numberGreaterThan(left, right, env)) +func numberBitwiseAnd(left *object.Number, right *object.Number, env *object.Environment) object.Object { + return &object.Number{Value: left.Value & right.GetUInt64(), Kind: left.Kind} } -func numberGreaterThanEqual(left object.Object, right object.Object, env *object.Environment) object.Object { - return evalBangOperatorExpression(numberLessThan(left, right, env)) +func numberBitwiseOr(left *object.Number, right *object.Number, env *object.Environment) object.Object { + return &object.Number{Value: left.Value | right.GetUInt64(), Kind: left.Kind} } -func numberGreaterThan(left object.Object, right object.Object, env *object.Environment) object.Object { - leftNum, rightNum, kind := castToLargerNumberType(left, right) +func numberBitwiseXor(left *object.Number, right *object.Number, env *object.Environment) object.Object { + return &object.Number{Value: left.Value ^ right.GetUInt64(), Kind: left.Kind} +} - switch kind { - case object.IntegerKind: - return greaterThan[int64](leftNum, rightNum) - case object.Float32Kind: - return greaterThan[float32](leftNum, rightNum) - case object.Float64Kind: - return greaterThan[float64](leftNum, rightNum) +func numberLessThan(left *object.Number, right *object.Number, env *object.Environment) object.Object { + leftNum, rightNum, err := arithmeticCast(left, right) + + if err != nil { + return newError(err.Error()) + } + + if object.IsInteger(leftNum) && leftNum.IsSigned() { + return nativeBoolToBooleanObject(leftNum.GetInt64() < rightNum.GetInt64()) + } else if object.IsInteger(leftNum) && leftNum.IsUnsigned() { + return nativeBoolToBooleanObject(leftNum.GetUInt64() < rightNum.GetUInt64()) + } else if object.IsFloat32(leftNum) { + return nativeBoolToBooleanObject(leftNum.GetFloat32() < rightNum.GetFloat32()) + } else if object.IsFloat64(leftNum) { + return nativeBoolToBooleanObject(leftNum.GetFloat64() < rightNum.GetFloat64()) } - return nil + return NULL } -func numberEquals(left object.Object, right object.Object, env *object.Environment) object.Object { - leftNum, rightNum, kind := castToLargerNumberType(left, right) +func numberGreaterThan(left *object.Number, right *object.Number, env *object.Environment) object.Object { + leftNum, rightNum, err := arithmeticCast(left, right) - switch kind { - case object.IntegerKind: - return equals[int64](leftNum, rightNum) - case object.Float32Kind: - return equals[float32](leftNum, rightNum) - case object.Float64Kind: - return equals[float64](leftNum, rightNum) + if err != nil { + return newError(err.Error()) + } + + if object.IsInteger(leftNum) && leftNum.IsSigned() { + return nativeBoolToBooleanObject(leftNum.GetInt64() > rightNum.GetInt64()) + } else if object.IsInteger(leftNum) && leftNum.IsUnsigned() { + return nativeBoolToBooleanObject(leftNum.GetUInt64() > rightNum.GetUInt64()) + } else if object.IsFloat32(leftNum) { + return nativeBoolToBooleanObject(leftNum.GetFloat32() > rightNum.GetFloat32()) + } else if object.IsFloat64(leftNum) { + return nativeBoolToBooleanObject(leftNum.GetFloat64() > rightNum.GetFloat64()) } - return nil + return NULL } -func numberNotEquals(left object.Object, right object.Object, env *object.Environment) object.Object { - leftNum, rightNum, kind := castToLargerNumberType(left, right) +func numberEquals(left *object.Number, right *object.Number, env *object.Environment) object.Object { + leftNum, rightNum, err := arithmeticCast(left, right) - switch kind { - case object.IntegerKind: - return notEquals[int64](leftNum, rightNum) - case object.Float32Kind: - return notEquals[float32](leftNum, rightNum) - case object.Float64Kind: - return notEquals[float64](leftNum, rightNum) + if err != nil { + return newError(err.Error()) } - return nil + if object.IsInteger(leftNum) && leftNum.IsSigned() { + return nativeBoolToBooleanObject(leftNum.GetInt64() == rightNum.GetInt64()) + } else if object.IsInteger(leftNum) && leftNum.IsUnsigned() { + return nativeBoolToBooleanObject(leftNum.GetUInt64() == rightNum.GetUInt64()) + } else if object.IsFloat32(leftNum) { + return nativeBoolToBooleanObject(leftNum.GetFloat32() == rightNum.GetFloat32()) + } else if object.IsFloat64(leftNum) { + return nativeBoolToBooleanObject(leftNum.GetFloat64() == rightNum.GetFloat64()) + } + + return NULL } -func numberPlusEquals(left object.Object, right object.Object, env *object.Environment) object.Object { - return assignment(left, numberAddition(left, right, env), env) +func numberLessThanEqual(left *object.Number, right *object.Number, env *object.Environment) object.Object { + return evalBangOperatorExpression(numberGreaterThan(left, right, env)) } -func numberMinusEquals(left object.Object, right object.Object, env *object.Environment) object.Object { - return assignment(left, numberSubtraction(left, right, env), env) +func numberGreaterThanEqual(left *object.Number, right *object.Number, env *object.Environment) object.Object { + return evalBangOperatorExpression(numberLessThan(left, right, env)) } -func numberTimesEquals(left object.Object, right object.Object, env *object.Environment) object.Object { - return assignment(left, numberMultiplication(left, right, env), env) +func numberNotEquals(left *object.Number, right *object.Number, env *object.Environment) object.Object { + return evalBangOperatorExpression(numberEquals(left, right, env)) } -func numberDivideEquals(left object.Object, right object.Object, env *object.Environment) object.Object { - return assignment(left, numberDivision(left, right, env), env) +func numberPlusEquals(left object.Object, right *object.Number, env *object.Environment) object.Object { + return assignment(left, numberAddition(object.UnwrapReferenceObject(left).(*object.Number), right, env), env) +} + +func numberMinusEquals(left object.Object, right *object.Number, env *object.Environment) object.Object { + return assignment(left, numberSubtraction(object.UnwrapReferenceObject(left).(*object.Number), right, env), env) +} + +func numberTimesEquals(left object.Object, right *object.Number, env *object.Environment) object.Object { + return assignment(left, numberMultiplication(object.UnwrapReferenceObject(left).(*object.Number), right, env), env) +} + +func numberDivideEquals(left object.Object, right *object.Number, env *object.Environment) object.Object { + return assignment(left, numberDivision(object.UnwrapReferenceObject(left).(*object.Number), right, env), env) } func assignment(left object.Object, right object.Object, env *object.Environment) object.Object { lvalue, ok := left.(object.ObjectReference) rvalue := object.UnwrapReferenceObject(right) + lvalueType := object.UnwrapReferenceType(lvalue.GetValue().Type()) + rvalueType := object.UnwrapReferenceType(rvalue.Type()) + if !ok { return newError("Invalid lvalue %s", left.Inspect()) } if lvalue.Type().IsConstant() { return newError("Cannot assign to const variable") } - if lvalue.GetValue().Type().Signature() != rvalue.Type().Signature() { - return newError("Cannot assign %s to %s", lvalue.Type().Signature(), right.Type().Signature()) + + if lvalueType.Signature() != rvalueType.Signature() { + cast := typeCast(rvalue, lvalueType, EXPLICIT_CAST) + if !object.IsError(cast) { + rvalue = cast + } else { + return cast + } } _, err := lvalue.UpdateValue(rvalue) diff --git a/evaluator/evaluator.go b/evaluator/evaluator.go index f1e1b99..3e73167 100644 --- a/evaluator/evaluator.go +++ b/evaluator/evaluator.go @@ -1,6 +1,8 @@ package evaluator import ( + "math" + "github.com/0xM-D/interpreter/ast" "github.com/0xM-D/interpreter/object" ) @@ -12,11 +14,11 @@ func Eval(node ast.Node, env *object.Environment) object.Object { case *ast.ExpressionStatement: return Eval(node.Expression, env) case *ast.IntegerLiteral: - return &object.Number[int64]{Value: node.Value} + return evalIntegerLiteral(node, env) case *ast.Float32Literal: - return &object.Number[float32]{Value: node.Value} + return &object.Number{Value: uint64(math.Float32bits(node.Value)), Kind: object.Float32Kind} case *ast.Float64Literal: - return &object.Number[float64]{Value: node.Value} + return &object.Number{Value: math.Float64bits(node.Value), Kind: object.Float64Kind} case *ast.Boolean: return nativeBoolToBooleanObject(node.Value) case *ast.PrefixExpression: @@ -38,10 +40,7 @@ func Eval(node ast.Node, env *object.Environment) object.Object { } return &object.ReturnValue{Value: val, ReturnValueObjectType: object.ReturnValueObjectType{ReturnType: val.Type()}} case *ast.LetStatement: - error := declareVariable(&(*node).DeclarationStatement, nil, env) - if error != nil { - return error - } + return evalDeclarationStatement(&(*node).DeclarationStatement, env) case *ast.Identifier: return evalIdentifier(node, env) case *ast.FunctionLiteral: @@ -79,20 +78,11 @@ func Eval(node ast.Node, env *object.Environment) object.Object { } return evalAccessExpression(object.UnwrapReferenceObject(left), right.Value, env) case *ast.TypedDeclarationStatement: - error := evalDeclarationStatement(&(*node).DeclarationStatement, env) - if error != nil { - return error - } + return evalDeclarationStatement(&(*node).DeclarationStatement, env) case *ast.AssignmentDeclarationStatement: - error := evalDeclarationStatement(&(*node).DeclarationStatement, env) - if error != nil { - return error - } + return evalDeclarationStatement(&(*node).DeclarationStatement, env) case *ast.ForStatement: - error := evalForStatement(node, env) - if error != nil { - return error - } + return evalForStatement(node, env) case *ast.TernaryExpression: return evalTernaryExpression(node, env) case *ast.TypeCastExpression: diff --git a/evaluator/tests/array_literals_test.go b/evaluator/tests/array_literals_test.go index 51a3d21..37aa714 100644 --- a/evaluator/tests/array_literals_test.go +++ b/evaluator/tests/array_literals_test.go @@ -1,6 +1,7 @@ package evaluator_tests import ( + "math/big" "testing" "github.com/0xM-D/interpreter/object" @@ -21,8 +22,8 @@ func TestArrayLiterals(t *testing.T) { len(result.Elements)) } - testIntegerObject(t, result.Elements[0], 1) - testIntegerObject(t, result.Elements[1], 4) - testIntegerObject(t, result.Elements[2], 6) + testIntegerObject(t, result.Elements[0], big.NewInt(1)) + testIntegerObject(t, result.Elements[1], big.NewInt(4)) + testIntegerObject(t, result.Elements[2], big.NewInt(6)) } diff --git a/evaluator/tests/assignment_declaration_test.go b/evaluator/tests/assignment_declaration_test.go index 106a0d2..32f8495 100644 --- a/evaluator/tests/assignment_declaration_test.go +++ b/evaluator/tests/assignment_declaration_test.go @@ -1,6 +1,9 @@ package evaluator_tests -import "testing" +import ( + "math/big" + "testing" +) func TestAssignmentDeclaration(t *testing.T) { tests := []struct { @@ -13,6 +16,6 @@ func TestAssignmentDeclaration(t *testing.T) { {"a := 5; let b = a; let c = a + b + 5; c;", 15}, } for _, tt := range tests { - testIntegerObject(t, testEval(tt.input), tt.expected) + testIntegerObject(t, testEval(tt.input), big.NewInt(tt.expected)) } } diff --git a/evaluator/tests/assignment_expression_test.go b/evaluator/tests/assignment_expression_test.go index ed3c8cc..c4fff82 100644 --- a/evaluator/tests/assignment_expression_test.go +++ b/evaluator/tests/assignment_expression_test.go @@ -1,25 +1,26 @@ package evaluator_tests -import "testing" +import ( + "math/big" + "testing" +) func TestAsignmentExpression(t *testing.T) { tests := []struct { input string expected interface{} }{ - {"let a = 5; a = 3;", 3}, - {"a := 5 * 5; a += 20;", 45}, - {"a := 3; b := a; a += b", 6}, - {"a := 5; a -= 1", 4}, - {"a := 5; a *= 2", 10}, - {"a := 50; a /= 5", 10}, + {"let a = 5; a = 3;", big.NewInt(3)}, + {"a := 5 * 5; a += 20;", big.NewInt(45)}, + {"a := 3; b := a; a += b", big.NewInt(6)}, + {"a := 5; a -= 1", big.NewInt(4)}, + {"a := 5; a *= 2", big.NewInt(10)}, + {"a := 50; a /= 5", big.NewInt(10)}, {`a := "a"; a += "bc"`, "abc"}, } for _, tt := range tests { switch expected := tt.expected.(type) { - case int: - testIntegerObject(t, testEval(tt.input), int64(expected)) - case int64: + case *big.Int: testIntegerObject(t, testEval(tt.input), expected) case string: testStringObject(t, testEval(tt.input), expected) diff --git a/evaluator/tests/error_handling_test.go b/evaluator/tests/error_handling_test.go index dc47033..4ef7447 100644 --- a/evaluator/tests/error_handling_test.go +++ b/evaluator/tests/error_handling_test.go @@ -13,15 +13,15 @@ func TestErrorHandling(t *testing.T) { }{ { "5 + true;", - "operator + not defined on types int and bool", + "operator + not defined on types int64 and bool", }, { "5 + true; 5;", - "operator + not defined on types int and bool", + "operator + not defined on types int64 and bool", }, { "-true", - "unknown operator: -bool", + "Operator - not defined on type bool", }, { "true + false;", @@ -56,7 +56,7 @@ func TestErrorHandling(t *testing.T) { }, { `int a = "fasdf"`, - "Expression of type string cannot be assigned to int", + "Expression of type string cannot be assigned to int64", }, { `a := "fasdf"; bool c = a;`, @@ -71,7 +71,7 @@ func TestErrorHandling(t *testing.T) { "Cannot assign to const variable", }, { - `const int a = 3; a = 5*6;`, + `const int32 a = 3; a = 5*6;`, "Cannot assign to const variable", }, { @@ -80,14 +80,14 @@ func TestErrorHandling(t *testing.T) { }, { `a := 3; a += "test"`, - "operator += not defined on types int and string", + "operator += not defined on types int64 and string", }, { `bool a = true; a += true`, "operator += not defined on types bool and bool", }, { - `const int a = 3; a += 1`, + `const int64 a = 3; a += 1`, "Cannot assign to const variable", }, { @@ -95,12 +95,12 @@ func TestErrorHandling(t *testing.T) { "Incorrect parameter count for function() -> void fun. expected=0, got=1", }, { - `fun := fn(a: int, b: int)->int { return a + b; }; fun()`, - "Incorrect parameter count for function(int, int) -> int fun. expected=2, got=0", + `fun := fn(a: int64, b: int64)->int64 { return a + b; }; fun()`, + "Incorrect parameter count for function(int64, int64) -> int64 fun. expected=2, got=0", }, { `new []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}.deleteee(1, 3)`, - "Member deleteee does not exist on int[]", + "Member deleteee does not exist on []int64", }, } for _, tt := range tests { diff --git a/evaluator/tests/eval_float_exp_test.go b/evaluator/tests/eval_float_exp_test.go index 666f450..cfc91db 100644 --- a/evaluator/tests/eval_float_exp_test.go +++ b/evaluator/tests/eval_float_exp_test.go @@ -1,6 +1,7 @@ package evaluator_tests import ( + "math/big" "testing" ) @@ -11,12 +12,15 @@ func TestEvalFloatExpression(t *testing.T) { expected interface{} }{ {"5.0", 5.0}, + {"-6.0", -6.0}, {"1.1f", float32(1.1)}, {"-5f", float32(-5.0)}, {"-10.22233344f", -float32(10.22233344)}, + {"2.0 + 2", 4.0}, + {"2.0f * 3", float32(6.0)}, {"5.0 + 5.0f + .5 + 5 - 10", 5.5}, {"2.0 * 2f * 2.0f * 2 * 2", 32.0}, - {"-50 + 100 + -50", 0}, + {"-50 + 100 + -50", big.NewInt(0)}, {"5f * 2 + 10f", float32(20)}, {"5 + 2f * 10", float32(25)}, {"20f + 2.0 * -10f", 0.0}, diff --git a/evaluator/tests/eval_integer_exp_test.go b/evaluator/tests/eval_integer_exp_test.go index 7d0a8b0..4efb4f1 100644 --- a/evaluator/tests/eval_integer_exp_test.go +++ b/evaluator/tests/eval_integer_exp_test.go @@ -1,48 +1,49 @@ package evaluator_tests import ( + "math/big" "testing" ) func TestEvalIntegerExpression(t *testing.T) { tests := []struct { input string - expected int64 + expected *big.Int }{ - {"5", 5}, - {"10", 10}, - {"-5", -5}, - {"-10", -10}, - {"5 + 5 + 5 + 5 - 10", 10}, - {"2 * 2 * 2 * 2 * 2", 32}, - {"-50 + 100 + -50", 0}, - {"5 * 2 + 10", 20}, - {"5 + 2 * 10", 25}, - {"20 + 2 * -10", 0}, - {"50 / 2 * 2 + 10", 60}, - {"2 * (5 + 10)", 30}, - {"3 * 3 * 3 + 10", 37}, - {"3 * (3 * 3) + 10", 37}, - {"(5 + 10 * 2 + 15 / 3) * 2 + -10", 50}, - {"1 << 1", 2}, - {"1 << 62", 1 << 62}, - {"1 << 63 >> 1", -(1 << 63 >> 1)}, - {"1 << 64 >> 2", 0}, - {"1 >> 1", 0}, - {"256 >> 2", 64}, - {"3 >> 1", 1}, - {"3 << 1", 6}, - {"1 | 3", 3}, - {"4097 | 272", 4369}, - {"0 | 272", 272}, - {"0 | 0", 0}, - {"0 & 0", 0}, - {"0 & 1", 0}, - {"4097 & 272", 0}, - {"7 & 3", 3}, - {"~0", ^0}, - {"((1 << 10) - 1) ^ (1 << 8)", ((1 << 10) - 1) ^ (1 << 8)}, - {"~123", ^123}, + {"5", big.NewInt(5)}, + {"10", big.NewInt(10)}, + {"-5", big.NewInt(-5)}, + {"-10", big.NewInt(-10)}, + {"5 + 5 + 5 + 5 - 10", big.NewInt(10)}, + {"2 * 2 * 2 * 2 * 2", big.NewInt(32)}, + {"-50 + 100 + -50", big.NewInt(0)}, + {"5 * 2 + 10", big.NewInt(20)}, + {"5 + 2 * 10", big.NewInt(25)}, + {"20 + 2 * -10", big.NewInt(0)}, + {"50 / 2 * 2 + 10", big.NewInt(60)}, + {"2 * (5 + 10)", big.NewInt(30)}, + {"3 * 3 * 3 + 10", big.NewInt(37)}, + {"3 * (3 * 3) + 10", big.NewInt(37)}, + {"(5 + 10 * 2 + 15 / 3) * 2 + -10", big.NewInt(50)}, + {"1 << 1", big.NewInt(2)}, + {"1 << 62", big.NewInt(4611686018427387904)}, + {"1 << 63 >> 1", big.NewInt(1 << 63 >> 1)}, + {"1 << 64 >> 2", big.NewInt(0)}, + {"1 >> 1", big.NewInt(0)}, + {"256 >> 2", big.NewInt(64)}, + {"3 >> 1", big.NewInt(1)}, + {"3 << 1", big.NewInt(6)}, + {"1 | 3", big.NewInt(3)}, + {"4097 | 272", big.NewInt(4369)}, + {"0 | 272", big.NewInt(272)}, + {"0 | 0", big.NewInt(0)}, + {"0 & 0", big.NewInt(0)}, + {"0 & 1", big.NewInt(0)}, + {"4097 & 272", big.NewInt(0)}, + {"7 & 3", big.NewInt(3)}, + {"~0", big.NewInt(^0)}, + {"((1 << 10) - 1) ^ (1 << 8)", big.NewInt(int64((1<<10)-1) ^ (1 << 8))}, + {"~123", big.NewInt(^123)}, } for _, tt := range tests { evaluated := testEval(tt.input) diff --git a/evaluator/tests/for_statement_test.go b/evaluator/tests/for_statement_test.go index 04a03f2..05b9d0c 100644 --- a/evaluator/tests/for_statement_test.go +++ b/evaluator/tests/for_statement_test.go @@ -7,10 +7,10 @@ func TestForStatement(t *testing.T) { input string expected interface{} }{ - {`string a = ""; for(int i = 0; i < 10; i+=1) { a += "a" }; a`, "aaaaaaaaaa"}, - {"int x = 55; for(int i = 10; i >= 0; i-=1) { x -= i }; x", 0}, - {"int i = 0; for(; i < 20; i+=1) {}; i", 20}, - {"int i = 5; for(; i > 0 ;) { i-=1 } i", 0}, + {`string a = ""; for(int8 i = 0; i < 10; i+=1) { a += "a" }; a`, "aaaaaaaaaa"}, + {"let x = 55; for(int16 i = 10; i >= 0; i-=1) { x -= i }; x", 0}, + {"let i = 0; for(; i < 20; i+=1) {}; i", 20}, + {"let i = 5; for(; i > 0 ;) { i-=1 } i", 0}, } for _, tt := range tests { testLiteralObject(t, testEval(tt.input), tt.expected) diff --git a/evaluator/tests/function_application_test.go b/evaluator/tests/function_application_test.go index a1ecc78..6d32526 100644 --- a/evaluator/tests/function_application_test.go +++ b/evaluator/tests/function_application_test.go @@ -1,21 +1,24 @@ package evaluator_tests -import "testing" +import ( + "math/big" + "testing" +) func TestFunctionApplication(t *testing.T) { tests := []struct { input string expected int64 }{ - {"let identity = fn(x: int) -> int { x; }; identity(5);", 5}, - {"let identity = fn(x: int) -> int { return x; }; identity(5);", 5}, - {"let double = fn(x: int) -> int { x * 2; }; double(5);", 10}, - {"let add = fn(x: int, y: int) -> int { x + y; }; add(5, 5);", 10}, - {"let add = fn(x: int, y: int) -> int { x + y; }; add(5 + 5, add(5, 5));", 20}, - {"fn(x: int)-> int { x; }(5)", 5}, - {"a := 3; b := 4; let add = fn(x: int, y: int) -> int { x + y; }; add(a, b);", 7}, + {"let identity = fn(x: int8) -> int8 { x; }; identity(5);", 5}, + {"let identity = fn(x: int16) -> int16 { return x; }; identity(5);", 5}, + {"let double = fn(x: int32) -> int32 { x * 2; }; double(5);", 10}, + {"let add = fn(x: int64, y: int32) -> int64 { x + y; }; add(5, 5);", 10}, + {"let add = fn(x: int64, y: int64) -> int64 { x + y; }; add(5 + 5, add(5, 5));", 20}, + {"fn(x: uint8)-> uint8 { x; }(5)", 5}, + {"a := 3; b := 4; let add = fn(x: uint32, y: uint32) -> uint32 { x + y; }; add(a, b);", 7}, } for _, tt := range tests { - testIntegerObject(t, testEval(tt.input), tt.expected) + testIntegerObject(t, testEval(tt.input), big.NewInt(tt.expected)) } } diff --git a/evaluator/tests/function_object_test.go b/evaluator/tests/function_object_test.go index 751f024..7030721 100644 --- a/evaluator/tests/function_object_test.go +++ b/evaluator/tests/function_object_test.go @@ -7,7 +7,7 @@ import ( ) func TestFunctionObject(t *testing.T) { - input := "fn(x:int)->int { x + 2; };" + input := "fn(x:int64)->int64 { x + 2; };" evaluated := testEval(input) fn, ok := evaluated.(*object.Function) diff --git a/evaluator/tests/hash_literals_test.go b/evaluator/tests/hash_literals_test.go index 402f80f..497312b 100644 --- a/evaluator/tests/hash_literals_test.go +++ b/evaluator/tests/hash_literals_test.go @@ -1,6 +1,7 @@ package evaluator_tests import ( + "math/big" "testing" "github.com/0xM-D/interpreter/object" @@ -36,6 +37,6 @@ func TestHashLiterals(t *testing.T) { t.Errorf("no pair for given key in Pairs") } - testIntegerObject(t, pair.Value, expectedValue) + testIntegerObject(t, pair.Value, big.NewInt(expectedValue)) } } diff --git a/evaluator/tests/if_else_expression_test.go b/evaluator/tests/if_else_expression_test.go index 85149d0..ca1e073 100644 --- a/evaluator/tests/if_else_expression_test.go +++ b/evaluator/tests/if_else_expression_test.go @@ -1,26 +1,29 @@ package evaluator_tests -import "testing" +import ( + "math/big" + "testing" +) func TestIfElseExpressions(t *testing.T) { tests := []struct { input string expected interface{} }{ - {"if (true) { 10 }", 10}, + {"if (true) { 10 }", big.NewInt(10)}, {"if (false) { 10 }", nil}, - {"if (1) { 10 }", 10}, - {"if (1 < 2) { 10 }", 10}, + {"if (1) { 10 }", big.NewInt(10)}, + {"if (1 < 2) { 10 }", big.NewInt(10)}, {"if (1 > 2) { 10 }", nil}, - {"if (1 > 2) { 10 } else { 20 }", 20}, - {"if (1 < 2) { 10 } else { 20 }", 10}, + {"if (1 > 2) { 10 } else { 20 }", big.NewInt(20)}, + {"if (1 < 2) { 10 } else { 20 }", big.NewInt(10)}, } for _, tt := range tests { evaluated := testEval(tt.input) - integer, ok := tt.expected.(int) + integer, ok := tt.expected.(*big.Int) if ok { - testIntegerObject(t, evaluated, int64(integer)) + testIntegerObject(t, evaluated, integer) } else { testNullObject(t, evaluated) } diff --git a/evaluator/tests/index_operator_exp_test.go b/evaluator/tests/index_operator_exp_test.go index 5d61891..0327b3d 100644 --- a/evaluator/tests/index_operator_exp_test.go +++ b/evaluator/tests/index_operator_exp_test.go @@ -1,6 +1,9 @@ package evaluator_tests -import "testing" +import ( + "math/big" + "testing" +) func TestArrayIndexExpressions(t *testing.T) { tests := []struct { @@ -9,35 +12,35 @@ func TestArrayIndexExpressions(t *testing.T) { }{ { "new []int{1, 2, 3}[0]", - 1, + big.NewInt(1), }, { "new []int{1, 2, 3}[1]", - 2, + big.NewInt(2), }, { "new []int{1, 2, 3}[2]", - 3, + big.NewInt(3), }, { "let i = 0; new []int{1}[i];", - 1, + big.NewInt(1), }, { "new []int{1, 2, 3}[1 + 1];", - 3, + big.NewInt(3), }, { "let myArray = new []int{1, 2, 3}; myArray[2];", - 3, + big.NewInt(3), }, { "let myArray = new []int{1, 2, 3}; myArray[0] + myArray[1] + myArray[2];", - 6, + big.NewInt(6), }, { "let myArray = new []int{1, 2, 3}; let i = myArray[0]; myArray[i]", - 2, + big.NewInt(2), }, { "new []int{1, 2, 3}[3]", @@ -49,15 +52,15 @@ func TestArrayIndexExpressions(t *testing.T) { }, { "let a = new []int{1}; a[0] = 2; a[0]", - 2, + big.NewInt(2), }, } for _, tt := range tests { evaluated := testEval(tt.input) - integer, ok := tt.expected.(int) + integer, ok := tt.expected.(*big.Int) if ok { - testIntegerObject(t, evaluated, int64(integer)) + testIntegerObject(t, evaluated, integer) } else { testNullObject(t, evaluated) } @@ -71,7 +74,7 @@ func TestHashIndexExpressions(t *testing.T) { }{ { `new map{string->int}{"foo": 5}["foo"]`, - 5, + big.NewInt(5), }, { `new map{string->int}{"foo": 5}["bar"]`, @@ -79,7 +82,7 @@ func TestHashIndexExpressions(t *testing.T) { }, { `let key = "foo"; new map{string->int}{"foo": 5}[key]`, - 5, + big.NewInt(5), }, { `new map{string->int}{}["foo"]`, @@ -87,36 +90,36 @@ func TestHashIndexExpressions(t *testing.T) { }, { `new map{int->int}{5: 5}[5]`, - 5, + big.NewInt(5), }, { `new map{bool->int}{true: 5}[true]`, - 5, + big.NewInt(5), }, { `new map{vool->int}{false: 5}[false]`, - 5, + big.NewInt(5), }, { `new map{bool->int}{false: 5}[false]`, - 5, + big.NewInt(5), }, { "let a = new map{string->int}{1: 2}; a[1] = 3; a[1]", - 3, + big.NewInt(3), }, { "let a = new map{string->int}{1: 2}; b := 1; a[b] = b; a[1]", - 1, + big.NewInt(1), }, } for _, tt := range tests { evaluated := testEval(tt.input) - integer, ok := tt.expected.(int) + integer, ok := tt.expected.(*big.Int) if ok { - testIntegerObject(t, evaluated, int64(integer)) + testIntegerObject(t, evaluated, integer) } else { testNullObject(t, evaluated) } diff --git a/evaluator/tests/let_statement_test.go b/evaluator/tests/let_statement_test.go index d7cfe25..6ab342f 100644 --- a/evaluator/tests/let_statement_test.go +++ b/evaluator/tests/let_statement_test.go @@ -1,16 +1,19 @@ package evaluator_tests -import "testing" +import ( + "math/big" + "testing" +) func TestLetStatements(t *testing.T) { tests := []struct { input string - expected int64 + expected *big.Int }{ - {"let a = 5; a;", 5}, - {"let a = 5 * 5; a;", 25}, - {"let a = 5; let b = a; b;", 5}, - {"let a = 5; let b = a; let c = a + b + 5; c;", 15}, + {"let a = 5; a;", big.NewInt(5)}, + {"let a = 5 * 5; a;", big.NewInt(25)}, + {"let a = 5; let b = a; b;", big.NewInt(5)}, + {"let a = 5; let b = a; let c = a + b + 5; c;", big.NewInt(15)}, } for _, tt := range tests { testIntegerObject(t, testEval(tt.input), tt.expected) diff --git a/evaluator/tests/return_statement_test.go b/evaluator/tests/return_statement_test.go index 8682f98..c75ae42 100644 --- a/evaluator/tests/return_statement_test.go +++ b/evaluator/tests/return_statement_test.go @@ -1,22 +1,25 @@ package evaluator_tests -import "testing" +import ( + "math/big" + "testing" +) func TestReturnStatements(t *testing.T) { tests := []struct { input string - expected int64 + expected *big.Int }{ - {"return 10;", 10}, - {"return 10; 9;", 10}, - {"return 2 * 5; 9;", 10}, - {"9; return 2 * 5; 9;", 10}, + {"return 10;", big.NewInt(10)}, + {"return 10; 9;", big.NewInt(10)}, + {"return 2 * 5; 9;", big.NewInt(10)}, + {"9; return 2 * 5; 9;", big.NewInt(10)}, {` if (10 > 1) { if (10 > 1) { return 10; } return 1; - }`, 10}, + }`, big.NewInt(10)}, } for _, tt := range tests { evaluated := testEval(tt.input) diff --git a/evaluator/tests/type_builtins_test.go b/evaluator/tests/type_builtins_test.go index 19026ec..f4baf03 100644 --- a/evaluator/tests/type_builtins_test.go +++ b/evaluator/tests/type_builtins_test.go @@ -1,37 +1,40 @@ package evaluator_tests -import "testing" +import ( + "math/big" + "testing" +) func TestTypeBuiltins(t *testing.T) { tests := []struct { input string expected interface{} }{ - {"let arr = new []int{1, 2, 3}; arr.size();", 3}, - {"let arr = new []int{}; arr.size();", 0}, - {"new []int{1, 2, 3, 4, 5}.size();", 5}, - {`str := "abcdef"; str.length();`, 6}, - {`const string str = ""; str.length();`, 0}, - {`"bleh".length()`, 4}, + {"let arr = new []int{1, 2, 3}; arr.size();", big.NewInt(3)}, + {"let arr = new []int{}; arr.size();", big.NewInt(0)}, + {"new []int{1, 2, 3, 4, 5}.size();", big.NewInt(5)}, + {`str := "abcdef"; str.length();`, big.NewInt(6)}, + {`const string str = ""; str.length();`, big.NewInt(0)}, + {`"bleh".length()`, big.NewInt(4)}, {"1.toString()", "1"}, {"(123*456).toString()", "56088"}, - {"let arr = new []int{1, 2, 3}; arr.push(4).size()", 4}, - {"new []int{}.push(1).size();", 1}, - {"new []int{1, 2, 3}.delete(1, 5).size();", 1}, - {`new []int{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10"}.delete(1, 3);`, []string{"1", "5", "6", "7", "8", "9", "10"}}, - {`new []int{}.pushMultiple("0", 10)`, []string{"0", "0", "0", "0", "0", "0", "0", "0", "0", "0"}}, - {`new []int{1, 2, 3}.pushMultiple(0, 10).size()`, 13}, - {`new []int{1, 2, 3}.slice(0, 3).size()`, 3}, - {`new []int{1, 2, 3}.slice(0, 2).size()`, 2}, - {`new []int{1, 2, 3}.slice(1, 2).size()`, 2}, - {`new []int{1, 2, 3}.slice(1, 100).size()`, 2}, - {`new []int{1, 2, 3}.slice(2, 1).size()`, 1}, - {`new []int{1, 2, 3}.slice(2, 0).size()`, 0}, + {"let arr = new []int{1, 2, 3}; arr.push(4).size()", big.NewInt(4)}, + {"new []int{}.push(1).size();", big.NewInt(1)}, + {"new []int{1, 2, 3}.delete(1, 5).size();", big.NewInt(1)}, + {`new []string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10"}.delete(1, 3);`, []string{"1", "5", "6", "7", "8", "9", "10"}}, + {`new []string{}.pushMultiple("0", 10)`, []string{"0", "0", "0", "0", "0", "0", "0", "0", "0", "0"}}, + {`new []int{1, 2, 3}.pushMultiple(0, 10).size()`, big.NewInt(13)}, + {`new []int{1, 2, 3}.slice(0, 3).size()`, big.NewInt(3)}, + {`new []int{1, 2, 3}.slice(0, 2).size()`, big.NewInt(2)}, + {`new []int{1, 2, 3}.slice(1, 2).size()`, big.NewInt(2)}, + {`new []int{1, 2, 3}.slice(1, 100).size()`, big.NewInt(2)}, + {`new []int{1, 2, 3}.slice(2, 1).size()`, big.NewInt(1)}, + {`new []int{1, 2, 3}.slice(2, 0).size()`, big.NewInt(0)}, } for _, tt := range tests { switch expected := tt.expected.(type) { - case int: - testIntegerObject(t, testEval(tt.input), int64(expected)) + case *big.Int: + testIntegerObject(t, testEval(tt.input), expected) case string: testStringObject(t, testEval(tt.input), expected) case []string: diff --git a/evaluator/tests/typed_declaration_statement_test.go b/evaluator/tests/typed_declaration_statement_test.go index fde2294..26abac3 100644 --- a/evaluator/tests/typed_declaration_statement_test.go +++ b/evaluator/tests/typed_declaration_statement_test.go @@ -1,6 +1,7 @@ package evaluator_tests import ( + "math/big" "testing" "github.com/0xM-D/interpreter/object" @@ -11,17 +12,17 @@ func TestTypedDeclarationStatement(t *testing.T) { input string expected interface{} }{ - {"int a = 5; a;", 5}, + {"int64 a = 5; a;", big.NewInt(5)}, {"string a = \"testmmm\"; a;", "testmmm"}, {"const []int a = new []int{1, 2, 3, 4}; let b = a; b;", []string{"1", "2", "3", "4"}}, {"bool a = true; let b = !a; b;", false}, - {"const function(int, int)->int sum = fn(a: int, b: int) -> int { return a + b; }; sum", ExpectedFunction{ + {"const function(int64, int64)->int64 sum = fn(a: int64, b: int64) -> int64 { return a + b; }; sum", ExpectedFunction{ "fn(a, b) {" + "\n" + "return (a + b);" + "\n" + "}", object.FunctionObjectType{ - ParameterTypes: []object.ObjectType{object.IntegerKind, object.IntegerKind}, - ReturnValueType: object.IntegerKind, + ParameterTypes: []object.ObjectType{object.Int64Kind, object.Int64Kind}, + ReturnValueType: object.Int64Kind, }, }}, {"function()->void sum = fn() -> void {}; sum", ExpectedFunction{ @@ -34,9 +35,7 @@ func TestTypedDeclarationStatement(t *testing.T) { } for _, tt := range tests { switch expected := tt.expected.(type) { - case int: - testIntegerObject(t, testEval(tt.input), int64(expected)) - case int64: + case *big.Int: testIntegerObject(t, testEval(tt.input), expected) case string: testStringObject(t, testEval(tt.input), expected) diff --git a/evaluator/tests/util.go b/evaluator/tests/util.go index 4b73c32..176a95b 100644 --- a/evaluator/tests/util.go +++ b/evaluator/tests/util.go @@ -1,6 +1,7 @@ package evaluator_tests import ( + "math/big" "testing" "github.com/0xM-D/interpreter/evaluator" @@ -25,17 +26,17 @@ func testNullObject(t *testing.T, obj object.Object) bool { return true } -func testIntegerObject(t *testing.T, obj object.Object, expected int64) bool { +func testIntegerObject(t *testing.T, obj object.Object, expected *big.Int) bool { if !object.IsInteger(obj) { t.Errorf("object is not Integer. got=%T (%+v)", obj, obj) return false } - result := object.UnwrapReferenceObject(obj).(*object.Number[int64]) + resultString := obj.Inspect() - if result.Value != expected { - t.Errorf("object has wrong value. got=%d, want=%d", - result.Value, expected) + if resultString != expected.String() { + t.Errorf("object has wrong value. got=%s, want=%s", + resultString, expected.String()) return false } return true @@ -47,11 +48,11 @@ func testFloat32Object(t *testing.T, obj object.Object, expected float32) bool { return false } - result := object.UnwrapReferenceObject(obj).(*object.Number[float32]) + result := object.UnwrapReferenceObject(obj).(*object.Number).GetFloat32() - if result.Value != expected { + if result != expected { t.Errorf("object has wrong value. got=%f, want=%f", - result.Value, expected) + result, expected) return false } return true @@ -63,11 +64,11 @@ func testFloat64Object(t *testing.T, obj object.Object, expected float64) bool { return false } - result := object.UnwrapReferenceObject(obj).(*object.Number[float64]) + result := object.UnwrapReferenceObject(obj).(*object.Number).GetFloat64() - if result.Value != expected { + if result != expected { t.Errorf("object has wrong value. got=%f, want=%f", - result.Value, expected) + result, expected) return false } return true @@ -75,9 +76,7 @@ func testFloat64Object(t *testing.T, obj object.Object, expected float64) bool { func testNumber(t *testing.T, obj object.Object, expected interface{}) bool { switch v := expected.(type) { - case int: - return testIntegerObject(t, obj, int64(v)) - case int64: + case *big.Int: return testIntegerObject(t, obj, v) case float32: return testFloat32Object(t, obj, v) @@ -193,9 +192,7 @@ func testBooleanObject(t *testing.T, obj object.Object, expected bool) bool { func testLiteralObject(t *testing.T, obj object.Object, expected interface{}) { switch expected := expected.(type) { - case int: - testIntegerObject(t, obj, int64(expected)) - case int64: + case *big.Int: testIntegerObject(t, obj, expected) case string: testStringObject(t, obj, expected) diff --git a/evaluator/type_cast.go b/evaluator/type_cast.go index d4d89c0..af71147 100644 --- a/evaluator/type_cast.go +++ b/evaluator/type_cast.go @@ -1,7 +1,8 @@ package evaluator import ( - "strconv" + "fmt" + "math" "github.com/0xM-D/interpreter/object" ) @@ -11,96 +12,152 @@ const ( EXPLICIT_CAST = false ) -type CastRuleSignature struct { - from string - to string -} +const ( + _ uint8 = iota + INT8_WEIGHT + UINT8_WEIGHT + INT16_WEIGHT + UINT16_WEIGHT + INT32_WEIGHT + UINT32_WEIGHT + INT64_WEIGHT + UINT64_WEIGHT + FLOAT32_WEIGHT + FLOAT64_WEIGHT +) -type CastRule struct { - allowImplicit bool - cast func(object.Object) object.Object +var numberCastWeight = map[object.ObjectKind]uint8{ + object.Int8Kind: INT8_WEIGHT, + object.Int16Kind: INT16_WEIGHT, + object.Int32Kind: INT32_WEIGHT, + object.Int64Kind: INT64_WEIGHT, + object.UInt8Kind: UINT8_WEIGHT, + object.UInt16Kind: UINT16_WEIGHT, + object.UInt32Kind: UINT32_WEIGHT, + object.UInt64Kind: UINT64_WEIGHT, + object.Float32Kind: FLOAT32_WEIGHT, + object.Float64Kind: FLOAT64_WEIGHT, } -var castRules = map[CastRuleSignature]CastRule{ - {"int", "string"}: {true, intToString}, - {"int", "float32"}: {true, numberCast[int64, float32]}, - {"int", "float64"}: {true, numberCast[int64, float64]}, - {"float32", "float64"}: {true, numberCast[float32, float64]}, - {"{string -> string}", "{int -> string}"}: {true, castEmptyMap}, - {"int[]", "string[]"}: {true, castEmptyArray}, - {"any[]", "int[]"}: {true, castEmptyArray}, +var isIntegerKindUnsiged = map[object.ObjectKind]bool{ + object.Int8Kind: false, + object.Int16Kind: false, + object.Int32Kind: false, + object.Int64Kind: false, + object.UInt8Kind: true, + object.UInt16Kind: true, + object.UInt32Kind: true, + object.UInt64Kind: true, } -func typeCast(obj object.Object, targetType object.ObjectType, implicit bool) object.Object { - fromSignature := obj.Type().Signature() - toSignature := targetType.Signature() - castRuleSignature := CastRuleSignature{fromSignature, toSignature} - - castRule, castRuleExists := castRules[castRuleSignature] - if !castRuleExists { - return newError("No rule to cast between %s and %s is defined", fromSignature, toSignature) +func typeCast(obj object.Object, targetType object.ObjectType, castType bool) object.Object { + if obj.Type().Signature() == targetType.Signature() { + return obj } - if implicit && !castRule.allowImplicit { - return newError("Implicit cast not allowed between %s and %s", fromSignature, toSignature) + if object.IsNumber(obj) && object.IsNumberKind(targetType.Kind()) { + casted, err := numberCast(obj.(*object.Number), targetType.Kind(), castType) + if err != nil { + return newError(err.Error()) + } + return casted } - return castRule.cast(obj) -} + if object.IsNumber(obj) && targetType.Kind() == object.StringKind { + return &object.String{Value: obj.Inspect()} + } -func intToString(obj object.Object) object.Object { - integer := object.UnwrapReferenceObject(obj).(*object.Number[int64]) - return &object.String{Value: strconv.FormatInt(integer.Value, 10)} + return newError("Type cast from %s to %s is not defined", obj.Type().Signature(), targetType.Signature()) } -func numberCast[F int64 | float32 | float64, T int64 | float32 | float64](obj object.Object) object.Object { - val := object.UnwrapReferenceObject(obj).(*object.Number[F]) - return &object.Number[T]{Value: T(val.Value)} -} +func numberCast(number *object.Number, target object.ObjectKind, castType bool) (*object.Number, error) { -func castEmptyMap(obj object.Object) object.Object { - hash := object.UnwrapReferenceObject(obj).(*object.Hash) - if len(hash.Pairs) != 0 { - return newError("Can't cast non empty hash") + if number.Kind == target { + return number, nil } - return &object.Hash{Pairs: hash.Pairs, HashObjectType: object.HashObjectType{KeyType: object.IntegerKind, ValueType: object.IntegerKind}} -} -func castEmptyArray(obj object.Object) object.Object { - array := object.UnwrapReferenceObject(obj).(*object.Array) - if len(array.Elements) != 0 { - return newError("Can't cast non empty array") + numberWeight := numberCastWeight[number.Type().Kind()] + targetWeight := numberCastWeight[target] + + if numberWeight > targetWeight && castType == IMPLICIT_CAST { + return nil, fmt.Errorf("Cannot implicitly cast %s into %s", number.Type().Kind(), target.Kind()) } - return &object.Array{Elements: array.Elements, ArrayObjectType: object.ArrayObjectType{ElementType: object.StringKind}} -} + var value uint64 + + if object.IsInteger(number) && !object.IsIntegerKind(target) { // Casting from int to float + if number.IsSigned() && target.Kind() == object.Float64Kind { + value = math.Float64bits(float64(number.GetInt64())) + } else if number.IsSigned() && target.Kind() == object.Float32Kind { + value = uint64(math.Float32bits(float32(number.GetInt64()))) + } else if number.IsUnsigned() && target.Kind() == object.Float64Kind { + value = math.Float64bits(float64(number.GetUInt64())) + } else if number.IsUnsigned() && target.Kind() == object.Float32Kind { + value = uint64(math.Float32bits(float32(number.GetUInt64()))) + } + + } else if object.IsFloat(number) && object.IsIntegerKind(target) { // casting from float to int + + if object.IS_SIGNED[target] && object.IsFloat32(number) { + value = uint64(int64(number.GetFloat32())) + } else if object.IS_SIGNED[target] && object.IsFloat64(number) { + value = uint64(int64(number.GetFloat64())) + } else if !object.IS_SIGNED[target] && object.IsFloat32(number) { + value = uint64(number.GetFloat32()) + } else if !object.IS_SIGNED[target] && object.IsFloat64(number) { + value = uint64(number.GetFloat64()) + } + + } else if object.IsFloat(number) && (target == object.Float32Kind || target == object.Float64Kind) { // casting from float to float + if number.Type() == object.Float32Kind && target == object.Float64Kind { + value = math.Float64bits(float64(number.GetFloat32())) + } else if number.Type() == object.Float64Kind && target == object.Float32Kind { + value = uint64(math.Float32bits(float32(number.GetFloat64()))) + } + } else { // casting from int to int + switch target { + case object.Int8Kind: + value = object.Int64Bits(int64(int8(number.GetInt64()))) + case object.Int16Kind: + value = object.Int64Bits(int64(int16(number.GetInt64()))) + case object.Int32Kind: + value = object.Int64Bits(int64(int32(number.GetInt64()))) + case object.Int64Kind: + value = object.Int64Bits(number.GetInt64()) + case object.UInt8Kind: + value = uint64(uint8(number.GetInt64())) + case object.UInt16Kind: + value = uint64(uint16(number.GetInt64())) + case object.UInt32Kind: + value = uint64(uint32(number.GetInt64())) + case object.UInt64Kind: + value = uint64(uint64(number.GetInt64())) + } -const ( - _ uint8 = iota - INT_WEIGHT - FLOAT32_WEIGHT - FLOAT64_WEIGHT -) + } -var numberCastWeight = map[object.ObjectKind]uint8{ - object.IntegerKind: INT_WEIGHT, - object.Float32Kind: FLOAT32_WEIGHT, - object.Float64Kind: FLOAT64_WEIGHT, + return &object.Number{Value: value, Kind: target}, nil } -func castToLargerNumberType(num1 object.Object, num2 object.Object) (object.Object, object.Object, object.ObjectKind) { - num1 = object.UnwrapReferenceObject(num1) - num2 = object.UnwrapReferenceObject(num2) - - k1 := num1.Type().Kind() - k2 := num2.Type().Kind() - w1 := numberCastWeight[k1] - w2 := numberCastWeight[k2] +func arithmeticCast(first, second *object.Number) (*object.Number, *object.Number, error) { - if w1 == w2 { - return num1, num2, k1 + if first.Type().Kind() == second.Type().Kind() { + return first, second, nil } - if w1 > w2 { - return num1, typeCast(num2, k1, IMPLICIT_CAST), k1 + + firstWeight := numberCastWeight[first.Type().Kind()] + secondWeight := numberCastWeight[second.Type().Kind()] + + if firstWeight < secondWeight { + castedFirst, err := numberCast(first, second.Kind, true) + if err != nil { + return nil, nil, err + } + return castedFirst, second, nil + } else { + castedSecond, err := numberCast(second, first.Kind, true) + if err != nil { + return nil, nil, err + } + return first, castedSecond, nil } - return typeCast(num1, k2, IMPLICIT_CAST), num2, k2 } diff --git a/evaluator/util.go b/evaluator/util.go index f2d79c3..a25c67a 100644 --- a/evaluator/util.go +++ b/evaluator/util.go @@ -2,6 +2,8 @@ package evaluator import ( "fmt" + "math" + "math/big" "github.com/0xM-D/interpreter/ast" "github.com/0xM-D/interpreter/object" @@ -35,27 +37,35 @@ func evalBangOperatorExpression(right object.Object) object.Object { } func evalMinusPrefixOperatorExpression(right object.Object) object.Object { - switch right.Type() { - case object.IntegerKind: - return &object.Number[int64]{Value: -right.(*object.Number[int64]).Value} - case object.Float32Kind: - return &object.Number[float32]{Value: -right.(*object.Number[float32]).Value} - case object.Float64Kind: - return &object.Number[float64]{Value: -right.(*object.Number[float64]).Value} - default: - return newError("unknown operator: -%s", right.Type().Signature()) + if !object.IsNumber(right) { + return newError("Operator - not defined on type %s", right.Type().Signature()) + } + + number := right.(*object.Number) + if object.IsInteger(number) && number.IsUnsigned() { + return newError("Operator - not defined on unsigned integer type %s", number.Kind.Signature()) + } + if object.IsInteger(number) && number.IsSigned() { + return &object.Number{Value: object.Int64Bits(-number.GetInt64()), Kind: number.Kind} + } + if number.Kind == object.Float32Kind { + return &object.Number{Value: uint64(math.Float32bits(-number.GetFloat32())), Kind: number.Kind} + } + if number.Kind == object.Float64Kind { + return &object.Number{Value: math.Float64bits(-number.GetFloat64()), Kind: number.Kind} } + + return newError("Operator - not defined on number type %s", right.Type().Signature()) } func evalTildePrefixOperatorExpression(right object.Object) object.Object { - switch right.Type() { - case object.IntegerKind: - return &object.Number[int64]{Value: ^right.(*object.Number[int64]).Value} - default: - return newError("unknown operator: -%s", right.Type().Signature()) - + if !object.IsNumber(right) { + return newError("Operator ~ not defined on type %s", right.Type().Signature()) } + + number := right.(*object.Number) + return &object.Number{Value: ^number.Value, Kind: number.Kind} } func evalIfExpression(ie *ast.IfExpression, env *object.Environment) object.Object { @@ -184,8 +194,11 @@ func evalNewHashExpression(exp *ast.NewExpression, env *object.Environment) obje } func evalAccessExpression(left object.Object, right string, env *object.Environment) object.Object { - member := left.Type().Builtins().Get(right) - + repo := left.Type().Builtins() + var member *object.BuiltinFunction + if repo != nil { + member = left.Type().Builtins().Get(right) + } if member == nil { return newError("Member %s does not exist on %s", right, left.Type().Signature()) } @@ -210,7 +223,7 @@ func evalIndexExpression(left, index object.Object) object.Object { func evalArrayIndexExpression(array, index object.Object) object.Object { arrayObject := array.(*object.Array) - idx := index.(*object.Number[int64]).Value + idx := typeCast(index, object.Int64Kind, true).(*object.Number).GetInt64() max := int64(len(arrayObject.Elements) - 1) if idx < 0 || idx > max { @@ -236,25 +249,6 @@ func evalHashIndexExpression(hash, index object.Object) object.Object { return &object.HashElementReference{Hash: hashObject, Key: index} } -func evalDeclarationStatement(node *ast.DeclarationStatement, env *object.Environment) object.Object { - var objectType object.ObjectType - var err error - - if node.Type != nil { - objectType, err = evalType(node.Type, env) - if err != nil { - return newError(err.Error()) - } - } - - declared := declareVariable(node, objectType, env) - if declared != nil { - return declared - } - - return nil -} - func evalType(typeNode ast.Type, env *object.Environment) (object.ObjectType, error) { switch casted := typeNode.(type) { case ast.HashType: @@ -299,15 +293,30 @@ func evalType(typeNode ast.Type, env *object.Environment) (object.ObjectType, er return nil, fmt.Errorf("Unknown type: %s", typeNode.String()) } -func declareVariable(declNode *ast.DeclarationStatement, expectedType object.ObjectType, env *object.Environment) object.Object { +func evalDeclarationStatement(declNode *ast.DeclarationStatement, env *object.Environment) object.Object { val := object.UnwrapReferenceObject(Eval(declNode.Value, env)) + var expectedType object.ObjectType + + if declNode.Type != nil { + var err error + expectedType, err = evalType(declNode.Type, env) + + if err != nil { + return newError(err.Error()) + } + + } if object.IsError(val) { return val } + if object.IsNumber(val) && expectedType == nil { + expectedType = object.Int64Kind + } + if expectedType != nil { - cast := typeCast(val, expectedType, IMPLICIT_CAST) + cast := typeCast(val, expectedType, EXPLICIT_CAST) if !object.IsError(cast) { val = cast } @@ -410,6 +419,42 @@ func evalTernaryExpression(node *ast.TernaryExpression, env *object.Environment) return result } +func evalIntegerLiteral(node *ast.IntegerLiteral, env *object.Environment) object.Object { + kind, err := getMinimumIntegerType(&node.Value) + if err != nil { + return newError(err.Error()) + } + + if !object.IS_SIGNED[kind] { + return &object.Number{Value: node.Value.Uint64(), Kind: object.UInt64Kind} + } + + return &object.Number{Value: object.Int64Bits(node.Value.Int64()), Kind: object.Int64Kind} +} + +func getMinimumIntegerType(number *big.Int) (object.ObjectKind, error) { + switch { + case number.Cmp(big.NewInt(math.MaxInt8)) == -1: + return object.Int8Kind, nil + case number.Cmp(big.NewInt(math.MaxUint8)) == -1: + return object.UInt8Kind, nil + case number.Cmp(big.NewInt(math.MaxInt16)) == -1: + return object.Int16Kind, nil + case number.Cmp(big.NewInt(math.MaxUint16)) == -1: + return object.UInt16Kind, nil + case number.Cmp(big.NewInt(math.MaxInt32)) == -1: + return object.Int32Kind, nil + case number.Cmp(big.NewInt(math.MaxUint32)) == -1: + return object.UInt32Kind, nil + case number.Cmp(big.NewInt(math.MaxInt64)) == -1: + return object.Int64Kind, nil + case number.Cmp(new(big.Int).SetUint64(math.MaxUint64)) == -1: + return object.UInt64Kind, nil + default: + return object.ErrorKind, fmt.Errorf("Integer ouside maximum range") + } +} + func evalExplicitTypeCast(node *ast.TypeCastExpression, env *object.Environment) object.Object { left := Eval(node.Left, env) if object.IsError(left) { diff --git a/object/array.go b/object/array.go index 2ff6332..6feb207 100644 --- a/object/array.go +++ b/object/array.go @@ -9,7 +9,7 @@ type ArrayObjectType struct { ElementType ObjectType } -func (a *ArrayObjectType) Signature() string { return a.ElementType.Signature() + "[]" } +func (a *ArrayObjectType) Signature() string { return "[]" + a.ElementType.Signature() } func (a *ArrayObjectType) Kind() ObjectKind { return ArrayKind } func (a *ArrayObjectType) Builtins() *FunctionRepository { return ArrayKind.Builtins() } func (a *ArrayObjectType) IsConstant() bool { return false } diff --git a/object/constants.go b/object/constants.go index 1737bc7..6176b34 100644 --- a/object/constants.go +++ b/object/constants.go @@ -1,9 +1,5 @@ package object -import ( - "strconv" -) - type ObjectKind string var intrinsicTypeFunctionRepositories = initIntrinsicTypeBuiltins() @@ -15,7 +11,14 @@ func (o ObjectKind) IsConstant() bool { return true } const ( Invalid ObjectKind = "invalid" - IntegerKind ObjectKind = "int" + Int8Kind ObjectKind = "int8" + Int16Kind ObjectKind = "int16" + Int32Kind ObjectKind = "int32" + Int64Kind ObjectKind = "int64" + UInt8Kind ObjectKind = "uint8" + UInt16Kind ObjectKind = "uint16" + UInt32Kind ObjectKind = "uint32" + UInt64Kind ObjectKind = "uint64" Float32Kind ObjectKind = "float32" Float64Kind ObjectKind = "float64" BooleanKind ObjectKind = "bool" @@ -33,17 +36,20 @@ const ( func initIntrinsicTypeBuiltins() map[ObjectKind]*FunctionRepository { repos := map[ObjectKind]*FunctionRepository{} - repos[IntegerKind] = initIntegerBuiltins() + numberBuiltins := initNumberBuiltins() + for _, nt := range NumberTypes { + repos[nt] = numberBuiltins + } repos[ArrayKind] = initArrayBuiltins() repos[StringKind] = initStringBuiltins() return repos } -func initIntegerBuiltins() *FunctionRepository { +func initNumberBuiltins() *FunctionRepository { repo := FunctionRepository{Functions: map[string]*BuiltinFunction{}} - repo.register("toString", FunctionObjectType{ParameterTypes: []ObjectType{}, ReturnValueType: StringKind}, intToString) + repo.register("toString", FunctionObjectType{ParameterTypes: []ObjectType{}, ReturnValueType: StringKind}, numberToString) return &repo } @@ -51,7 +57,7 @@ func initIntegerBuiltins() *FunctionRepository { func initStringBuiltins() *FunctionRepository { repo := FunctionRepository{Functions: map[string]*BuiltinFunction{}} - repo.register("length", FunctionObjectType{ParameterTypes: []ObjectType{}, ReturnValueType: IntegerKind}, stringLength) + repo.register("length", FunctionObjectType{ParameterTypes: []ObjectType{}, ReturnValueType: Int64Kind}, stringLength) return &repo } @@ -59,27 +65,27 @@ func initStringBuiltins() *FunctionRepository { func initArrayBuiltins() *FunctionRepository { repo := FunctionRepository{Functions: map[string]*BuiltinFunction{}} - repo.register("size", FunctionObjectType{ParameterTypes: []ObjectType{}, ReturnValueType: IntegerKind}, arraySize) - repo.register("push", FunctionObjectType{ParameterTypes: []ObjectType{IntegerKind}, ReturnValueType: ArrayKind}, arrayPush) - repo.register("pushMultiple", FunctionObjectType{ParameterTypes: []ObjectType{AnyKind, IntegerKind}, ReturnValueType: ArrayKind}, arrayPushMultiple) - repo.register("delete", FunctionObjectType{ParameterTypes: []ObjectType{IntegerKind, IntegerKind}, ReturnValueType: ArrayKind}, arrayDelete) - repo.register("slice", FunctionObjectType{ParameterTypes: []ObjectType{IntegerKind, IntegerKind}, ReturnValueType: ArrayKind}, arraySlice) + repo.register("size", FunctionObjectType{ParameterTypes: []ObjectType{}, ReturnValueType: Int64Kind}, arraySize) + repo.register("push", FunctionObjectType{ParameterTypes: []ObjectType{Int64Kind}, ReturnValueType: ArrayKind}, arrayPush) + repo.register("pushMultiple", FunctionObjectType{ParameterTypes: []ObjectType{AnyKind, Int64Kind}, ReturnValueType: ArrayKind}, arrayPushMultiple) + repo.register("delete", FunctionObjectType{ParameterTypes: []ObjectType{Int64Kind, Int64Kind}, ReturnValueType: ArrayKind}, arrayDelete) + repo.register("slice", FunctionObjectType{ParameterTypes: []ObjectType{Int64Kind, Int64Kind}, ReturnValueType: ArrayKind}, arraySlice) return &repo } -func intToString(params ...Object) Object { - integer := params[0].(*Number[int64]) - return &String{strconv.FormatInt(integer.Value, 10)} +func numberToString(params ...Object) Object { + number := params[0].(*Number) + return &String{number.Inspect()} } func stringLength(params ...Object) Object { str := params[0].(*String) - return &Number[int64]{int64(len(str.Value))} + return &Number{Value: uint64(len(str.Value)), Kind: UInt64Kind} } func arraySize(params ...Object) Object { arr := params[0].(*Array) - return &Number[int64]{int64(len(arr.Elements))} + return &Number{Value: uint64(len(arr.Elements)), Kind: UInt64Kind} } func arrayPush(params ...Object) Object { @@ -92,8 +98,8 @@ func arrayPush(params ...Object) Object { func arrayDelete(params ...Object) Object { arr := params[0].(*Array) - startIndex := UnwrapReferenceObject(params[1]).(*Number[int64]).Value - count := UnwrapReferenceObject(params[2]).(*Number[int64]).Value + startIndex := UnwrapReferenceObject(params[1]).(*Number).GetInt64() + count := UnwrapReferenceObject(params[2]).(*Number).GetInt64() arrLen := int64(len(arr.Elements)) if startIndex+count >= arrLen { @@ -112,10 +118,10 @@ func arrayDelete(params ...Object) Object { func arrayPushMultiple(params ...Object) Object { arr := params[0].(*Array) element := UnwrapReferenceObject(params[1]) - size := int(UnwrapReferenceObject(params[2]).(*Number[int64]).Value) + size := UnwrapReferenceObject(params[2]).(*Number).GetInt64() newElements := make([]Object, 0, size) - for i := 0; i != size; i++ { + for i := int64(0); i != size; i++ { newElements = append(newElements, element) } @@ -126,8 +132,8 @@ func arrayPushMultiple(params ...Object) Object { func arraySlice(params ...Object) Object { arr := params[0].(*Array) - startIndex := int(UnwrapReferenceObject(params[1]).(*Number[int64]).Value) - count := int(UnwrapReferenceObject(params[2]).(*Number[int64]).Value) + startIndex := UnwrapReferenceObject(params[1]).(*Number).GetInt64() + count := UnwrapReferenceObject(params[2]).(*Number).GetInt64() startIndex = boundArrayIndex(arr, startIndex) endIndex := boundArrayIndex(arr, startIndex+count-1) @@ -135,13 +141,13 @@ func arraySlice(params ...Object) Object { return &Array{ArrayObjectType: arr.ArrayObjectType, Elements: arr.Elements[startIndex : endIndex+1]} } -func boundArrayIndex(arr *Array, index int) int { +func boundArrayIndex(arr *Array, index int64) int64 { if index < 0 { index = 0 } - if index >= len(arr.Elements) { - index = len(arr.Elements) - 1 + if index >= int64(len(arr.Elements)) { + index = int64(len(arr.Elements)) - 1 } return index diff --git a/object/environment.go b/object/environment.go index cb2f232..95c74db 100644 --- a/object/environment.go +++ b/object/environment.go @@ -13,14 +13,23 @@ type Environment struct { outer *Environment } -var GLOBAL_TYPES = map[ObjectKind]bool{ - IntegerKind: true, - Float32Kind: true, - Float64Kind: true, - BooleanKind: true, - NullKind: true, - StringKind: true, - VoidKind: true, +var GLOBAL_TYPES = map[ObjectKind]ObjectKind{ + "char": Int8Kind, + "int": Int64Kind, + Int8Kind: Int8Kind, + Int16Kind: Int16Kind, + Int32Kind: Int32Kind, + Int64Kind: Int64Kind, + UInt8Kind: UInt8Kind, + UInt16Kind: UInt16Kind, + UInt32Kind: UInt32Kind, + UInt64Kind: Int64Kind, + Float32Kind: Float32Kind, + Float64Kind: Float64Kind, + BooleanKind: BooleanKind, + NullKind: NullKind, + StringKind: StringKind, + VoidKind: VoidKind, } func NewEnvironment() *Environment { @@ -52,15 +61,15 @@ func (e *Environment) Get(name string) Object { } func (e *Environment) GetObjectType(name string) (ObjectType, bool) { - _, exists := GLOBAL_TYPES[ObjectKind(name)] - if exists { - return ObjectKind(name), true + globalObjectType, globalObjectTypeExists := GLOBAL_TYPES[ObjectKind(name)] + if globalObjectTypeExists { + return globalObjectType, true } - obj, ok := e.typeStore[name] - if !ok && e.outer != nil { - obj, ok = e.outer.GetObjectType(name) + objectType, objectTypeExists := e.typeStore[name] + if !objectTypeExists && e.outer != nil { + objectType, objectTypeExists = e.outer.GetObjectType(name) } - return obj, ok + return objectType, objectTypeExists } func (e *Environment) Declare(name string, isConstant bool, val Object) ObjectReference { diff --git a/object/hash_key.go b/object/hash_key.go index 9dab932..b97fa13 100644 --- a/object/hash_key.go +++ b/object/hash_key.go @@ -2,27 +2,22 @@ package object import "hash/fnv" -type HashKey struct { - Type ObjectType - Value uint64 -} +type HashKey uint64 func (b *Boolean) HashKey() HashKey { - var value uint64 if b.Value { - value = 1 + return 1 } else { - value = 0 + return 0 } - return HashKey{Type: b.Type(), Value: value} } -func (i Number[int64]) HashKey() HashKey { - return HashKey{Type: i.Type(), Value: uint64(i.Value)} +func (i Number) HashKey() HashKey { + return HashKey(i.Value) } func (s *String) HashKey() HashKey { h := fnv.New64a() h.Write([]byte(s.Value)) - return HashKey{Type: s.Type(), Value: h.Sum64()} + return HashKey(h.Sum64()) } diff --git a/object/number.go b/object/number.go index a571812..696df7c 100644 --- a/object/number.go +++ b/object/number.go @@ -2,44 +2,86 @@ package object import ( "fmt" + "math" + "unsafe" ) -type Number[V int64 | float32 | float64] struct { - Value V +type Number struct { + Value uint64 + Kind ObjectKind } -func (i *Number[V]) Inspect() string { - switch val := any(i.Value).(type) { - case int64: - return fmt.Sprintf("%d", val) - case float32: - return fmt.Sprintf("%gf", val) - case float64: - return fmt.Sprintf("%g", val) +func (n *Number) Inspect() string { + if IsFloat32(n) { + return fmt.Sprintf("%gf", n.GetFloat32()) } - return "This should never ever be reached" + if IsFloat64(n) { + return fmt.Sprintf("%g", n.GetFloat64()) + } + if n.IsSigned() { + return fmt.Sprintf("%d", n.GetInt64()) + } + return fmt.Sprintf("%d", n.GetUInt64()) } -func (i *Number[V]) Type() ObjectType { - switch any(i.Value).(type) { - case int64: - return IntegerKind - case float32: - return Float32Kind - case float64: - return Float64Kind - } - return ErrorKind +func (n *Number) Type() ObjectType { + return n.Kind } -func NewNumberOfKind(kind ObjectKind) Object { - switch kind { - case IntegerKind: - return &Number[int64]{} - case Float32Kind: - return &Number[float32]{} - case Float64Kind: - return &Number[float64]{} - } - return &Error{Message: fmt.Sprintf("%s is not a number", kind)} +func (n *Number) GetUInt64() uint64 { + return n.Value +} + +func (n *Number) GetInt64() int64 { + return Int64FromBits(n.Value) +} + +func (n *Number) GetFloat32() float32 { + return math.Float32frombits(uint32(n.Value)) +} + +func (n *Number) GetFloat64() float64 { + return math.Float64frombits(n.Value) +} + +var IS_SIGNED = map[ObjectKind]bool{ + Int8Kind: true, + Int16Kind: true, + Int32Kind: true, + Int64Kind: true, + Float32Kind: true, + Float64Kind: true, + UInt8Kind: false, + UInt16Kind: false, + UInt32Kind: false, + UInt64Kind: false, +} + +func (n *Number) IsSigned() bool { + return IS_SIGNED[n.Kind] +} + +func (n *Number) IsUnsigned() bool { + return !IS_SIGNED[n.Kind] +} + +func Int64Bits(x int64) uint64 { + return *(*uint64)(unsafe.Pointer(&x)) +} + +func Int64FromBits(x uint64) int64 { + return *(*int64)(unsafe.Pointer(&x)) +} + +var NumberTypes = []ObjectKind{ + Int8Kind, + Int16Kind, + Int32Kind, + Int64Kind, + UInt8Kind, + UInt16Kind, + UInt32Kind, + UInt64Kind, + Float32Kind, + Float64Kind, } diff --git a/object/util.go b/object/util.go index 7d56bc1..eac74da 100644 --- a/object/util.go +++ b/object/util.go @@ -1,7 +1,18 @@ package object +var INTEGER_TYPES = map[ObjectKind]bool{ + Int8Kind: true, + Int16Kind: true, + Int32Kind: true, + Int64Kind: true, + UInt8Kind: true, + UInt16Kind: true, + UInt32Kind: true, + UInt64Kind: true, +} + func IsInteger(i Object) bool { - return i.Type().Kind() == IntegerKind + return IsIntegerKind(i.Type().Kind()) } func IsFloat32(i Object) bool { @@ -61,6 +72,15 @@ func TypesMatch(t1 ObjectType, t2 ObjectType) bool { return t1.Signature() == t2.Signature() } +func IsIntegerKind(k ObjectKind) bool { + _, isInteger := INTEGER_TYPES[k] + return isInteger +} + +func IsNumberKind(k ObjectKind) bool { + return k == Float32Kind || k == Float64Kind || IsIntegerKind(k) +} + func UnwrapReferenceObject(or Object) Object { object, ok := or.(ObjectReference) if ok { diff --git a/parser/parse_integer_literal.go b/parser/parse_integer_literal.go index 514486b..06c6057 100644 --- a/parser/parse_integer_literal.go +++ b/parser/parse_integer_literal.go @@ -1,7 +1,7 @@ package parser import ( - "strconv" + "math/big" "github.com/0xM-D/interpreter/ast" ) @@ -9,13 +9,13 @@ import ( func (p *Parser) parseIntegerLiteral() ast.Expression { lit := &ast.IntegerLiteral{Token: p.curToken} - value, err := strconv.ParseInt(p.curToken.Literal, 0, 64) - if err != nil { + value, ok := new(big.Int).SetString(p.curToken.Literal, 10) + if !ok { p.newError(lit, "could not parse %q as integer", p.curToken.Literal) return nil } - lit.Value = value + lit.Value = *value return lit } diff --git a/parser/tests/access_expression_test.go b/parser/tests/access_expression_test.go index a05c76d..f720280 100644 --- a/parser/tests/access_expression_test.go +++ b/parser/tests/access_expression_test.go @@ -1,6 +1,7 @@ package parser_tests import ( + "math/big" "testing" "github.com/0xM-D/interpreter/ast" @@ -14,9 +15,9 @@ func TestAccessExpressions(t *testing.T) { leftValue interface{} rightValue interface{} }{ - {"5.toString", 5, TestIdentifier{"toString"}}, + {"5.toString", big.NewInt(5), TestIdentifier{"toString"}}, {"array.size", TestIdentifier{"array"}, TestIdentifier{"size"}}, - {"new []int{1, 2, 3, 4}.size", []interface{}{1, 2, 3, 4}, TestIdentifier{"size"}}, + {"new []int{1, 2, 3, 4}.size", []interface{}{big.NewInt(1), big.NewInt(2), big.NewInt(3), big.NewInt(4)}, TestIdentifier{"size"}}, {"str.length", TestIdentifier{"str"}, TestIdentifier{"length"}}, {`"ABCDEFG".length`, "ABCDEFG", TestIdentifier{"length"}}, } diff --git a/parser/tests/call_expression_parsing_test.go b/parser/tests/call_expression_parsing_test.go index c2c127f..55a6929 100644 --- a/parser/tests/call_expression_parsing_test.go +++ b/parser/tests/call_expression_parsing_test.go @@ -1,6 +1,7 @@ package parser_tests import ( + "math/big" "testing" "github.com/0xM-D/interpreter/ast" @@ -34,7 +35,7 @@ func TestCallExpressionParsing(t *testing.T) { if len(exp.Arguments) != 3 { t.Fatalf("wrong length of arguments. got=%d", len(exp.Arguments)) } - testLiteralExpression(t, exp.Arguments[0], 1) - testInfixExpression(t, exp.Arguments[1], 2, "*", 3) - testInfixExpression(t, exp.Arguments[2], 4, "+", 5) + testLiteralExpression(t, exp.Arguments[0], big.NewInt(1)) + testInfixExpression(t, exp.Arguments[1], big.NewInt(2), "*", big.NewInt(3)) + testInfixExpression(t, exp.Arguments[2], big.NewInt(4), "+", big.NewInt(5)) } diff --git a/parser/tests/for_statement_test.go b/parser/tests/for_statement_test.go index 937cf5d..d9896ec 100644 --- a/parser/tests/for_statement_test.go +++ b/parser/tests/for_statement_test.go @@ -1,6 +1,7 @@ package parser_tests import ( + "math/big" "testing" "github.com/0xM-D/interpreter/ast" @@ -40,14 +41,14 @@ func TestForStatement(t *testing.T) { t.Fatalf("stmt.Condition is not ast.ExpressionStatement. got=%T", stmt.Condition) } - testInfixExpression(t, condition.Expression, TestIdentifier{"i"}, "<", 10) + testInfixExpression(t, condition.Expression, TestIdentifier{"i"}, "<", big.NewInt(10)) afterThought, ok := stmt.AfterThought.(*ast.ExpressionStatement) if !ok { t.Fatalf("stmt.AfterThoguht is not ast.ExpressionStatement. got=%T", stmt.AfterThought) } - testInfixExpression(t, afterThought.Expression, TestIdentifier{"i"}, "+=", 1) + testInfixExpression(t, afterThought.Expression, TestIdentifier{"i"}, "+=", big.NewInt(1)) if len(stmt.Body.Statements) != 1 { t.Fatalf("for loop body is not 1 statements. got=%d\n", diff --git a/parser/tests/hash_literal_test.go b/parser/tests/hash_literal_test.go index d2ad168..fcdd8bf 100644 --- a/parser/tests/hash_literal_test.go +++ b/parser/tests/hash_literal_test.go @@ -1,6 +1,7 @@ package parser_tests import ( + "math/big" "testing" "github.com/0xM-D/interpreter/ast" @@ -33,10 +34,10 @@ func TestParsingHashLiteralsStringKeys(t *testing.T) { t.Errorf("hash.InitializationList has wrong length. got=%d", len(hash.InitializationList)) } - expected := map[string]int64{ - "one": 1, - "two": 2, - "three": 3, + expected := map[string]*big.Int{ + "one": big.NewInt(1), + "two": big.NewInt(2), + "three": big.NewInt(3), } for _, expr := range hash.InitializationList { @@ -99,13 +100,13 @@ func TestParsingHashLiteralsWithExpressions(t *testing.T) { tests := map[string]func(ast.Expression){ "one": func(e ast.Expression) { - testInfixExpression(t, e, 0, "+", 1) + testInfixExpression(t, e, big.NewInt(0), "+", big.NewInt(1)) }, "two": func(e ast.Expression) { - testInfixExpression(t, e, 10, "-", 8) + testInfixExpression(t, e, big.NewInt(10), "-", big.NewInt(8)) }, "three": func(e ast.Expression) { - testInfixExpression(t, e, 15, "/", 5) + testInfixExpression(t, e, big.NewInt(15), "/", big.NewInt(5)) }, } diff --git a/parser/tests/index_expression_parsing_test.go b/parser/tests/index_expression_parsing_test.go index abc5ad9..75d5442 100644 --- a/parser/tests/index_expression_parsing_test.go +++ b/parser/tests/index_expression_parsing_test.go @@ -1,6 +1,7 @@ package parser_tests import ( + "math/big" "testing" "github.com/0xM-D/interpreter/ast" @@ -27,7 +28,7 @@ func TestIndexExpressionParsing(t *testing.T) { return } - if !testInfixExpression(t, indexExp.Index, 1, "+", 1) { + if !testInfixExpression(t, indexExp.Index, big.NewInt(1), "+", big.NewInt(1)) { return } diff --git a/parser/tests/infix_expression_test.go b/parser/tests/infix_expression_test.go index 6bc0502..0982953 100644 --- a/parser/tests/infix_expression_test.go +++ b/parser/tests/infix_expression_test.go @@ -1,6 +1,7 @@ package parser_tests import ( + "math/big" "testing" "github.com/0xM-D/interpreter/ast" @@ -15,14 +16,14 @@ func TestParsingInfixExpressions(t *testing.T) { operator string rightValue interface{} }{ - {"5 + 5;", 5, "+", 5}, - {"5 - 5;", 5, "-", 5}, - {"5 * 5;", 5, "*", 5}, - {"5 / 5;", 5, "/", 5}, - {"5 > 5;", 5, ">", 5}, - {"5 < 5;", 5, "<", 5}, - {"5 == 5;", 5, "==", 5}, - {"5 != 5;", 5, "!=", 5}, + {"5 + 5;", big.NewInt(5), "+", big.NewInt(5)}, + {"5 - 5;", big.NewInt(5), "-", big.NewInt(5)}, + {"5 * 5;", big.NewInt(5), "*", big.NewInt(5)}, + {"5 / 5;", big.NewInt(5), "/", big.NewInt(5)}, + {"5 > 5;", big.NewInt(5), ">", big.NewInt(5)}, + {"5 < 5;", big.NewInt(5), "<", big.NewInt(5)}, + {"5 == 5;", big.NewInt(5), "==", big.NewInt(5)}, + {"5 != 5;", big.NewInt(5), "!=", big.NewInt(5)}, {"foobar + barfoo;", TestIdentifier{"foobar"}, "+", TestIdentifier{"barfoo"}}, {"foobar - barfoo;", TestIdentifier{"foobar"}, "-", TestIdentifier{"barfoo"}}, {"foobar * barfoo;", TestIdentifier{"foobar"}, "*", TestIdentifier{"barfoo"}}, diff --git a/parser/tests/integer_literal_expression_test.go b/parser/tests/integer_literal_expression_test.go index 1f1f161..f929f96 100644 --- a/parser/tests/integer_literal_expression_test.go +++ b/parser/tests/integer_literal_expression_test.go @@ -1,6 +1,7 @@ package parser_tests import ( + "math/big" "testing" "github.com/0xM-D/interpreter/ast" @@ -26,5 +27,5 @@ func TestIntegerLiteral(t *testing.T) { t.Fatalf("program.Statements[0] is not an *ast.ExpressionStatement. got=%T", program.Statements[0]) } - testIntegerLiteral(t, stmt.Expression, 5) + testIntegerLiteral(t, stmt.Expression, big.NewInt(5)) } diff --git a/parser/tests/let_statement_test.go b/parser/tests/let_statement_test.go index d6fe149..2331520 100644 --- a/parser/tests/let_statement_test.go +++ b/parser/tests/let_statement_test.go @@ -1,6 +1,7 @@ package parser_tests import ( + "math/big" "testing" "github.com/0xM-D/interpreter/ast" @@ -14,7 +15,7 @@ func TestLetStatements(t *testing.T) { expectedIdentifier TestIdentifier expectedValue interface{} }{ - {"let x = 5;", TestIdentifier{"x"}, 5}, + {"let x = 5;", TestIdentifier{"x"}, big.NewInt(5)}, {"let y = true;", TestIdentifier{"y"}, true}, {"let foobar = y;", TestIdentifier{"foobar"}, TestIdentifier{"y"}}, } diff --git a/parser/tests/prefix_expression_test.go b/parser/tests/prefix_expression_test.go index 94072c2..9c87098 100644 --- a/parser/tests/prefix_expression_test.go +++ b/parser/tests/prefix_expression_test.go @@ -1,6 +1,7 @@ package parser_tests import ( + "math/big" "testing" "github.com/0xM-D/interpreter/ast" @@ -14,11 +15,11 @@ func TestParsingPrefixExpressions(t *testing.T) { operator string value interface{} }{ - {"!5", "!", 5}, - {"-15", "-", 15}, + {"!5", "!", big.NewInt(5)}, + {"-15", "-", big.NewInt(15)}, {"!true;", "!", true}, {"!false;", "!", false}, - {"~1;", "~", 1}, + {"~1;", "~", big.NewInt(1)}, } for _, tt := range prefixTests { diff --git a/parser/tests/ternary_operator_test.go b/parser/tests/ternary_operator_test.go index a3f098d..359f005 100644 --- a/parser/tests/ternary_operator_test.go +++ b/parser/tests/ternary_operator_test.go @@ -1,6 +1,7 @@ package parser_tests import ( + "math/big" "testing" "github.com/0xM-D/interpreter/ast" @@ -15,7 +16,7 @@ func TestTernaryOperator(t *testing.T) { valueIfTrue interface{} valueIfFalse interface{} }{ - {" true ? 1 : 2", true, 1, 2}, + {" true ? 1 : 2", true, big.NewInt(1), big.NewInt(2)}, {" false ? foo : bar", false, TestIdentifier{"foo"}, TestIdentifier{"bar"}}, } diff --git a/parser/tests/util.go b/parser/tests/util.go index f4d680f..9dcce84 100644 --- a/parser/tests/util.go +++ b/parser/tests/util.go @@ -2,6 +2,7 @@ package parser_tests import ( "fmt" + "math/big" "reflect" "testing" @@ -23,15 +24,15 @@ func checkParserErrors(t *testing.T, p *parser.Parser) { t.FailNow() } -func testIntegerLiteral(t *testing.T, il ast.Expression, value int64) bool { +func testIntegerLiteral(t *testing.T, il ast.Expression, value *big.Int) bool { integ, ok := il.(*ast.IntegerLiteral) if !ok { t.Errorf("il not *ast.IntegerLiteral. got=%T", il) return false } - if integ.Value != value { - t.Errorf("integ.Value not %d. got=%d", value, integ.Value) + if integ.Value.Cmp(value) != 0 { + t.Errorf("integ.Value not %d. got=%s", value, integ.Value.String()) return false } @@ -153,9 +154,7 @@ func testLiteralExpression(t *testing.T, exp ast.Expression, expected interface{ } switch v := expected.(type) { - case int: - return testIntegerLiteral(t, exp, int64(v)) - case int64: + case *big.Int: return testIntegerLiteral(t, exp, v) case string: return testStringLiteral(t, exp, v)