diff --git a/TODO.md b/TODO.md index c597228..a6b450c 100644 --- a/TODO.md +++ b/TODO.md @@ -23,9 +23,6 @@ - At first glance, looks like failure in the typing system - Related to the FIXME in phasm_type3? - Related: Parser is putting stuff in ModuleDataBlock -- WEBASSEMBLY_BUILTIN_BYTES_OPS is special cased - - Should be part of a prelude (?) - - In Haskell this is not a type class - Casting is not implemented except u32 which is special cased - Make prelude more an actual thing - Merge in type3types.LOOKUP_TABLE diff --git a/phasm/codestyle.py b/phasm/codestyle.py index 2386a85..d6f57bb 100644 --- a/phasm/codestyle.py +++ b/phasm/codestyle.py @@ -76,10 +76,6 @@ def expression(inp: ourlang.Expression) -> str: return str(inp.variable.name) if isinstance(inp, ourlang.UnaryOp): - if ( - inp.operator in ourlang.WEBASSEMBLY_BUILTIN_BYTES_OPS): - return f'{inp.operator}({expression(inp.right)})' - if inp.operator == 'cast': mtyp = type3(inp.type3) if mtyp is None: diff --git a/phasm/compiler.py b/phasm/compiler.py index b34dfa9..9d8cc0e 100644 --- a/phasm/compiler.py +++ b/phasm/compiler.py @@ -233,6 +233,9 @@ INSTANCES = { 'a=f32': stdlib_types.f32_natnum_arithmic_shift_right, 'a=f64': stdlib_types.f64_natnum_arithmic_shift_right, }, + prelude.Sized_.methods['len']: { + 'a=bytes': stdlib_types.bytes_sized_len, + }, } def phasm_compile(inp: ourlang.Module) -> wasm.Module: @@ -458,12 +461,6 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None: assert isinstance(inp.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR - if inp.type3 == prelude.u32: - if inp.operator == 'len': - if inp.right.type3 == prelude.bytes_: - wgn.i32.load() - return - if inp.operator == 'cast': if inp.type3 == prelude.u32 and inp.right.type3 == prelude.u8: # Nothing to do, you can use an u8 value as a u32 no problem diff --git a/phasm/ourlang.py b/phasm/ourlang.py index d92849c..77168df 100644 --- a/phasm/ourlang.py +++ b/phasm/ourlang.py @@ -4,14 +4,11 @@ Contains the syntax tree for ourlang import enum from typing import Dict, Iterable, List, Optional, Union -from typing_extensions import Final - from . import prelude from .type3.placeholders import PlaceholderForType, Type3OrPlaceholder from .type3.typeclasses import Type3ClassMethod from .type3.types import Type3 -WEBASSEMBLY_BUILTIN_BYTES_OPS: Final = ('len', ) class Expression: """ diff --git a/phasm/parser.py b/phasm/parser.py index dc644da..ced7d6d 100644 --- a/phasm/parser.py +++ b/phasm/parser.py @@ -490,14 +490,6 @@ class OurVisitor: ) unary_op.type3 = prelude.u32 return unary_op - elif node.func.id == 'len': - if 1 != len(node.args): - _raise_static_error(node, f'Function {node.func.id} requires 1 arguments but {len(node.args)} are given') - - return UnaryOp( - 'len', - self.visit_Module_FunctionDef_expr(module, function, our_locals, node.args[0]), - ) elif node.func.id == 'foldl': if 3 != len(node.args): _raise_static_error(node, f'Function {node.func.id} requires 3 arguments but {len(node.args)} are given') diff --git a/phasm/prelude/__init__.py b/phasm/prelude/__init__.py index 87e1aa1..06eda3c 100644 --- a/phasm/prelude/__init__.py +++ b/phasm/prelude/__init__.py @@ -241,6 +241,12 @@ Floating = Type3Class('Floating', [a], methods={ instance_type_class(Floating, f32) instance_type_class(Floating, f64) +Sized_ = Type3Class('Sized', [a], methods={ + 'len': [a, u32], +}, operators={}) # FIXME: Once we get type class families, add [] here + +instance_type_class(Sized_, bytes_) + PRELUDE_TYPE_CLASSES = { 'Eq': Eq, 'Ord': Ord, @@ -271,4 +277,5 @@ PRELUDE_METHODS = { **Integral.methods, **IntNum.methods, **NatNum.methods, + **Sized_.methods, } diff --git a/phasm/stdlib/types.py b/phasm/stdlib/types.py index b7192bb..8075930 100644 --- a/phasm/stdlib/types.py +++ b/phasm/stdlib/types.py @@ -858,3 +858,10 @@ def f32_intnum_neg(g: Generator) -> None: def f64_intnum_neg(g: Generator) -> None: g.f64.neg() + +## ### +## Class Sized + +def bytes_sized_len(g: Generator) -> None: + # The length is stored in the first 4 bytes + g.i32.load() diff --git a/phasm/type3/constraints.py b/phasm/type3/constraints.py index d2b791f..26de593 100644 --- a/phasm/type3/constraints.py +++ b/phasm/type3/constraints.py @@ -245,7 +245,7 @@ class MustImplementTypeClassConstraint(ConstraintBase): type3: placeholders.Type3OrPlaceholder DATA = { - 'bytes': {'Foldable', 'Sized'}, + 'bytes': {'Foldable'}, } def __init__(self, type_class3: Union[str, typeclasses.Type3Class], type3: placeholders.Type3OrPlaceholder, comment: Optional[str] = None) -> None: diff --git a/phasm/type3/constraintsgenerator.py b/phasm/type3/constraintsgenerator.py index 7fc5250..724993b 100644 --- a/phasm/type3/constraintsgenerator.py +++ b/phasm/type3/constraintsgenerator.py @@ -48,12 +48,6 @@ def expression(ctx: Context, inp: ourlang.Expression) -> ConstraintGenerator: return if isinstance(inp, ourlang.UnaryOp): - if 'len' == inp.operator: - yield from expression(ctx, inp.right) - yield MustImplementTypeClassConstraint('Sized', inp.right.type3) - yield SameTypeConstraint(prelude.u32, inp.type3, comment='len :: Sized a => a -> u32') - return - if 'cast' == inp.operator: yield from expression(ctx, inp.right) yield CastableConstraint(inp.right.type3, inp.type3) diff --git a/tests/integration/test_lang/test_bytes.py b/tests/integration/test_lang/test_bytes.py deleted file mode 100644 index 324fa62..0000000 --- a/tests/integration/test_lang/test_bytes.py +++ /dev/null @@ -1,16 +0,0 @@ -import pytest - -from ..helpers import Suite - - -@pytest.mark.integration_test -def test_bytes_length(): - code_py = """ -@exported -def testEntry(f: bytes) -> u32: - return len(f) -""" - - result = Suite(code_py).run_code(b'This yet is another test') - - assert 24 == result.returned_value diff --git a/tests/integration/test_lang/test_sized.py b/tests/integration/test_lang/test_sized.py new file mode 100644 index 0000000..ea2e9a8 --- /dev/null +++ b/tests/integration/test_lang/test_sized.py @@ -0,0 +1,20 @@ +import pytest + +from ..helpers import Suite + + +@pytest.mark.integration_test +@pytest.mark.parametrize('type_, in_put, exp_result', [ + ('bytes', b'Hello, world!', 13), + # ('u8[4]', (1, 2, 3, 4), 4), # FIXME: Implement this +]) +def test_len(type_, in_put, exp_result): + code_py = f""" +@exported +def testEntry(f: {type_}) -> u32: + return len(f) +""" + + result = Suite(code_py).run_code(in_put) + + assert exp_result == result.returned_value