From 1afa3efc31e1d4a488a1a5f3cbb6711e6659ea0b Mon Sep 17 00:00:00 2001 From: "Johan B.W. de Vries" Date: Mon, 26 Dec 2022 13:11:36 +0100 Subject: [PATCH] Reimplements casting and foldl Probably not 100% type checked yet. Also, fixed logical right shift; standard says the type is different. --- phasm/compiler.py | 15 +++--- phasm/parser.py | 2 - phasm/type3/constraints.py | 46 ++++++++++++++++++- phasm/type3/constraintsgenerator.py | 23 ++++++++-- .../integration/test_lang/test_primitives.py | 4 +- 5 files changed, 72 insertions(+), 18 deletions(-) diff --git a/phasm/compiler.py b/phasm/compiler.py index a511d0e..219b680 100644 --- a/phasm/compiler.py +++ b/phasm/compiler.py @@ -297,11 +297,10 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None: 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 - # return + if inp.operator == 'cast': + if inp.type3 == type3types.u32 and inp.right.type3 == type3types.u8: + # Nothing to do, you can use an u8 value as a u32 no problem + return raise NotImplementedError(expression, inp.type3, inp.operator) @@ -397,10 +396,8 @@ def expression_fold(wgn: WasmGenerator, inp: ourlang.Fold) -> None: """ assert isinstance(inp.type3, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR - raise NotImplementedError('TODO: Broken after new type system') - - if inp.iter.type.__class__.__name__ != 'TypeBytes': - raise NotImplementedError(expression, inp, inp.iter.type) + if inp.iter.type3 is not type3types.bytes: + raise NotImplementedError(expression_fold, inp, inp.iter.type3) wgn.add_statement('nop', comment='acu :: u8') acu_var = wgn.temp_var_u8(f'fold_{codestyle.type3(inp.type3)}_acu') diff --git a/phasm/parser.py b/phasm/parser.py index ab7b327..fd06510 100644 --- a/phasm/parser.py +++ b/phasm/parser.py @@ -563,8 +563,6 @@ class OurVisitor: if 2 != len(func.posonlyargs): _raise_static_error(node, f'Function {node.func.id} requires a function with 2 arguments but a function with {len(func.posonlyargs)} args is given') - raise NotImplementedError('TODO: Broken after new type system') - return Fold( Fold.Direction.LEFT, func, diff --git a/phasm/type3/constraints.py b/phasm/type3/constraints.py index 6307d90..6999d64 100644 --- a/phasm/type3/constraints.py +++ b/phasm/type3/constraints.py @@ -147,6 +147,50 @@ class SameTypeConstraint(ConstraintBase): return f'SameTypeConstraint({args}, comment={repr(self.comment)})' +class CastableConstraint(ConstraintBase): + """ + A type can be cast to another type + """ + __slots__ = ('from_type3', 'to_type3', ) + + from_type3: types.Type3OrPlaceholder + to_type3: types.Type3OrPlaceholder + + def __init__(self, from_type3: types.Type3OrPlaceholder, to_type3: types.Type3OrPlaceholder, comment: Optional[str] = None) -> None: + super().__init__(comment=comment) + + self.from_type3 = from_type3 + self.to_type3 = to_type3 + + def check(self, smap: SubstitutionMap) -> CheckResult: + ftyp = self.from_type3 + if isinstance(ftyp, types.PlaceholderForType) and ftyp in smap: + ftyp = smap[ftyp] + + ttyp = self.to_type3 + if isinstance(ttyp, types.PlaceholderForType) and ttyp in smap: + ttyp = smap[ttyp] + + if isinstance(ftyp, types.PlaceholderForType) or isinstance(ttyp, types.PlaceholderForType): + return RequireTypeSubstitutes() + + if ftyp is types.u8 and ttyp is types.u32: + return None + + return Error(f'Cannot cast {ftyp.name} to {ttyp.name}') + + def human_readable(self) -> HumanReadableRet: + return ( + '{to_type3}({from_type3})', + { + 'to_type3': self.to_type3, + 'from_type3': self.from_type3, + }, + ) + + def __repr__(self) -> str: + return f'CastableConstraint({repr(self.from_type3)}, {repr(self.to_type3)}, comment={repr(self.comment)})' + class MustImplementTypeClassConstraint(ConstraintBase): """ A type must implement a given type class @@ -162,7 +206,7 @@ class MustImplementTypeClassConstraint(ConstraintBase): 'u64': {'BitWiseOperation', 'BasicMathOperation'}, 'i32': {'BasicMathOperation'}, 'i64': {'BasicMathOperation'}, - 'bytes': {'Sized'}, + 'bytes': {'Foldable', 'Sized'}, 'f32': {'BasicMathOperation', 'FloatingPoint'}, 'f64': {'BasicMathOperation', 'FloatingPoint'}, } diff --git a/phasm/type3/constraintsgenerator.py b/phasm/type3/constraintsgenerator.py index 2093303..36ffa3b 100644 --- a/phasm/type3/constraintsgenerator.py +++ b/phasm/type3/constraintsgenerator.py @@ -11,7 +11,7 @@ from .constraints import ( Context, ConstraintBase, - CanBeSubscriptedConstraint, + CastableConstraint, CanBeSubscriptedConstraint, LiteralFitsConstraint, MustImplementTypeClassConstraint, SameTypeConstraint, ) @@ -52,6 +52,11 @@ def expression(ctx: Context, inp: ourlang.Expression) -> Generator[ConstraintBas yield SameTypeConstraint(inp.right.type3, inp.type3, comment='sqrt :: FloatingPoint a => a -> a') return + if 'cast' == inp.operator: + yield from expression(ctx, inp.right) + yield CastableConstraint(inp.right.type3, inp.type3) + return + raise NotImplementedError(expression, inp, inp.operator) if isinstance(inp, ourlang.BinaryOp): @@ -69,10 +74,8 @@ def expression(ctx: Context, inp: ourlang.Expression) -> Generator[ConstraintBas 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') + yield SameTypeConstraint(inp.left.type3, inp.right.type3, inp.type3, + comment=f'({inp.operator}) :: a -> a -> a') return if inp.operator in ('+', '-', '*', '/', ): @@ -112,6 +115,16 @@ def expression(ctx: Context, inp: ourlang.Expression) -> Generator[ConstraintBas comment=f'The type of a struct member reference is the same as the type of struct member {inp.struct_type3.name}.{inp.member}') return + if isinstance(inp, ourlang.Fold): + yield from expression(ctx, inp.base) + yield from expression(ctx, inp.iter) + + yield SameTypeConstraint(inp.func.posonlyargs[0].type3, inp.func.returns_type3, inp.base.type3, inp.type3, + comment='foldl :: Foldable t => (b -> a -> b) -> b -> t a -> b') + yield MustImplementTypeClassConstraint('Foldable', inp.iter.type3) + + return + raise NotImplementedError(expression, inp) def function(ctx: Context, inp: ourlang.Function) -> Generator[ConstraintBase, None, None]: diff --git a/tests/integration/test_lang/test_primitives.py b/tests/integration/test_lang/test_primitives.py index 2c3565a..6e815a2 100644 --- a/tests/integration/test_lang/test_primitives.py +++ b/tests/integration/test_lang/test_primitives.py @@ -111,7 +111,9 @@ def testEntry() -> {type_}: return 10 >> 3 """ - result = Suite(code_py).run_code() + # Check with wasmtime, as other engines don't mind if the type + # doesn't match. They'll complain when: (>>) : u32 -> u64 -> u32 + result = Suite(code_py).run_code(runtime='wasmtime') assert 1 == result.returned_value assert TYPE_MAP[type_] == type(result.returned_value)