From f2d4aa9b75ac0a6c3b180cb0bac9ed475478b270 Mon Sep 17 00:00:00 2001 From: DustTheory Date: Wed, 20 Sep 2023 13:40:02 +0200 Subject: [PATCH 1/5] Added new number types --- ast/types.go | 4 +- evaluator/eval_infix_expression.go | 221 +++++++++++++----- evaluator/evaluator.go | 39 +--- evaluator/tests/array_literals_test.go | 7 +- .../tests/assignment_declaration_test.go | 7 +- evaluator/tests/assignment_expression_test.go | 21 +- evaluator/tests/error_handling_test.go | 20 +- evaluator/tests/eval_integer_exp_test.go | 71 +++--- evaluator/tests/for_statement_test.go | 8 +- evaluator/tests/function_application_test.go | 21 +- evaluator/tests/function_object_test.go | 2 +- evaluator/tests/hash_literals_test.go | 3 +- evaluator/tests/if_else_expression_test.go | 19 +- evaluator/tests/index_operator_exp_test.go | 47 ++-- evaluator/tests/let_statement_test.go | 15 +- evaluator/tests/return_statement_test.go | 17 +- evaluator/tests/type_builtins_test.go | 41 ++-- .../tests/typed_declaration_statement_test.go | 15 +- evaluator/tests/util.go | 20 +- evaluator/type_cast.go | 90 +++++-- evaluator/util.go | 125 ++++++++-- object/constants.go | 23 +- object/environment.go | 38 +-- object/hash_key.go | 17 +- object/number.go | 25 +- object/util.go | 22 +- parser/parse_integer_literal.go | 8 +- parser/tests/access_expression_test.go | 5 +- parser/tests/call_expression_parsing_test.go | 7 +- parser/tests/for_statement_test.go | 5 +- parser/tests/hash_literal_test.go | 15 +- parser/tests/index_expression_parsing_test.go | 3 +- parser/tests/infix_expression_test.go | 17 +- .../tests/integer_literal_expression_test.go | 3 +- parser/tests/let_statement_test.go | 3 +- parser/tests/prefix_expression_test.go | 7 +- parser/tests/ternary_operator_test.go | 3 +- parser/tests/util.go | 11 +- 38 files changed, 652 insertions(+), 373 deletions(-) diff --git a/ast/types.go b/ast/types.go index 9a8be53..e0f59dc 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..e81b425 100644 --- a/evaluator/eval_infix_expression.go +++ b/evaluator/eval_infix_expression.go @@ -20,11 +20,11 @@ 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.Int64Kind), string(object.Int64Kind)}: integerBitwiseAnd, + {"|", string(object.Int64Kind), string(object.Int64Kind)}: integerBitwiseOr, + {"^", string(object.Int64Kind), string(object.Int64Kind)}: integerBitwiseXor, + {">>", string(object.Int64Kind), string(object.Int64Kind)}: integerBitwiseShiftRight, + {"<<", string(object.Int64Kind), string(object.Int64Kind)}: integerBitwiseShiftLeft, {"+", string(object.StringKind), string(object.StringKind)}: stringAddition, {"+=", string(object.StringKind), string(object.StringKind)}: stringPlusEquals, @@ -102,38 +102,34 @@ func evalNumberInfixExpression(left object.Object, right object.Object, operator } } -func add[T int64 | float32 | float64](a object.Object, b object.Object) *object.Number[T] { +func add[T int8 | int16 | int32 | int64 | uint8 | uint16 | uint32 | uint64 | 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 subtract[T int64 | float32 | float64](a object.Object, b object.Object) *object.Number[T] { +func subtract[T int8 | int16 | int32 | int64 | uint8 | uint16 | uint32 | uint64 | 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 multiply[T int64 | float32 | float64](a object.Object, b object.Object) *object.Number[T] { +func multiply[T int8 | int16 | int32 | int64 | uint8 | uint16 | uint32 | uint64 | 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 divide[T int64 | float32 | float64](a object.Object, b object.Object) *object.Number[T] { +func divide[T int8 | int16 | int32 | int64 | uint8 | uint16 | uint32 | uint64 | 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 lessThan[T int64 | float32 | float64](a object.Object, b object.Object) *object.Boolean { +func lessThan[T int8 | int16 | int32 | int64 | uint8 | uint16 | uint32 | uint64 | float32 | float64](a object.Object, b object.Object) *object.Boolean { return nativeBoolToBooleanObject(a.(*object.Number[T]).Value < b.(*object.Number[T]).Value) } -func greaterThan[T int64 | float32 | float64](a object.Object, b object.Object) *object.Boolean { +func greaterThan[T int8 | int16 | int32 | int64 | uint8 | uint16 | uint32 | uint64 | float32 | float64](a object.Object, b object.Object) *object.Boolean { return nativeBoolToBooleanObject(a.(*object.Number[T]).Value > b.(*object.Number[T]).Value) } -func equals[T int64 | float32 | float64](a object.Object, b object.Object) *object.Boolean { +func equals[T int8 | int16 | int32 | int64 | uint8 | uint16 | uint32 | uint64 | float32 | float64](a object.Object, b object.Object) *object.Boolean { return nativeBoolToBooleanObject(a.(*object.Number[T]).Value == b.(*object.Number[T]).Value) } -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) -} - 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} } @@ -155,11 +151,27 @@ func integerBitwiseShiftRight(a object.Object, b object.Object, env *object.Envi } func numberAddition(left object.Object, right object.Object, env *object.Environment) object.Object { - leftNum, rightNum, kind := castToLargerNumberType(left, right) - - switch kind { - case object.IntegerKind: + nums := castToLargerNumberType(left, right) + leftNum := nums[0] + rightNum := nums[1] + + switch leftNum.Type().Kind() { + case object.Int8Kind: + return add[int8](leftNum, rightNum) + case object.Int16Kind: + return add[int16](leftNum, rightNum) + case object.Int32Kind: + return add[int32](leftNum, rightNum) + case object.Int64Kind: return add[int64](leftNum, rightNum) + case object.UInt8Kind: + return add[uint8](leftNum, rightNum) + case object.UInt16Kind: + return add[uint16](leftNum, rightNum) + case object.UInt32Kind: + return add[uint32](leftNum, rightNum) + case object.UInt64Kind: + return add[uint64](leftNum, rightNum) case object.Float32Kind: return add[float32](leftNum, rightNum) case object.Float64Kind: @@ -170,11 +182,27 @@ func numberAddition(left object.Object, right object.Object, env *object.Environ } func numberSubtraction(left object.Object, right object.Object, env *object.Environment) object.Object { - leftNum, rightNum, kind := castToLargerNumberType(left, right) - - switch kind { - case object.IntegerKind: + nums := castToLargerNumberType(left, right) + leftNum := nums[0] + rightNum := nums[1] + + switch leftNum.Type().Kind() { + case object.Int8Kind: + return subtract[int8](leftNum, rightNum) + case object.Int16Kind: + return subtract[int16](leftNum, rightNum) + case object.Int32Kind: + return subtract[int32](leftNum, rightNum) + case object.Int64Kind: return subtract[int64](leftNum, rightNum) + case object.UInt8Kind: + return subtract[uint8](leftNum, rightNum) + case object.UInt16Kind: + return subtract[uint16](leftNum, rightNum) + case object.UInt32Kind: + return subtract[uint32](leftNum, rightNum) + case object.UInt64Kind: + return subtract[uint64](leftNum, rightNum) case object.Float32Kind: return subtract[float32](leftNum, rightNum) case object.Float64Kind: @@ -185,11 +213,27 @@ func numberSubtraction(left object.Object, right object.Object, env *object.Envi } func numberMultiplication(left object.Object, right object.Object, env *object.Environment) object.Object { - leftNum, rightNum, kind := castToLargerNumberType(left, right) - - switch kind { - case object.IntegerKind: + nums := castToLargerNumberType(left, right) + leftNum := nums[0] + rightNum := nums[1] + + switch leftNum.Type().Kind() { + case object.Int8Kind: + return multiply[int8](leftNum, rightNum) + case object.Int16Kind: + return multiply[int16](leftNum, rightNum) + case object.Int32Kind: + return multiply[int32](leftNum, rightNum) + case object.Int64Kind: return multiply[int64](leftNum, rightNum) + case object.UInt8Kind: + return multiply[uint8](leftNum, rightNum) + case object.UInt16Kind: + return multiply[uint16](leftNum, rightNum) + case object.UInt32Kind: + return multiply[uint32](leftNum, rightNum) + case object.UInt64Kind: + return multiply[uint64](leftNum, rightNum) case object.Float32Kind: return multiply[float32](leftNum, rightNum) case object.Float64Kind: @@ -200,11 +244,27 @@ func numberMultiplication(left object.Object, right object.Object, env *object.E } func numberDivision(left object.Object, right object.Object, env *object.Environment) object.Object { - leftNum, rightNum, kind := castToLargerNumberType(left, right) - - switch kind { - case object.IntegerKind: + nums := castToLargerNumberType(left, right) + leftNum := nums[0] + rightNum := nums[1] + + switch leftNum.Type().Kind() { + case object.Int8Kind: + return divide[int8](leftNum, rightNum) + case object.Int16Kind: + return divide[int16](leftNum, rightNum) + case object.Int32Kind: + return divide[int32](leftNum, rightNum) + case object.Int64Kind: return divide[int64](leftNum, rightNum) + case object.UInt8Kind: + return divide[uint8](leftNum, rightNum) + case object.UInt16Kind: + return divide[uint16](leftNum, rightNum) + case object.UInt32Kind: + return divide[uint32](leftNum, rightNum) + case object.UInt64Kind: + return divide[uint64](leftNum, rightNum) case object.Float32Kind: return divide[float32](leftNum, rightNum) case object.Float64Kind: @@ -215,10 +275,27 @@ func numberDivision(left object.Object, right object.Object, env *object.Environ } func numberLessThan(left object.Object, right object.Object, env *object.Environment) object.Object { - leftNum, rightNum, kind := castToLargerNumberType(left, right) - switch kind { - case object.IntegerKind: + nums := castToLargerNumberType(left, right) + leftNum := nums[0] + rightNum := nums[1] + + switch leftNum.Type().Kind() { + case object.Int8Kind: + return lessThan[int8](leftNum, rightNum) + case object.Int16Kind: + return lessThan[int16](leftNum, rightNum) + case object.Int32Kind: + return lessThan[int32](leftNum, rightNum) + case object.Int64Kind: return lessThan[int64](leftNum, rightNum) + case object.UInt8Kind: + return lessThan[uint8](leftNum, rightNum) + case object.UInt16Kind: + return lessThan[uint16](leftNum, rightNum) + case object.UInt32Kind: + return lessThan[uint32](leftNum, rightNum) + case object.UInt64Kind: + return lessThan[uint64](leftNum, rightNum) case object.Float32Kind: return lessThan[float32](leftNum, rightNum) case object.Float64Kind: @@ -237,11 +314,27 @@ func numberGreaterThanEqual(left object.Object, right object.Object, env *object } func numberGreaterThan(left object.Object, right object.Object, env *object.Environment) object.Object { - leftNum, rightNum, kind := castToLargerNumberType(left, right) - - switch kind { - case object.IntegerKind: + nums := castToLargerNumberType(left, right) + leftNum := nums[0] + rightNum := nums[1] + + switch leftNum.Type().Kind() { + case object.Int8Kind: + return greaterThan[int8](leftNum, rightNum) + case object.Int16Kind: + return greaterThan[int16](leftNum, rightNum) + case object.Int32Kind: + return greaterThan[int32](leftNum, rightNum) + case object.Int64Kind: return greaterThan[int64](leftNum, rightNum) + case object.UInt8Kind: + return greaterThan[uint8](leftNum, rightNum) + case object.UInt16Kind: + return greaterThan[uint16](leftNum, rightNum) + case object.UInt32Kind: + return greaterThan[uint32](leftNum, rightNum) + case object.UInt64Kind: + return greaterThan[uint64](leftNum, rightNum) case object.Float32Kind: return greaterThan[float32](leftNum, rightNum) case object.Float64Kind: @@ -252,11 +345,27 @@ func numberGreaterThan(left object.Object, right object.Object, env *object.Envi } func numberEquals(left object.Object, right object.Object, env *object.Environment) object.Object { - leftNum, rightNum, kind := castToLargerNumberType(left, right) - - switch kind { - case object.IntegerKind: + nums := castToLargerNumberType(left, right) + leftNum := nums[0] + rightNum := nums[1] + + switch leftNum.Type().Kind() { + case object.Int8Kind: + return equals[int8](leftNum, rightNum) + case object.Int16Kind: + return equals[int16](leftNum, rightNum) + case object.Int32Kind: + return equals[int32](leftNum, rightNum) + case object.Int64Kind: return equals[int64](leftNum, rightNum) + case object.UInt8Kind: + return equals[uint8](leftNum, rightNum) + case object.UInt16Kind: + return equals[uint16](leftNum, rightNum) + case object.UInt32Kind: + return equals[uint32](leftNum, rightNum) + case object.UInt64Kind: + return equals[uint64](leftNum, rightNum) case object.Float32Kind: return equals[float32](leftNum, rightNum) case object.Float64Kind: @@ -267,18 +376,7 @@ func numberEquals(left object.Object, right object.Object, env *object.Environme } func numberNotEquals(left object.Object, right object.Object, env *object.Environment) object.Object { - leftNum, rightNum, kind := castToLargerNumberType(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) - } - - return nil + return evalBangOperatorExpression(numberEquals(left, right, env)) } func numberPlusEquals(left object.Object, right object.Object, env *object.Environment) object.Object { @@ -300,14 +398,23 @@ func numberDivideEquals(left object.Object, right object.Object, env *object.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, IMPLICIT_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 8e41789..199f457 100644 --- a/evaluator/evaluator.go +++ b/evaluator/evaluator.go @@ -12,7 +12,7 @@ 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} case *ast.Float64Literal: @@ -38,10 +38,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: @@ -59,15 +56,7 @@ func Eval(node ast.Node, env *object.Environment) object.Object { case *ast.StringLiteral: return &object.String{Value: node.Value} case *ast.ArrayLiteral: - elements := evalExpressions(node.Elements, env) - var elementType object.ObjectType = object.AnyKind - if len(elements) == 1 && object.IsError(elements[0]) { - return elements[0] - } - if len(elements) > 0 { - elementType = elements[0].Type() - } - return &object.Array{Elements: elements, ArrayObjectType: object.ArrayObjectType{ElementType: elementType}} + return evalArrayLiteral(node, env) case *ast.IndexExpression: left := Eval(node.Left, env) if object.IsError(left) { @@ -92,27 +81,13 @@ func Eval(node ast.Node, env *object.Environment) object.Object { case *ast.HashLiteral: return evalHashLiteral(node, 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: - { - result := evalTernaryExpression(node, env) - if result != nil { - return result - } - } + return evalTernaryExpression(node, env) } return nil diff --git a/evaluator/tests/array_literals_test.go b/evaluator/tests/array_literals_test.go index 6824d7f..3cda1ce 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 e1e59b8..a260d95 100644 --- a/evaluator/tests/error_handling_test.go +++ b/evaluator/tests/error_handling_test.go @@ -13,11 +13,11 @@ func TestErrorHandling(t *testing.T) { }{ { "5 + true;", - "operator + not defined on types int and bool", + "operator + not defined on types int8 and bool", }, { "5 + true; 5;", - "operator + not defined on types int and bool", + "operator + not defined on types int8 and bool", }, { "-true", @@ -59,8 +59,8 @@ func TestErrorHandling(t *testing.T) { "unusable as hash key: function(string) -> string", }, { - `int a = "fasdf"`, - "Expression of type string cannot be assigned to int", + `int64 a = "fasdf"`, + "Expression of type string cannot be assigned to int64", }, { `a := "fasdf"; bool c = a;`, @@ -75,7 +75,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", }, { @@ -84,14 +84,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", }, { @@ -99,12 +99,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", }, { `[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 int8[]", }, } for _, tt := range tests { diff --git a/evaluator/tests/eval_integer_exp_test.go b/evaluator/tests/eval_integer_exp_test.go index 7d0a8b0..1133aa3 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(62)}, + {"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 3a5d36c..41a6321 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/evaluator" @@ -43,6 +44,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 873c390..0eb3084 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) { }{ { "[1, 2, 3][0]", - 1, + big.NewInt(1), }, { "[1, 2, 3][1]", - 2, + big.NewInt(2), }, { "[1, 2, 3][2]", - 3, + big.NewInt(3), }, { "let i = 0; [1][i];", - 1, + big.NewInt(1), }, { "[1, 2, 3][1 + 1];", - 3, + big.NewInt(3), }, { "let myArray = [1, 2, 3]; myArray[2];", - 3, + big.NewInt(3), }, { "let myArray = [1, 2, 3]; myArray[0] + myArray[1] + myArray[2];", - 6, + big.NewInt(6), }, { "let myArray = [1, 2, 3]; let i = myArray[0]; myArray[i]", - 2, + big.NewInt(2), }, { "[1, 2, 3][3]", @@ -49,15 +52,15 @@ func TestArrayIndexExpressions(t *testing.T) { }, { "let a = [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) { }{ { `{"foo": 5}["foo"]`, - 5, + big.NewInt(5), }, { `{"foo": 5}["bar"]`, @@ -79,7 +82,7 @@ func TestHashIndexExpressions(t *testing.T) { }, { `let key = "foo"; {"foo": 5}[key]`, - 5, + big.NewInt(5), }, { `{}["foo"]`, @@ -87,36 +90,36 @@ func TestHashIndexExpressions(t *testing.T) { }, { `{5: 5}[5]`, - 5, + big.NewInt(5), }, { `{true: 5}[true]`, - 5, + big.NewInt(5), }, { `{false: 5}[false]`, - 5, + big.NewInt(5), }, { `{false: 5}[false]`, - 5, + big.NewInt(5), }, { "let a = {1: 2}; a[1] = 3; a[1]", - 3, + big.NewInt(3), }, { "let a = {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 f20384d..077b8cd 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 = [1, 2, 3]; arr.size();", 3}, - {"let arr = []; arr.size();", 0}, - {"[1, 2, 3, 4, 5].size();", 5}, - {`str := "abcdef"; str.length();`, 6}, - {`const string str = ""; str.length();`, 0}, - {`"bleh".length()`, 4}, + {"let arr = [1, 2, 3]; arr.size();", big.NewInt(3)}, + {"let arr = []; arr.size();", big.NewInt(0)}, + {"[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 = [1, 2, 3]; arr.push(4).size()", 4}, - {"[].push(1).size();", 1}, - {"[1, 2, 3].delete(1, 5).size();", 1}, + {"let arr = [1, 2, 3]; arr.push(4).size()", big.NewInt(4)}, + {"[].push(1).size();", big.NewInt(1)}, + {"[1, 2, 3].delete(1, 5).size();", big.NewInt(1)}, {`["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"].delete(1, 3);`, []string{"1", "5", "6", "7", "8", "9", "10"}}, {`[].pushMultiple("0", 10)`, []string{"0", "0", "0", "0", "0", "0", "0", "0", "0", "0"}}, - {`[1, 2, 3].pushMultiple(0, 10).size()`, 13}, - {`[1, 2, 3].slice(0, 3).size()`, 3}, - {`[1, 2, 3].slice(0, 2).size()`, 2}, - {`[1, 2, 3].slice(1, 2).size()`, 2}, - {`[1, 2, 3].slice(1, 100).size()`, 2}, - {`[1, 2, 3].slice(2, 1).size()`, 1}, - {`[1, 2, 3].slice(2, 0).size()`, 0}, + {`[1, 2, 3].pushMultiple(0, 10).size()`, big.NewInt(13)}, + {`[1, 2, 3].slice(0, 3).size()`, big.NewInt(3)}, + {`[1, 2, 3].slice(0, 2).size()`, big.NewInt(2)}, + {`[1, 2, 3].slice(1, 2).size()`, big.NewInt(2)}, + {`[1, 2, 3].slice(1, 100).size()`, big.NewInt(2)}, + {`[1, 2, 3].slice(2, 1).size()`, big.NewInt(1)}, + {`[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 6a1be11..b506212 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 = [1, 2, 3, 4]; let b = a; b;", []string{"1", "2", "3", "4"}}, + {"const int8[] a = [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 5670b03..92237ae 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" @@ -14,7 +15,6 @@ func testEval(input string) object.Object { p := parser.New(l) program := p.ParseProgram() env := object.NewEnvironment() - return evaluator.Eval(program, env) } @@ -26,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 @@ -76,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) @@ -194,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..d609756 100644 --- a/evaluator/type_cast.go +++ b/evaluator/type_cast.go @@ -22,20 +22,39 @@ type CastRule struct { } 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}, + {object.Int8Kind.Signature(), object.Int16Kind.Signature()}: {true, numberCast[int8, int16]}, + {object.Int8Kind.Signature(), object.Int32Kind.Signature()}: {true, numberCast[int8, int32]}, + {object.Int8Kind.Signature(), object.Int64Kind.Signature()}: {true, numberCast[int8, int64]}, + {object.Int16Kind.Signature(), object.Int32Kind.Signature()}: {true, numberCast[int16, int32]}, + {object.Int16Kind.Signature(), object.Int64Kind.Signature()}: {true, numberCast[int16, int64]}, + {object.Int32Kind.Signature(), object.Int64Kind.Signature()}: {true, numberCast[int32, int64]}, + + {object.Int8Kind.Signature(), object.Float32Kind.Signature()}: {true, numberCast[int8, float32]}, + {object.Int16Kind.Signature(), object.Float32Kind.Signature()}: {true, numberCast[int16, float32]}, + {object.Int32Kind.Signature(), object.Float32Kind.Signature()}: {true, numberCast[int32, float32]}, + {object.Int64Kind.Signature(), object.Float32Kind.Signature()}: {true, numberCast[int64, float32]}, + + {object.Int8Kind.Signature(), object.Float64Kind.Signature()}: {true, numberCast[int8, float64]}, + {object.Int16Kind.Signature(), object.Float64Kind.Signature()}: {true, numberCast[int16, float64]}, + {object.Int32Kind.Signature(), object.Float64Kind.Signature()}: {true, numberCast[int32, float64]}, + {object.Int64Kind.Signature(), object.Float64Kind.Signature()}: {true, numberCast[int64, float64]}, + + {object.Float32Kind.Signature(), object.Float64Kind.Signature()}: {true, numberCast[float32, float64]}, + {"{string -> string}", "{int -> string}"}: {true, castEmptyMap}, + {"int[]", "string[]"}: {true, castEmptyArray}, + {"any[]", "int[]"}: {true, castEmptyArray}, } func typeCast(obj object.Object, targetType object.ObjectType, implicit bool) object.Object { + fromSignature := obj.Type().Signature() toSignature := targetType.Signature() castRuleSignature := CastRuleSignature{fromSignature, toSignature} + if fromSignature == toSignature { + return obj + } + castRule, castRuleExists := castRules[castRuleSignature] if !castRuleExists { return newError("No rule to cast between %s and %s is defined", fromSignature, toSignature) @@ -53,7 +72,9 @@ func intToString(obj object.Object) object.Object { return &object.String{Value: strconv.FormatInt(integer.Value, 10)} } -func numberCast[F int64 | float32 | float64, T int64 | float32 | float64](obj object.Object) object.Object { +func numberCast[ + F int8 | int16 | int32 | int64 | uint8 | uint16 | uint32 | uint64 | float32 | float64, + T int8 | int16 | int32 | int64 | uint8 | uint16 | uint32 | uint64 | float32 | float64](obj object.Object) object.Object { val := object.UnwrapReferenceObject(obj).(*object.Number[F]) return &object.Number[T]{Value: T(val.Value)} } @@ -63,7 +84,7 @@ func castEmptyMap(obj object.Object) object.Object { if len(hash.Pairs) != 0 { return newError("Can't cast non empty hash") } - return &object.Hash{Pairs: hash.Pairs, HashObjectType: object.HashObjectType{KeyType: object.IntegerKind, ValueType: object.IntegerKind}} + return &object.Hash{Pairs: hash.Pairs, HashObjectType: object.HashObjectType{KeyType: object.Int64Kind, ValueType: object.Int64Kind}} } func castEmptyArray(obj object.Object) object.Object { @@ -76,31 +97,54 @@ func castEmptyArray(obj object.Object) object.Object { const ( _ uint8 = iota - INT_WEIGHT + INT8_WEIGHT + UINT8_WEIGHT + INT16_WEIGHT + UINT16_WEIGHT + INT32_WEIGHT + UINT32_WEIGHT + INT64_WEIGHT + UINT64_WEIGHT FLOAT32_WEIGHT FLOAT64_WEIGHT ) var numberCastWeight = map[object.ObjectKind]uint8{ - object.IntegerKind: INT_WEIGHT, + 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, } -func castToLargerNumberType(num1 object.Object, num2 object.Object) (object.Object, object.Object, object.ObjectKind) { - num1 = object.UnwrapReferenceObject(num1) - num2 = object.UnwrapReferenceObject(num2) +func castToLargerNumberType(nums ...object.Object) []object.Object { + if len(nums) == 0 { + return []object.Object{} + } - k1 := num1.Type().Kind() - k2 := num2.Type().Kind() - w1 := numberCastWeight[k1] - w2 := numberCastWeight[k2] + kindToCastTo := object.UnwrapReferenceObject(nums[0]).Type().Kind() - if w1 == w2 { - return num1, num2, k1 + for _, num := range nums { + currKind := object.UnwrapReferenceObject(num).Type().Kind() + if numberCastWeight[currKind] > numberCastWeight[kindToCastTo] { + kindToCastTo = currKind + } } - if w1 > w2 { - return num1, typeCast(num2, k1, IMPLICIT_CAST), k1 + + resultNums := []object.Object{} + + for _, num := range nums { + result := typeCast(object.UnwrapReferenceObject(num), kindToCastTo, true) + if object.IsError(result) { + println(result.Inspect()) + } + resultNums = append(resultNums, result) } - return typeCast(num1, k2, IMPLICIT_CAST), num2, k2 + + return resultNums } diff --git a/evaluator/util.go b/evaluator/util.go index 303b5c8..51436b7 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" @@ -36,8 +38,22 @@ func evalBangOperatorExpression(right object.Object) object.Object { func evalMinusPrefixOperatorExpression(right object.Object) object.Object { switch right.Type() { - case object.IntegerKind: + case object.Int8Kind: + return &object.Number[int8]{Value: -right.(*object.Number[int8]).Value} + case object.Int16Kind: + return &object.Number[int16]{Value: -right.(*object.Number[int16]).Value} + case object.Int32Kind: + return &object.Number[int32]{Value: -right.(*object.Number[int32]).Value} + case object.Int64Kind: return &object.Number[int64]{Value: -right.(*object.Number[int64]).Value} + case object.UInt8Kind: + return &object.Number[uint8]{Value: -right.(*object.Number[uint8]).Value} + case object.UInt16Kind: + return &object.Number[uint16]{Value: -right.(*object.Number[uint16]).Value} + case object.UInt32Kind: + return &object.Number[uint32]{Value: -right.(*object.Number[uint32]).Value} + case object.UInt64Kind: + return &object.Number[uint64]{Value: -right.(*object.Number[uint64]).Value} case object.Float32Kind: return &object.Number[float32]{Value: -right.(*object.Number[float32]).Value} case object.Float64Kind: @@ -50,8 +66,22 @@ func evalMinusPrefixOperatorExpression(right object.Object) object.Object { func evalTildePrefixOperatorExpression(right object.Object) object.Object { switch right.Type() { - case object.IntegerKind: + case object.Int8Kind: + return &object.Number[int8]{Value: ^right.(*object.Number[int8]).Value} + case object.Int16Kind: + return &object.Number[int16]{Value: ^right.(*object.Number[int16]).Value} + case object.Int32Kind: + return &object.Number[int32]{Value: ^right.(*object.Number[int32]).Value} + case object.Int64Kind: return &object.Number[int64]{Value: ^right.(*object.Number[int64]).Value} + case object.UInt8Kind: + return &object.Number[uint8]{Value: ^right.(*object.Number[uint8]).Value} + case object.UInt16Kind: + return &object.Number[uint16]{Value: ^right.(*object.Number[uint16]).Value} + case object.UInt32Kind: + return &object.Number[uint32]{Value: ^right.(*object.Number[uint32]).Value} + case object.UInt64Kind: + return &object.Number[uint64]{Value: ^right.(*object.Number[uint64]).Value} default: return newError("unknown operator: -%s", right.Type().Signature()) @@ -148,7 +178,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[int64]).Value max := int64(len(arrayObject.Elements) - 1) if idx < 0 || idx > max { @@ -158,6 +188,36 @@ func evalArrayIndexExpression(array, index object.Object) object.Object { return &object.ArrayElementReference{Array: arrayObject, Index: idx} } +func evalArrayLiteral(node *ast.ArrayLiteral, env *object.Environment) object.Object { + elements := evalExpressions(node.Elements, env) + var elementType object.ObjectType = object.AnyKind + + if len(elements) == 1 && object.IsError(elements[0]) { + return elements[0] + } + + if len(elements) > 0 { + elementType = elements[0].Type() + + for _, element := range elements { + + if object.IsNumber(element) && object.IsNumberKind(elementType.Kind()) { + continue + } + + if !object.TypesMatch(elementType, element.Type()) { + return newError("Array literal cannot contain mixed element types") + } + } + + if object.IsNumberKind(elementType.Kind()) { + elements = castToLargerNumberType(elements...) + } + + } + return &object.Array{Elements: elements, ArrayObjectType: object.ArrayObjectType{ElementType: elementType}} +} + func evalHashLiteral( node *ast.HashLiteral, env *object.Environment, @@ -203,25 +263,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: @@ -266,13 +307,28 @@ 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) if !object.IsError(cast) { @@ -376,3 +432,26 @@ func evalTernaryExpression(node *ast.TernaryExpression, env *object.Environment) return result } + +func evalIntegerLiteral(node *ast.IntegerLiteral, env *object.Environment) object.Object { + switch { + case node.Value.Cmp(big.NewInt(math.MaxInt8)) == -1: + return &object.Number[int8]{Value: int8(node.Value.Int64())} + case node.Value.Cmp(big.NewInt(math.MaxUint8)) == -1: + return &object.Number[uint8]{Value: uint8(node.Value.Int64())} + case node.Value.Cmp(big.NewInt(math.MaxInt16)) == -1: + return &object.Number[int16]{Value: int16(node.Value.Int64())} + case node.Value.Cmp(big.NewInt(math.MaxUint16)) == -1: + return &object.Number[uint16]{Value: uint16(node.Value.Int64())} + case node.Value.Cmp(big.NewInt(math.MaxInt32)) == -1: + return &object.Number[int32]{Value: int32(node.Value.Int64())} + case node.Value.Cmp(big.NewInt(math.MaxUint32)) == -1: + return &object.Number[uint32]{Value: uint32(node.Value.Int64())} + case node.Value.Cmp(big.NewInt(math.MaxInt64)) == -1: + return &object.Number[int64]{Value: int64(node.Value.Int64())} + case node.Value.Cmp(new(big.Int).SetUint64(math.MaxUint64)) == -1: + return &object.Number[uint64]{Value: uint64(node.Value.Uint64())} + default: + return newError("Integer out of max range") + } +} diff --git a/object/constants.go b/object/constants.go index 1737bc7..894e347 100644 --- a/object/constants.go +++ b/object/constants.go @@ -15,7 +15,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,7 +40,7 @@ const ( func initIntrinsicTypeBuiltins() map[ObjectKind]*FunctionRepository { repos := map[ObjectKind]*FunctionRepository{} - repos[IntegerKind] = initIntegerBuiltins() + repos[Int64Kind] = initIntegerBuiltins() repos[ArrayKind] = initArrayBuiltins() repos[StringKind] = initStringBuiltins() @@ -51,7 +58,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,11 +66,11 @@ 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 } diff --git a/object/environment.go b/object/environment.go index cb2f232..eaa0403 100644 --- a/object/environment.go +++ b/object/environment.go @@ -13,14 +13,22 @@ 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, + 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 +60,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..44dce51 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[T]) 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..ad5836d 100644 --- a/object/number.go +++ b/object/number.go @@ -4,26 +4,39 @@ import ( "fmt" ) -type Number[V int64 | float32 | float64] struct { +type Number[V int8 | int16 | int32 | int64 | uint8 | uint16 | uint32 | uint64 | float32 | float64] struct { Value V } 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) + default: + return fmt.Sprintf("%d", val) } - return "This should never ever be reached" } func (i *Number[V]) Type() ObjectType { switch any(i.Value).(type) { + case int8: + return Int8Kind + case int16: + return Int16Kind + case int32: + return Int32Kind case int64: - return IntegerKind + return Int64Kind + case uint8: + return UInt8Kind + case uint16: + return UInt16Kind + case uint32: + return UInt32Kind + case uint64: + return UInt64Kind case float32: return Float32Kind case float64: @@ -34,7 +47,7 @@ func (i *Number[V]) Type() ObjectType { func NewNumberOfKind(kind ObjectKind) Object { switch kind { - case IntegerKind: + case Int64Kind: return &Number[int64]{} case Float32Kind: return &Number[float32]{} 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 e16bba2..53f58c8 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"}}, - {"([1, 2, 3, 4]).size", []interface{}{1, 2, 3, 4}, TestIdentifier{"size"}}, + {"([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 3f7af12..96c722e 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" @@ -27,10 +28,10 @@ func TestParsingHashLiteralsStringKeys(t *testing.T) { t.Errorf("hash.Pairs has wrong length. got=%d", len(hash.Pairs)) } - 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 key, value := range hash.Pairs { @@ -87,13 +88,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 3938b03..55e11dd 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) From 1a614477c88cd0fe66b042ee2cad13a3c884494e Mon Sep 17 00:00:00 2001 From: DustTheory Date: Tue, 3 Oct 2023 13:09:29 +0200 Subject: [PATCH 2/5] update array signature --- object/array.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 } From 1a67fdff36da54daaa9fb011f616bf574f2bd2d6 Mon Sep 17 00:00:00 2001 From: DustTheory Date: Fri, 13 Oct 2023 11:51:34 +0200 Subject: [PATCH 3/5] fixed failing expression tests --- .vscode/settings.json | 3 + evaluator/eval_infix_expression.go | 462 ++++++++---------- evaluator/evaluator.go | 6 +- evaluator/tests/array_literals_test.go | 2 +- evaluator/tests/error_handling_test.go | 8 +- evaluator/tests/eval_float_exp_test.go | 6 +- evaluator/tests/eval_integer_exp_test.go | 4 +- evaluator/tests/hash_literals_test.go | 12 +- evaluator/tests/index_operator_exp_test.go | 2 +- evaluator/tests/type_builtins_test.go | 4 +- .../tests/typed_declaration_statement_test.go | 2 +- evaluator/tests/util.go | 12 +- evaluator/type_cast.go | 215 ++++---- evaluator/util.go | 143 +++--- object/constants.go | 41 +- object/environment.go | 1 + object/hash_key.go | 2 +- object/number.go | 117 +++-- 18 files changed, 516 insertions(+), 526 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..afb940f --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "discord.enabled": true +} \ No newline at end of file diff --git a/evaluator/eval_infix_expression.go b/evaluator/eval_infix_expression.go index e81b425..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.Int64Kind), string(object.Int64Kind)}: integerBitwiseAnd, - {"|", string(object.Int64Kind), string(object.Int64Kind)}: integerBitwiseOr, - {"^", string(object.Int64Kind), string(object.Int64Kind)}: integerBitwiseXor, - {">>", string(object.Int64Kind), string(object.Int64Kind)}: integerBitwiseShiftRight, - {"<<", string(object.Int64Kind), string(object.Int64Kind)}: integerBitwiseShiftLeft, - {"+", string(object.StringKind), string(object.StringKind)}: stringAddition, {"+=", string(object.StringKind), string(object.StringKind)}: stringPlusEquals, } @@ -67,332 +63,280 @@ 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 int8 | int16 | int32 | int64 | uint8 | uint16 | uint32 | uint64 | 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 int8 | int16 | int32 | int64 | uint8 | uint16 | uint32 | uint64 | 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 int8 | int16 | int32 | int64 | uint8 | uint16 | uint32 | uint64 | 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 int8 | int16 | int32 | int64 | uint8 | uint16 | uint32 | uint64 | 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 int8 | int16 | int32 | int64 | uint8 | uint16 | uint32 | uint64 | 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 int8 | int16 | int32 | int64 | uint8 | uint16 | uint32 | uint64 | 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 int8 | int16 | int32 | int64 | uint8 | uint16 | uint32 | uint64 | 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 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} -} + if err != nil { + return newError(err.Error()) + } -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} -} + var difference *object.Number -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} -} + 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 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} -} + castedDiffrence, err := numberCast(difference, leftNum.Kind, EXPLICIT_CAST) + if err != nil { + return newError(err.Error()) + } -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} + return castedDiffrence } -func numberAddition(left object.Object, right object.Object, env *object.Environment) object.Object { - nums := castToLargerNumberType(left, right) - leftNum := nums[0] - rightNum := nums[1] - - switch leftNum.Type().Kind() { - case object.Int8Kind: - return add[int8](leftNum, rightNum) - case object.Int16Kind: - return add[int16](leftNum, rightNum) - case object.Int32Kind: - return add[int32](leftNum, rightNum) - case object.Int64Kind: - return add[int64](leftNum, rightNum) - case object.UInt8Kind: - return add[uint8](leftNum, rightNum) - case object.UInt16Kind: - return add[uint16](leftNum, rightNum) - case object.UInt32Kind: - return add[uint32](leftNum, rightNum) - case object.UInt64Kind: - return add[uint64](leftNum, rightNum) - case object.Float32Kind: - return add[float32](leftNum, rightNum) - case object.Float64Kind: - return add[float64](leftNum, rightNum) +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()) } - return nil -} + var product *object.Number -func numberSubtraction(left object.Object, right object.Object, env *object.Environment) object.Object { - nums := castToLargerNumberType(left, right) - leftNum := nums[0] - rightNum := nums[1] - - switch leftNum.Type().Kind() { - case object.Int8Kind: - return subtract[int8](leftNum, rightNum) - case object.Int16Kind: - return subtract[int16](leftNum, rightNum) - case object.Int32Kind: - return subtract[int32](leftNum, rightNum) - case object.Int64Kind: - return subtract[int64](leftNum, rightNum) - case object.UInt8Kind: - return subtract[uint8](leftNum, rightNum) - case object.UInt16Kind: - return subtract[uint16](leftNum, rightNum) - case object.UInt32Kind: - return subtract[uint32](leftNum, rightNum) - case object.UInt64Kind: - return subtract[uint64](leftNum, rightNum) - case object.Float32Kind: - return subtract[float32](leftNum, rightNum) - case object.Float64Kind: - return subtract[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 numberMultiplication(left object.Object, right object.Object, env *object.Environment) object.Object { - nums := castToLargerNumberType(left, right) - leftNum := nums[0] - rightNum := nums[1] - - switch leftNum.Type().Kind() { - case object.Int8Kind: - return multiply[int8](leftNum, rightNum) - case object.Int16Kind: - return multiply[int16](leftNum, rightNum) - case object.Int32Kind: - return multiply[int32](leftNum, rightNum) - case object.Int64Kind: - return multiply[int64](leftNum, rightNum) - case object.UInt8Kind: - return multiply[uint8](leftNum, rightNum) - case object.UInt16Kind: - return multiply[uint16](leftNum, rightNum) - case object.UInt32Kind: - return multiply[uint32](leftNum, rightNum) - case object.UInt64Kind: - return multiply[uint64](leftNum, rightNum) - case object.Float32Kind: - return multiply[float32](leftNum, rightNum) - case object.Float64Kind: - return multiply[float64](leftNum, rightNum) +func numberDivision(left *object.Number, right *object.Number, env *object.Environment) object.Object { + leftNum, rightNum, err := arithmeticCast(left, right) + + if err != nil { + return newError(err.Error()) + } + + var quotient *object.Number + + 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} + } + + 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 { - nums := castToLargerNumberType(left, right) - leftNum := nums[0] - rightNum := nums[1] - - switch leftNum.Type().Kind() { - case object.Int8Kind: - return divide[int8](leftNum, rightNum) - case object.Int16Kind: - return divide[int16](leftNum, rightNum) - case object.Int32Kind: - return divide[int32](leftNum, rightNum) - case object.Int64Kind: - return divide[int64](leftNum, rightNum) - case object.UInt8Kind: - return divide[uint8](leftNum, rightNum) - case object.UInt16Kind: - return divide[uint16](leftNum, rightNum) - case object.UInt32Kind: - return divide[uint32](leftNum, rightNum) - case object.UInt64Kind: - return divide[uint64](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 { - nums := castToLargerNumberType(left, right) - leftNum := nums[0] - rightNum := nums[1] - - switch leftNum.Type().Kind() { - case object.Int8Kind: - return lessThan[int8](leftNum, rightNum) - case object.Int16Kind: - return lessThan[int16](leftNum, rightNum) - case object.Int32Kind: - return lessThan[int32](leftNum, rightNum) - case object.Int64Kind: - return lessThan[int64](leftNum, rightNum) - case object.UInt8Kind: - return lessThan[uint8](leftNum, rightNum) - case object.UInt16Kind: - return lessThan[uint16](leftNum, rightNum) - case object.UInt32Kind: - return lessThan[uint32](leftNum, rightNum) - case object.UInt64Kind: - return lessThan[uint64](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 numberBitwiseXor(left *object.Number, right *object.Number, env *object.Environment) object.Object { + return &object.Number{Value: left.Value ^ right.GetUInt64(), Kind: left.Kind} +} + +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 NULL } -func numberGreaterThan(left object.Object, right object.Object, env *object.Environment) object.Object { - nums := castToLargerNumberType(left, right) - leftNum := nums[0] - rightNum := nums[1] - - switch leftNum.Type().Kind() { - case object.Int8Kind: - return greaterThan[int8](leftNum, rightNum) - case object.Int16Kind: - return greaterThan[int16](leftNum, rightNum) - case object.Int32Kind: - return greaterThan[int32](leftNum, rightNum) - case object.Int64Kind: - return greaterThan[int64](leftNum, rightNum) - case object.UInt8Kind: - return greaterThan[uint8](leftNum, rightNum) - case object.UInt16Kind: - return greaterThan[uint16](leftNum, rightNum) - case object.UInt32Kind: - return greaterThan[uint32](leftNum, rightNum) - case object.UInt64Kind: - return greaterThan[uint64](leftNum, rightNum) - case object.Float32Kind: - return greaterThan[float32](leftNum, rightNum) - case object.Float64Kind: - return greaterThan[float64](leftNum, rightNum) +func numberGreaterThan(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 { - nums := castToLargerNumberType(left, right) - leftNum := nums[0] - rightNum := nums[1] - - switch leftNum.Type().Kind() { - case object.Int8Kind: - return equals[int8](leftNum, rightNum) - case object.Int16Kind: - return equals[int16](leftNum, rightNum) - case object.Int32Kind: - return equals[int32](leftNum, rightNum) - case object.Int64Kind: - return equals[int64](leftNum, rightNum) - case object.UInt8Kind: - return equals[uint8](leftNum, rightNum) - case object.UInt16Kind: - return equals[uint16](leftNum, rightNum) - case object.UInt32Kind: - return equals[uint32](leftNum, rightNum) - case object.UInt64Kind: - return equals[uint64](leftNum, rightNum) - case object.Float32Kind: - return equals[float32](leftNum, rightNum) - case object.Float64Kind: - return equals[float64](leftNum, rightNum) +func numberEquals(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 numberLessThanEqual(left *object.Number, right *object.Number, env *object.Environment) object.Object { + return evalBangOperatorExpression(numberGreaterThan(left, right, env)) +} + +func numberGreaterThanEqual(left *object.Number, right *object.Number, env *object.Environment) object.Object { + return evalBangOperatorExpression(numberLessThan(left, right, env)) } -func numberNotEquals(left object.Object, right object.Object, env *object.Environment) object.Object { +func numberNotEquals(left *object.Number, right *object.Number, env *object.Environment) object.Object { return evalBangOperatorExpression(numberEquals(left, right, env)) } -func numberPlusEquals(left object.Object, right object.Object, env *object.Environment) object.Object { - return assignment(left, numberAddition(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.Object, env *object.Environment) object.Object { - return assignment(left, numberSubtraction(left, 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.Object, env *object.Environment) object.Object { - return assignment(left, numberMultiplication(left, 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.Object, env *object.Environment) object.Object { - return assignment(left, numberDivision(left, 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 { @@ -409,7 +353,7 @@ func assignment(left object.Object, right object.Object, env *object.Environment } if lvalueType.Signature() != rvalueType.Signature() { - cast := typeCast(rvalue, lvalueType, IMPLICIT_CAST) + cast := typeCast(rvalue, lvalueType, EXPLICIT_CAST) if !object.IsError(cast) { rvalue = cast } else { diff --git a/evaluator/evaluator.go b/evaluator/evaluator.go index e454e02..07ed38a 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" ) @@ -14,9 +16,9 @@ func Eval(node ast.Node, env *object.Environment) object.Object { case *ast.IntegerLiteral: 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: diff --git a/evaluator/tests/array_literals_test.go b/evaluator/tests/array_literals_test.go index dc5d55f..d76769b 100644 --- a/evaluator/tests/array_literals_test.go +++ b/evaluator/tests/array_literals_test.go @@ -8,7 +8,7 @@ import ( ) func TestArrayLiterals(t *testing.T) { - input := "[]int{1, 2 * 2, 3 + 3}" + input := "[]int64{1, 2 * 2, 3 + 3}" evaluated := testEval(input) result, ok := evaluated.(*object.Array) diff --git a/evaluator/tests/error_handling_test.go b/evaluator/tests/error_handling_test.go index 5ef56dd..9f016b6 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 int8 and bool", + "operator + not defined on types int64 and bool", }, { "5 + true; 5;", - "operator + not defined on types int8 and bool", + "operator + not defined on types int64 and bool", }, { "-true", - "unknown operator: -bool", + "Operator - not defined on type bool", }, { "true + false;", @@ -104,7 +104,7 @@ func TestErrorHandling(t *testing.T) { }, { `[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}.deleteee(1, 3)`, - "Member deleteee does not exist on []int8", + "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 1133aa3..4efb4f1 100644 --- a/evaluator/tests/eval_integer_exp_test.go +++ b/evaluator/tests/eval_integer_exp_test.go @@ -26,8 +26,8 @@ func TestEvalIntegerExpression(t *testing.T) { {"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(62)}, - {"1 << 63 >> 1", big.NewInt(-(1 << 63 >> 1))}, + {"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)}, diff --git a/evaluator/tests/hash_literals_test.go b/evaluator/tests/hash_literals_test.go index 41a6321..035fa8d 100644 --- a/evaluator/tests/hash_literals_test.go +++ b/evaluator/tests/hash_literals_test.go @@ -26,12 +26,12 @@ func TestHashLiterals(t *testing.T) { } expected := map[object.HashKey]int64{ - (&object.String{Value: "one"}).HashKey(): 1, - (&object.String{Value: "two"}).HashKey(): 2, - (&object.String{Value: "three"}).HashKey(): 3, - (&object.Number[int64]{Value: 4}).HashKey(): 4, - evaluator.TRUE.HashKey(): 5, - evaluator.FALSE.HashKey(): 6, + (&object.String{Value: "one"}).HashKey(): 1, + (&object.String{Value: "two"}).HashKey(): 2, + (&object.String{Value: "three"}).HashKey(): 3, + (&object.Number{Value: 4, Kind: object.Int64Kind}).HashKey(): 4, + evaluator.TRUE.HashKey(): 5, + evaluator.FALSE.HashKey(): 6, } if len(result.Pairs) != len(expected) { diff --git a/evaluator/tests/index_operator_exp_test.go b/evaluator/tests/index_operator_exp_test.go index 5724470..6bb94cd 100644 --- a/evaluator/tests/index_operator_exp_test.go +++ b/evaluator/tests/index_operator_exp_test.go @@ -109,7 +109,7 @@ func TestHashIndexExpressions(t *testing.T) { big.NewInt(3), }, { - "let a = {1: 2}; b := 1; a[b] = b; a[1]", + "let a = map{ uint64 -> uint64 }{1: 2}; b := 1; a[b] = b; a[1]", big.NewInt(1), }, } diff --git a/evaluator/tests/type_builtins_test.go b/evaluator/tests/type_builtins_test.go index c27cfdb..96c1a2e 100644 --- a/evaluator/tests/type_builtins_test.go +++ b/evaluator/tests/type_builtins_test.go @@ -21,8 +21,8 @@ func TestTypeBuiltins(t *testing.T) { {"let arr = []int{1, 2, 3}; arr.push(4).size()", big.NewInt(4)}, {"[]int{}.push(1).size();", big.NewInt(1)}, {"[]int{1, 2, 3}.delete(1, 5).size();", big.NewInt(1)}, - {`[]int{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10"}.delete(1, 3);`, []string{"1", "5", "6", "7", "8", "9", "10"}}, - {`[]int{}.pushMultiple("0", 10)`, []string{"0", "0", "0", "0", "0", "0", "0", "0", "0", "0"}}, + {`[]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10"}.delete(1, 3);`, []string{"1", "5", "6", "7", "8", "9", "10"}}, + {`[]string{}.pushMultiple("0", 10)`, []string{"0", "0", "0", "0", "0", "0", "0", "0", "0", "0"}}, {`[]int{1, 2, 3}.pushMultiple(0, 10).size()`, big.NewInt(13)}, {`[]int{1, 2, 3}.slice(0, 3).size()`, big.NewInt(3)}, {`[]int{1, 2, 3}.slice(0, 2).size()`, big.NewInt(2)}, diff --git a/evaluator/tests/typed_declaration_statement_test.go b/evaluator/tests/typed_declaration_statement_test.go index 47eef63..3e6e393 100644 --- a/evaluator/tests/typed_declaration_statement_test.go +++ b/evaluator/tests/typed_declaration_statement_test.go @@ -14,7 +14,7 @@ func TestTypedDeclarationStatement(t *testing.T) { }{ {"int64 a = 5; a;", big.NewInt(5)}, {"string a = \"testmmm\"; a;", "testmmm"}, - {"const []int8 a = []int{1, 2, 3, 4}; let b = a; b;", []string{"1", "2", "3", "4"}}, + {"const []int8 a = []int8{1, 2, 3, 4}; let b = a; b;", []string{"1", "2", "3", "4"}}, {"bool a = true; let b = !a; b;", false}, {"const function(int64, int64)->int64 sum = fn(a: int64, b: int64) -> int64 { return a + b; }; sum", ExpectedFunction{ "fn(a, b) {" + "\n" + diff --git a/evaluator/tests/util.go b/evaluator/tests/util.go index 92237ae..176a95b 100644 --- a/evaluator/tests/util.go +++ b/evaluator/tests/util.go @@ -48,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 @@ -64,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 diff --git a/evaluator/type_cast.go b/evaluator/type_cast.go index d609756..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,90 +12,6 @@ const ( EXPLICIT_CAST = false ) -type CastRuleSignature struct { - from string - to string -} - -type CastRule struct { - allowImplicit bool - cast func(object.Object) object.Object -} - -var castRules = map[CastRuleSignature]CastRule{ - {object.Int8Kind.Signature(), object.Int16Kind.Signature()}: {true, numberCast[int8, int16]}, - {object.Int8Kind.Signature(), object.Int32Kind.Signature()}: {true, numberCast[int8, int32]}, - {object.Int8Kind.Signature(), object.Int64Kind.Signature()}: {true, numberCast[int8, int64]}, - {object.Int16Kind.Signature(), object.Int32Kind.Signature()}: {true, numberCast[int16, int32]}, - {object.Int16Kind.Signature(), object.Int64Kind.Signature()}: {true, numberCast[int16, int64]}, - {object.Int32Kind.Signature(), object.Int64Kind.Signature()}: {true, numberCast[int32, int64]}, - - {object.Int8Kind.Signature(), object.Float32Kind.Signature()}: {true, numberCast[int8, float32]}, - {object.Int16Kind.Signature(), object.Float32Kind.Signature()}: {true, numberCast[int16, float32]}, - {object.Int32Kind.Signature(), object.Float32Kind.Signature()}: {true, numberCast[int32, float32]}, - {object.Int64Kind.Signature(), object.Float32Kind.Signature()}: {true, numberCast[int64, float32]}, - - {object.Int8Kind.Signature(), object.Float64Kind.Signature()}: {true, numberCast[int8, float64]}, - {object.Int16Kind.Signature(), object.Float64Kind.Signature()}: {true, numberCast[int16, float64]}, - {object.Int32Kind.Signature(), object.Float64Kind.Signature()}: {true, numberCast[int32, float64]}, - {object.Int64Kind.Signature(), object.Float64Kind.Signature()}: {true, numberCast[int64, float64]}, - - {object.Float32Kind.Signature(), object.Float64Kind.Signature()}: {true, numberCast[float32, float64]}, - {"{string -> string}", "{int -> string}"}: {true, castEmptyMap}, - {"int[]", "string[]"}: {true, castEmptyArray}, - {"any[]", "int[]"}: {true, castEmptyArray}, -} - -func typeCast(obj object.Object, targetType object.ObjectType, implicit bool) object.Object { - - fromSignature := obj.Type().Signature() - toSignature := targetType.Signature() - castRuleSignature := CastRuleSignature{fromSignature, toSignature} - - if fromSignature == toSignature { - return obj - } - - castRule, castRuleExists := castRules[castRuleSignature] - if !castRuleExists { - return newError("No rule to cast between %s and %s is defined", fromSignature, toSignature) - } - - if implicit && !castRule.allowImplicit { - return newError("Implicit cast not allowed between %s and %s", fromSignature, toSignature) - } - - return castRule.cast(obj) -} - -func intToString(obj object.Object) object.Object { - integer := object.UnwrapReferenceObject(obj).(*object.Number[int64]) - return &object.String{Value: strconv.FormatInt(integer.Value, 10)} -} - -func numberCast[ - F int8 | int16 | int32 | int64 | uint8 | uint16 | uint32 | uint64 | float32 | float64, - T int8 | int16 | int32 | int64 | uint8 | uint16 | uint32 | uint64 | float32 | float64](obj object.Object) object.Object { - val := object.UnwrapReferenceObject(obj).(*object.Number[F]) - return &object.Number[T]{Value: T(val.Value)} -} - -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") - } - return &object.Hash{Pairs: hash.Pairs, HashObjectType: object.HashObjectType{KeyType: object.Int64Kind, ValueType: object.Int64Kind}} -} - -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") - } - return &object.Array{Elements: array.Elements, ArrayObjectType: object.ArrayObjectType{ElementType: object.StringKind}} -} - const ( _ uint8 = iota INT8_WEIGHT @@ -122,29 +39,125 @@ var numberCastWeight = map[object.ObjectKind]uint8{ object.Float64Kind: FLOAT64_WEIGHT, } -func castToLargerNumberType(nums ...object.Object) []object.Object { - if len(nums) == 0 { - return []object.Object{} - } +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, +} - kindToCastTo := object.UnwrapReferenceObject(nums[0]).Type().Kind() +func typeCast(obj object.Object, targetType object.ObjectType, castType bool) object.Object { + if obj.Type().Signature() == targetType.Signature() { + return obj + } - for _, num := range nums { - currKind := object.UnwrapReferenceObject(num).Type().Kind() - if numberCastWeight[currKind] > numberCastWeight[kindToCastTo] { - kindToCastTo = currKind + 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 + } + + if object.IsNumber(obj) && targetType.Kind() == object.StringKind { + return &object.String{Value: obj.Inspect()} + } + + return newError("Type cast from %s to %s is not defined", obj.Type().Signature(), targetType.Signature()) +} + +func numberCast(number *object.Number, target object.ObjectKind, castType bool) (*object.Number, error) { + + if number.Kind == target { + return number, nil } - resultNums := []object.Object{} + 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()) + } + 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 - for _, num := range nums { - result := typeCast(object.UnwrapReferenceObject(num), kindToCastTo, true) - if object.IsError(result) { - println(result.Inspect()) + 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()) } - resultNums = append(resultNums, result) + + } 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())) + } + } - return resultNums + return &object.Number{Value: value, Kind: target}, nil +} + +func arithmeticCast(first, second *object.Number) (*object.Number, *object.Number, error) { + + if first.Type().Kind() == second.Type().Kind() { + return first, second, nil + } + + 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 + } } diff --git a/evaluator/util.go b/evaluator/util.go index c69a2dd..1ffbd2a 100644 --- a/evaluator/util.go +++ b/evaluator/util.go @@ -37,55 +37,35 @@ func evalBangOperatorExpression(right object.Object) object.Object { } func evalMinusPrefixOperatorExpression(right object.Object) object.Object { - switch right.Type() { - case object.Int8Kind: - return &object.Number[int8]{Value: -right.(*object.Number[int8]).Value} - case object.Int16Kind: - return &object.Number[int16]{Value: -right.(*object.Number[int16]).Value} - case object.Int32Kind: - return &object.Number[int32]{Value: -right.(*object.Number[int32]).Value} - case object.Int64Kind: - return &object.Number[int64]{Value: -right.(*object.Number[int64]).Value} - case object.UInt8Kind: - return &object.Number[uint8]{Value: -right.(*object.Number[uint8]).Value} - case object.UInt16Kind: - return &object.Number[uint16]{Value: -right.(*object.Number[uint16]).Value} - case object.UInt32Kind: - return &object.Number[uint32]{Value: -right.(*object.Number[uint32]).Value} - case object.UInt64Kind: - return &object.Number[uint64]{Value: -right.(*object.Number[uint64]).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.Int8Kind: - return &object.Number[int8]{Value: ^right.(*object.Number[int8]).Value} - case object.Int16Kind: - return &object.Number[int16]{Value: ^right.(*object.Number[int16]).Value} - case object.Int32Kind: - return &object.Number[int32]{Value: ^right.(*object.Number[int32]).Value} - case object.Int64Kind: - return &object.Number[int64]{Value: ^right.(*object.Number[int64]).Value} - case object.UInt8Kind: - return &object.Number[uint8]{Value: ^right.(*object.Number[uint8]).Value} - case object.UInt16Kind: - return &object.Number[uint16]{Value: ^right.(*object.Number[uint16]).Value} - case object.UInt32Kind: - return &object.Number[uint32]{Value: ^right.(*object.Number[uint32]).Value} - case object.UInt64Kind: - return &object.Number[uint64]{Value: ^right.(*object.Number[uint64]).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 { @@ -152,8 +132,11 @@ func evalExpressions( } 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()) } @@ -178,7 +161,7 @@ func evalIndexExpression(left, index object.Object) object.Object { func evalArrayIndexExpression(array, index object.Object) object.Object { arrayObject := array.(*object.Array) - idx := typeCast(index, object.Int64Kind, true).(*object.Number[int64]).Value + idx := typeCast(index, object.Int64Kind, true).(*object.Number).GetInt64() max := int64(len(arrayObject.Elements) - 1) if idx < 0 || idx > max { @@ -190,28 +173,27 @@ func evalArrayIndexExpression(array, index object.Object) object.Object { func evalArrayLiteral(node *ast.ArrayLiteral, env *object.Environment) object.Object { elements := evalExpressions(node.Elements, env) - var elementType object.ObjectType = object.AnyKind + castedElements := []object.Object{} + elementType, err := evalType(node.Type.(ast.ArrayType).ElementType, env) + if err != nil { + return newError(err.Error()) + } if len(elements) == 1 && object.IsError(elements[0]) { return elements[0] } if len(elements) > 0 { - elementType = elements[0].Type() for _, element := range elements { - if object.IsNumber(element) && object.IsNumberKind(elementType.Kind()) { - continue + castedElement := typeCast(element, elementType, EXPLICIT_CAST) + if object.IsError(castedElement) { + return castedElement } - if !object.TypesMatch(elementType, element.Type()) { - return newError("Array literal cannot contain mixed element types") - } - } + castedElements = append(castedElements, castedElement) - if object.IsNumberKind(elementType.Kind()) { - elements = castToLargerNumberType(elements...) } } @@ -330,7 +312,7 @@ func evalDeclarationStatement(declNode *ast.DeclarationStatement, env *object.En } if expectedType != nil { - cast := typeCast(val, expectedType, IMPLICIT_CAST) + cast := typeCast(val, expectedType, EXPLICIT_CAST) if !object.IsError(cast) { val = cast } @@ -434,25 +416,38 @@ func evalTernaryExpression(node *ast.TernaryExpression, env *object.Environment) } 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 node.Value.Cmp(big.NewInt(math.MaxInt8)) == -1: - return &object.Number[int8]{Value: int8(node.Value.Int64())} - case node.Value.Cmp(big.NewInt(math.MaxUint8)) == -1: - return &object.Number[uint8]{Value: uint8(node.Value.Int64())} - case node.Value.Cmp(big.NewInt(math.MaxInt16)) == -1: - return &object.Number[int16]{Value: int16(node.Value.Int64())} - case node.Value.Cmp(big.NewInt(math.MaxUint16)) == -1: - return &object.Number[uint16]{Value: uint16(node.Value.Int64())} - case node.Value.Cmp(big.NewInt(math.MaxInt32)) == -1: - return &object.Number[int32]{Value: int32(node.Value.Int64())} - case node.Value.Cmp(big.NewInt(math.MaxUint32)) == -1: - return &object.Number[uint32]{Value: uint32(node.Value.Int64())} - case node.Value.Cmp(big.NewInt(math.MaxInt64)) == -1: - return &object.Number[int64]{Value: int64(node.Value.Int64())} - case node.Value.Cmp(new(big.Int).SetUint64(math.MaxUint64)) == -1: - return &object.Number[uint64]{Value: uint64(node.Value.Uint64())} + 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 newError("Integer out of max range") + return object.ErrorKind, fmt.Errorf("Integer ouside maximum range") } } diff --git a/object/constants.go b/object/constants.go index 894e347..6176b34 100644 --- a/object/constants.go +++ b/object/constants.go @@ -1,9 +1,5 @@ package object -import ( - "strconv" -) - type ObjectKind string var intrinsicTypeFunctionRepositories = initIntrinsicTypeBuiltins() @@ -40,17 +36,20 @@ const ( func initIntrinsicTypeBuiltins() map[ObjectKind]*FunctionRepository { repos := map[ObjectKind]*FunctionRepository{} - repos[Int64Kind] = 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 } @@ -74,19 +73,19 @@ func initArrayBuiltins() *FunctionRepository { 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 { @@ -99,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 { @@ -119,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) } @@ -133,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) @@ -142,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 eaa0403..95c74db 100644 --- a/object/environment.go +++ b/object/environment.go @@ -15,6 +15,7 @@ type Environment struct { var GLOBAL_TYPES = map[ObjectKind]ObjectKind{ "char": Int8Kind, + "int": Int64Kind, Int8Kind: Int8Kind, Int16Kind: Int16Kind, Int32Kind: Int32Kind, diff --git a/object/hash_key.go b/object/hash_key.go index 44dce51..b97fa13 100644 --- a/object/hash_key.go +++ b/object/hash_key.go @@ -12,7 +12,7 @@ func (b *Boolean) HashKey() HashKey { } } -func (i Number[T]) HashKey() HashKey { +func (i Number) HashKey() HashKey { return HashKey(i.Value) } diff --git a/object/number.go b/object/number.go index ad5836d..696df7c 100644 --- a/object/number.go +++ b/object/number.go @@ -2,57 +2,86 @@ package object import ( "fmt" + "math" + "unsafe" ) -type Number[V int8 | int16 | int32 | int64 | uint8 | uint16 | uint32 | uint64 | 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 float32: - return fmt.Sprintf("%gf", val) - case float64: - return fmt.Sprintf("%g", val) - default: - return fmt.Sprintf("%d", val) +func (n *Number) Inspect() string { + if IsFloat32(n) { + return fmt.Sprintf("%gf", n.GetFloat32()) } + 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 int8: - return Int8Kind - case int16: - return Int16Kind - case int32: - return Int32Kind - case int64: - return Int64Kind - case uint8: - return UInt8Kind - case uint16: - return UInt16Kind - case uint32: - return UInt32Kind - case uint64: - return UInt64Kind - 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 Int64Kind: - 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, } From 69261be584224542efec5da21e178aeeb5e27ec9 Mon Sep 17 00:00:00 2001 From: DustTheory Date: Tue, 31 Oct 2023 13:07:54 +0100 Subject: [PATCH 4/5] resolve merge issues --- evaluator/tests/error_handling_test.go | 8 ++------ evaluator/tests/index_operator_exp_test.go | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/evaluator/tests/error_handling_test.go b/evaluator/tests/error_handling_test.go index 0539ee2..4ef7447 100644 --- a/evaluator/tests/error_handling_test.go +++ b/evaluator/tests/error_handling_test.go @@ -54,13 +54,9 @@ func TestErrorHandling(t *testing.T) { `"Hello" - "World"`, "operator - not defined on types string and string", }, - { - `{"name": "Monkey"}[fn(x:string)->string { x }];`, - "unusable as hash key: function(string) -> string", - }, { `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;`, @@ -104,7 +100,7 @@ func TestErrorHandling(t *testing.T) { }, { `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/index_operator_exp_test.go b/evaluator/tests/index_operator_exp_test.go index 6f65f66..0327b3d 100644 --- a/evaluator/tests/index_operator_exp_test.go +++ b/evaluator/tests/index_operator_exp_test.go @@ -109,7 +109,7 @@ func TestHashIndexExpressions(t *testing.T) { big.NewInt(3), }, { - "let a = map{ uint64 -> uint64 }new map{string->int}{1: 2}; b := 1; a[b] = b; a[1]", + "let a = new map{string->int}{1: 2}; b := 1; a[b] = b; a[1]", big.NewInt(1), }, } From 9c95e24f69735037505220ce2ab68bf0a0c43316 Mon Sep 17 00:00:00 2001 From: DustTheory Date: Tue, 31 Oct 2023 13:09:43 +0100 Subject: [PATCH 5/5] removed settings json --- .vscode/settings.json | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index afb940f..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "discord.enabled": true -} \ No newline at end of file