diff --git a/phasm/compiler.py b/phasm/compiler.py index 103f580..71ee949 100644 --- a/phasm/compiler.py +++ b/phasm/compiler.py @@ -63,6 +63,11 @@ def type3(inp: type3types.Type3OrPlaceholder) -> wasm.WasmType: if inp is type3types.f64: return wasm.WasmTypeFloat64() + if inp is type3types.bytes: + # bytes are passed as pointer + # And pointers are i32 + return wasm.WasmTypeInt32() + if isinstance(inp, type3types.StructType3): # Structs and tuples are passed as pointer # And pointers are i32 @@ -273,13 +278,13 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None: wgn.add_statement(f'f64.{inp.operator}') return - # TODO: Broken after new type system - # if isinstance(inp.type, typing.TypeInt32): - # if inp.operator == 'len': - # if isinstance(inp.right.type, typing.TypeBytes): - # wgn.i32.load() - # return + if inp.type3 is type3types.u32: + if inp.operator == 'len': + if inp.right.type3 is type3types.bytes: + wgn.i32.load() + return + # TODO: Broken after new type system # if inp.operator == 'cast': # if isinstance(inp.type, typing.TypeUInt32) and isinstance(inp.right.type, typing.TypeUInt8): # # Nothing to do, you can use an u8 value as a u32 no problem diff --git a/phasm/type3/constraints.py b/phasm/type3/constraints.py index 4f66018..c097771 100644 --- a/phasm/type3/constraints.py +++ b/phasm/type3/constraints.py @@ -154,6 +154,17 @@ class MustImplementTypeClassConstraint(ConstraintBase): type_class3: str type3: types.Type3OrPlaceholder + DATA = { + 'u8': {'BitWiseOperation', 'BasicMathOperation'}, + 'u32': {'BitWiseOperation', 'BasicMathOperation'}, + 'u64': {'BitWiseOperation', 'BasicMathOperation'}, + 'i32': {'BasicMathOperation'}, + 'i64': {'BasicMathOperation'}, + 'bytes': {'Sized'}, + 'f32': {'BasicMathOperation', 'FloatingPoint'}, + 'f64': {'BasicMathOperation', 'FloatingPoint'}, + } + def __init__(self, type_class3: str, type3: types.Type3OrPlaceholder, comment: Optional[str] = None) -> None: super().__init__(comment=comment) @@ -168,14 +179,7 @@ class MustImplementTypeClassConstraint(ConstraintBase): if isinstance(typ, types.PlaceholderForType): return RequireTypeSubstitutes() - if 'BitWiseOperation' == self.type_class3 and (typ is types.u8 or typ is types.u32 or typ is types.u64): - return None - - if 'BasicMathOperation' == self.type_class3 and ( - typ is types.u8 or typ is types.u32 or typ is types.u64 - or typ is types.i32 or typ is types.i64 - or typ is types.f32 or typ is types.f64 - ): + if self.type_class3 in self.__class__.DATA.get(typ.name, set()): return None return Error(f'{typ.name} does not implement the {self.type_class3} type class') diff --git a/phasm/type3/constraintsgenerator.py b/phasm/type3/constraintsgenerator.py index 1adb51b..2878bd1 100644 --- a/phasm/type3/constraintsgenerator.py +++ b/phasm/type3/constraintsgenerator.py @@ -15,6 +15,8 @@ from .constraints import ( LiteralFitsConstraint, MustImplementTypeClassConstraint, SameTypeConstraint, ) +from . import types as type3types + def phasm_type3_generate_constraints(inp: ourlang.Module) -> List[ConstraintBase]: ctx = Context() @@ -37,6 +39,21 @@ def expression(ctx: Context, inp: ourlang.Expression) -> Generator[ConstraintBas comment=f'typeOf("{inp.variable.name}") == typeOf({inp.variable.name})') return + if isinstance(inp, ourlang.UnaryOp): + if 'len' == inp.operator: + yield from expression(ctx, inp.right) + yield MustImplementTypeClassConstraint('Sized', inp.right.type3) + yield SameTypeConstraint(type3types.u32, inp.type3, comment='len :: Sized a => a -> u32') + return + + if 'sqrt' == inp.operator: + yield from expression(ctx, inp.right) + yield MustImplementTypeClassConstraint('FloatingPoint', inp.right.type3) + yield SameTypeConstraint(inp.right.type3, inp.type3, comment='sqrt :: FloatingPoint a => a -> a') + return + + raise NotImplementedError(expression, inp, inp.operator) + if isinstance(inp, ourlang.BinaryOp): if inp.operator in ('|', '&', '^', ): yield from expression(ctx, inp.left) @@ -47,6 +64,17 @@ def expression(ctx: Context, inp: ourlang.Expression) -> Generator[ConstraintBas comment=f'({inp.operator}) :: a -> a -> a') return + if inp.operator in ('>>', '<<', ): + yield from expression(ctx, inp.left) + yield from expression(ctx, inp.right) + + yield MustImplementTypeClassConstraint('BitWiseOperation', inp.left.type3) + yield SameTypeConstraint(inp.left.type3, inp.type3, + comment=f'({inp.operator}) :: a -> u64 -> a') + yield SameTypeConstraint(type3types.u64, inp.right.type3, + comment=f'({inp.operator}) :: a -> u64 -> a') + return + if inp.operator in ('+', '-', '*', '/', ): yield from expression(ctx, inp.left) yield from expression(ctx, inp.right) diff --git a/phasm/type3/entry.py b/phasm/type3/entry.py index 8f77634..c1b68e3 100644 --- a/phasm/type3/entry.py +++ b/phasm/type3/entry.py @@ -76,6 +76,9 @@ def phasm_type3(inp: ourlang.Module, verbose: bool = False) -> None: new_placeholder_substitutes_len = len(placeholder_substitutes) if old_constraint_ids == new_constraint_ids and old_placeholder_substitutes_len == new_placeholder_substitutes_len: + if error_list: + raise Type3Exception(error_list) + raise Exception('Cannot type this program - not enough information') constraint_list = new_constraint_list diff --git a/phasm/type3/types.py b/phasm/type3/types.py index 7ff91e1..a91cab1 100644 --- a/phasm/type3/types.py +++ b/phasm/type3/types.py @@ -215,6 +215,11 @@ f64 = Type3('f64') A 32-bits IEEE 754 float, of 64 bits width. """ +bytes = Type3('bytes') +""" +This is a runtime-determined length piece of memory that can be indexed at runtime. +""" + static_array = Type3('static_array') """ This is a fixed length piece of memory that can be indexed at runtime. @@ -241,4 +246,5 @@ LOOKUP_TABLE: Dict[str, Type3] = { 'i64': i64, 'f32': f32, 'f64': f64, + 'bytes': bytes, } diff --git a/tests/integration/test_lang/test_bytes.py b/tests/integration/test_lang/test_bytes.py index 45fc2a1..6df111a 100644 --- a/tests/integration/test_lang/test_bytes.py +++ b/tests/integration/test_lang/test_bytes.py @@ -20,13 +20,13 @@ def testEntry(f: bytes) -> bytes: def test_bytes_length(): code_py = """ @exported -def testEntry(f: bytes) -> i32: +def testEntry(f: bytes) -> u32: return len(f) """ - result = Suite(code_py).run_code(b'This is another test') + result = Suite(code_py).run_code(b'This yet is another test') - assert 20 == result.returned_value + assert 24 == result.returned_value @pytest.mark.integration_test def test_bytes_index(): diff --git a/tests/integration/test_lang/test_primitives.py b/tests/integration/test_lang/test_primitives.py index ff2db0c..640a898 100644 --- a/tests/integration/test_lang/test_primitives.py +++ b/tests/integration/test_lang/test_primitives.py @@ -468,7 +468,7 @@ def testEntry() -> i32: return helper() """ - with pytest.raises(Type3Exception, match=r'i32.*i64'): + with pytest.raises(Type3Exception, match=r'i64 must be i32 instead'): Suite(code_py).run_code() @pytest.mark.integration_test diff --git a/tests/integration/test_lang/test_struct.py b/tests/integration/test_lang/test_struct.py index 35663c4..bb70323 100644 --- a/tests/integration/test_lang/test_struct.py +++ b/tests/integration/test_lang/test_struct.py @@ -99,5 +99,5 @@ def testEntry(arg: Struct) -> (i32, i32, ): return arg.param """ - with pytest.raises(Type3Exception, match=r'\(i32, i32, \) must be ' + type_ + ' instead'): + with pytest.raises(Type3Exception, match=type_ + r' must be tuple \(i32\) \(i32\) instead'): Suite(code_py).run_code() diff --git a/tests/integration/test_lang/test_tuple.py b/tests/integration/test_lang/test_tuple.py index fd74a72..a08b382 100644 --- a/tests/integration/test_lang/test_tuple.py +++ b/tests/integration/test_lang/test_tuple.py @@ -112,7 +112,7 @@ def test_tuple_constant_type_mismatch(): CONSTANT: (u32, u8, u8, ) = (24, 4000, 1, ) """ - with pytest.raises(Type3Exception, match='Must fit in 1 byte(s)'): + with pytest.raises(Type3Exception, match=r'Must fit in 1 byte\(s\)'): Suite(code_py).run_code() @pytest.mark.integration_test