diff --git a/phasm/compiler.py b/phasm/compiler.py index a029848..20b69bc 100644 --- a/phasm/compiler.py +++ b/phasm/compiler.py @@ -273,6 +273,20 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None: assert isinstance(inp.type3, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR # FIXME: Re-implement build-in operators + # Maybe operator_annotation is the way to go + # Maybe the older stuff below that is the way to go + + operator_annotation = f'({inp.operator}) :: {inp.left.type3:s} -> {inp.right.type3:s} -> {inp.type3:s}' + if operator_annotation == '(>) :: i32 -> i32 -> bool': + wgn.add_statement('i32.gt_s') + return + + if operator_annotation == '(<) :: u64 -> u64 -> bool': + wgn.add_statement('i64.lt_u') + return + if operator_annotation == '(==) :: u64 -> u64 -> bool': + wgn.add_statement('i64.eq') + return if inp.type3 == type3types.u8: if operator := U8_OPERATOR_MAP.get(inp.operator, None): @@ -321,7 +335,7 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None: wgn.add_statement(f'f64.{operator}') return - raise NotImplementedError(expression, inp.type3, inp.operator) + raise NotImplementedError(expression, inp.operator, inp.left.type3, inp.right.type3, inp.type3) if isinstance(inp, ourlang.UnaryOp): expression(wgn, inp.right) diff --git a/phasm/ourlang.py b/phasm/ourlang.py index af3d581..01430b9 100644 --- a/phasm/ourlang.py +++ b/phasm/ourlang.py @@ -123,6 +123,9 @@ class BinaryOp(Expression): self.left = left self.right = right + def __repr__(self) -> str: + return f'BinaryOp({repr(self.operator)}, {repr(self.left)}, {repr(self.right)})' + class FunctionCall(Expression): """ A function call expression within a statement diff --git a/phasm/type3/constraints.py b/phasm/type3/constraints.py index 2becba2..c1712ed 100644 --- a/phasm/type3/constraints.py +++ b/phasm/type3/constraints.py @@ -231,11 +231,11 @@ class MustImplementTypeClassConstraint(ConstraintBase): type3: types.Type3OrPlaceholder DATA = { - 'u8': {'BitWiseOperation', 'BasicMathOperation'}, - 'u32': {'BitWiseOperation', 'BasicMathOperation'}, - 'u64': {'BitWiseOperation', 'BasicMathOperation'}, - 'i32': {'BasicMathOperation'}, - 'i64': {'BasicMathOperation'}, + 'u8': {'BitWiseOperation', 'BasicMathOperation', 'EqualComparison', 'StrictPartialOrder'}, + 'u32': {'BitWiseOperation', 'BasicMathOperation', 'EqualComparison', 'StrictPartialOrder'}, + 'u64': {'BitWiseOperation', 'BasicMathOperation', 'EqualComparison', 'StrictPartialOrder'}, + 'i32': {'BasicMathOperation', 'EqualComparison', 'StrictPartialOrder'}, + 'i64': {'BasicMathOperation', 'EqualComparison', 'StrictPartialOrder'}, 'bytes': {'Foldable', 'Sized'}, 'f32': {'BasicMathOperation', 'FloatingPoint'}, 'f64': {'BasicMathOperation', 'FloatingPoint'}, diff --git a/phasm/type3/constraintsgenerator.py b/phasm/type3/constraintsgenerator.py index ad620e9..61e7ec9 100644 --- a/phasm/type3/constraintsgenerator.py +++ b/phasm/type3/constraintsgenerator.py @@ -17,19 +17,21 @@ from .constraints import ( from . import types as type3types +ConstraintGenerator = Generator[ConstraintBase, None, None] + def phasm_type3_generate_constraints(inp: ourlang.Module) -> List[ConstraintBase]: ctx = Context() return [*module(ctx, inp)] -def constant(ctx: Context, inp: ourlang.Constant) -> Generator[ConstraintBase, None, None]: +def constant(ctx: Context, inp: ourlang.Constant) -> ConstraintGenerator: if isinstance(inp, (ourlang.ConstantPrimitive, ourlang.ConstantTuple, ourlang.ConstantStruct)): yield LiteralFitsConstraint(inp.type3, inp) return raise NotImplementedError(constant, inp) -def expression(ctx: Context, inp: ourlang.Expression) -> Generator[ConstraintBase, None, None]: +def expression(ctx: Context, inp: ourlang.Expression) -> ConstraintGenerator: if isinstance(inp, ourlang.Constant): yield from constant(ctx, inp) return @@ -87,6 +89,28 @@ def expression(ctx: Context, inp: ourlang.Expression) -> Generator[ConstraintBas comment=f'({inp.operator}) :: a -> a -> a') return + if inp.operator == '==': + yield from expression(ctx, inp.left) + yield from expression(ctx, inp.right) + + yield MustImplementTypeClassConstraint('EqualComparison', inp.left.type3) + yield SameTypeConstraint(inp.left.type3, inp.right.type3, + comment=f'({inp.operator}) :: a -> a -> bool') + yield SameTypeConstraint(inp.type3, type3types.bool_, + comment=f'({inp.operator}) :: a -> a -> bool') + return + + if inp.operator in ('<', '>'): + yield from expression(ctx, inp.left) + yield from expression(ctx, inp.right) + + yield MustImplementTypeClassConstraint('StrictPartialOrder', inp.left.type3) + yield SameTypeConstraint(inp.left.type3, inp.right.type3, + comment=f'({inp.operator}) :: a -> a -> bool') + yield SameTypeConstraint(inp.type3, type3types.bool_, + comment=f'({inp.operator}) :: a -> a -> bool') + return + raise NotImplementedError(expression, inp) if isinstance(inp, ourlang.FunctionCall): @@ -141,26 +165,50 @@ def expression(ctx: Context, inp: ourlang.Expression) -> Generator[ConstraintBas raise NotImplementedError(expression, inp) -def function(ctx: Context, inp: ourlang.Function) -> Generator[ConstraintBase, None, None]: +def statement_return(ctx: Context, fun: ourlang.Function, inp: ourlang.StatementReturn) -> ConstraintGenerator: + yield from expression(ctx, inp.value) + + yield SameTypeConstraint(fun.returns_type3, inp.value.type3, + comment=f'The type of the value returned from function {fun.name} should match its return type') + +def statement_if(ctx: Context, fun: ourlang.Function, inp: ourlang.StatementIf) -> ConstraintGenerator: + yield from expression(ctx, inp.test) + + yield SameTypeConstraint(inp.test.type3, type3types.bool_, + comment=f'Must pass a boolean expression to if') + + for stmt in inp.statements: + yield from statement(ctx, fun, stmt) + + for stmt in inp.else_statements: + yield from statement(ctx, fun, stmt) + +def statement(ctx: Context, fun: ourlang.Function, inp: ourlang.Statement) -> ConstraintGenerator: + if isinstance(inp, ourlang.StatementReturn): + yield from statement_return(ctx, fun, inp) + return + + if isinstance(inp, ourlang.StatementIf): + yield from statement_if(ctx, fun, inp) + return + + raise NotImplementedError(statement, fun, inp) + +def function(ctx: Context, inp: ourlang.Function) -> ConstraintGenerator: assert not inp.imported if isinstance(inp, ourlang.StructConstructor): return - if len(inp.statements) != 1 or not isinstance(inp.statements[0], ourlang.StatementReturn): - raise NotImplementedError('Functions with not just a return statement') + for stmt in inp.statements: + yield from statement(ctx, inp, stmt) - yield from expression(ctx, inp.statements[0].value) - - yield SameTypeConstraint(inp.returns_type3, inp.statements[0].value.type3, - comment=f'The type of the value returned from function {inp.name} should match its return type') - -def module_constant_def(ctx: Context, inp: ourlang.ModuleConstantDef) -> Generator[ConstraintBase, None, None]: +def module_constant_def(ctx: Context, inp: ourlang.ModuleConstantDef) -> ConstraintGenerator: yield from constant(ctx, inp.constant) yield SameTypeConstraint(inp.type3, inp.constant.type3, comment=f'The type of the value for module constant definition {inp.name} should match the type of that constant') -def module(ctx: Context, inp: ourlang.Module) -> Generator[ConstraintBase, None, None]: +def module(ctx: Context, inp: ourlang.Module) -> ConstraintGenerator: for cdef in inp.constant_defs.values(): yield from module_constant_def(ctx, cdef) diff --git a/phasm/type3/types.py b/phasm/type3/types.py index a4f6597..ae598ef 100644 --- a/phasm/type3/types.py +++ b/phasm/type3/types.py @@ -202,6 +202,11 @@ none = PrimitiveType3('none') The none type, for when functions simply don't return anything. e.g., IO(). """ +bool_ = PrimitiveType3('bool') +""" +The bool type, either True or False +""" + u8 = PrimitiveType3('u8') """ The unsigned 8-bit integer type. @@ -280,6 +285,7 @@ determined length, and each argument can be different. LOOKUP_TABLE: Dict[str, Type3] = { 'none': none, + 'bool': bool_, 'u8': u8, 'u32': u32, 'u64': u64,