From da381e4a4869ff56423dcb02bc4fc39139368622 Mon Sep 17 00:00:00 2001 From: "Johan B.W. de Vries" Date: Wed, 9 Apr 2025 13:16:50 +0200 Subject: [PATCH] Made Integral use Python's operators Rather than Haskell's div and rem methods, we use the Python operators since these are often used. And we have as goal to make it 'look like Python' --- phasm/compiler.py | 8 ++++- phasm/parser.py | 4 +++ phasm/stdlib/types.py | 12 +++++++ phasm/type3/typeclasses.py | 6 ++-- tests/integration/test_lang/test_integral.py | 36 +++++++++++++++++--- 5 files changed, 59 insertions(+), 7 deletions(-) diff --git a/phasm/compiler.py b/phasm/compiler.py index c38d180..d7d0cc8 100644 --- a/phasm/compiler.py +++ b/phasm/compiler.py @@ -133,12 +133,18 @@ INSTANCES = { 'a=f32': stdlib_types.f32_fractional_div, 'a=f64': stdlib_types.f64_fractional_div, }, - type3classes.Integral.methods['div']: { + type3classes.Integral.operators['//']: { 'a=u32': stdlib_types.u32_integral_div, 'a=u64': stdlib_types.u64_integral_div, 'a=i32': stdlib_types.i32_integral_div, 'a=i64': stdlib_types.i64_integral_div, }, + type3classes.Integral.operators['%']: { + 'a=u32': stdlib_types.u32_integral_rem, + 'a=u64': stdlib_types.u64_integral_rem, + 'a=i32': stdlib_types.i32_integral_rem, + 'a=i64': stdlib_types.i64_integral_rem, + }, type3classes.IntNum.methods['abs']: { 'a=i32': stdlib_types.i32_intnum_abs, 'a=i64': stdlib_types.i64_intnum_abs, diff --git a/phasm/parser.py b/phasm/parser.py index dfe0ae2..2e44f92 100644 --- a/phasm/parser.py +++ b/phasm/parser.py @@ -366,6 +366,10 @@ class OurVisitor: operator = '*' elif isinstance(node.op, ast.Div): operator = '/' + elif isinstance(node.op, ast.FloorDiv): + operator = '//' + elif isinstance(node.op, ast.Mod): + operator = '%' elif isinstance(node.op, ast.LShift): operator = '<<' elif isinstance(node.op, ast.RShift): diff --git a/phasm/stdlib/types.py b/phasm/stdlib/types.py index 186dfb7..7f85017 100644 --- a/phasm/stdlib/types.py +++ b/phasm/stdlib/types.py @@ -495,6 +495,18 @@ def i32_integral_div(g: Generator) -> None: def i64_integral_div(g: Generator) -> None: g.add_statement('i64.div_s') +def u32_integral_rem(g: Generator) -> None: + g.add_statement('i32.rem_u') + +def u64_integral_rem(g: Generator) -> None: + g.add_statement('i64.rem_u') + +def i32_integral_rem(g: Generator) -> None: + g.add_statement('i32.rem_s') + +def i64_integral_rem(g: Generator) -> None: + g.add_statement('i64.rem_s') + ## ### ## class NatNum diff --git a/phasm/type3/typeclasses.py b/phasm/type3/typeclasses.py index 638e5fb..eae6c6e 100644 --- a/phasm/type3/typeclasses.py +++ b/phasm/type3/typeclasses.py @@ -120,8 +120,10 @@ IntNum = Type3Class('IntNum', ['a'], methods={ }, operators={}, inherited_classes=[NatNum]) Integral = Type3Class('Eq', ['a'], methods={ - 'div': 'a -> a -> a', -}, operators={}, inherited_classes=[NatNum]) +}, operators={ + '//': 'a -> a -> a', + '%': 'a -> a -> a', +}, inherited_classes=[NatNum]) Fractional = Type3Class('Fractional', ['a'], methods={ 'ceil': 'a -> a', diff --git a/tests/integration/test_lang/test_integral.py b/tests/integration/test_lang/test_integral.py index 7bbb373..c51d892 100644 --- a/tests/integration/test_lang/test_integral.py +++ b/tests/integration/test_lang/test_integral.py @@ -6,11 +6,11 @@ TYPE_LIST = ['u32', 'u64', 'i32', 'i64'] @pytest.mark.integration_test @pytest.mark.parametrize('type_', TYPE_LIST) -def test_division_int(type_): +def test_integral_div_ok(type_): code_py = f""" @exported def testEntry() -> {type_}: - return div(10, 3) + return 10 // 3 """ result = Suite(code_py).run_code() @@ -20,11 +20,39 @@ def testEntry() -> {type_}: @pytest.mark.integration_test @pytest.mark.parametrize('type_', TYPE_LIST) -def test_division_zero_let_it_crash_int(type_): +def test_integral_div_zero_let_it_crash_int(type_): code_py = f""" @exported def testEntry() -> {type_}: - return div(10, 0) + return 10 // 0 +""" + + # WebAssembly dictates that integer division is a partial operator (e.g. unreachable for 0) + # https://www.w3.org/TR/wasm-core-1/#-hrefop-idiv-umathrmidiv_u_n-i_1-i_2 + with pytest.raises(Exception): + Suite(code_py).run_code() + +@pytest.mark.integration_test +@pytest.mark.parametrize('type_', TYPE_LIST) +def test_integral_rem_ok(type_): + code_py = f""" +@exported +def testEntry() -> {type_}: + return 10 % 3 +""" + + result = Suite(code_py).run_code() + + assert 1 == result.returned_value + assert isinstance(result.returned_value, int) + +@pytest.mark.integration_test +@pytest.mark.parametrize('type_', TYPE_LIST) +def test_integral_rem_zero_let_it_crash_int(type_): + code_py = f""" +@exported +def testEntry() -> {type_}: + return 10 % 0 """ # WebAssembly dictates that integer division is a partial operator (e.g. unreachable for 0)