diff --git a/phasm/compiler.py b/phasm/compiler.py index 686a7ac..c38d180 100644 --- a/phasm/compiler.py +++ b/phasm/compiler.py @@ -49,6 +49,66 @@ INSTANCES = { 'a=f32': stdlib_types.f32_eq_not_equals, 'a=f64': stdlib_types.f64_eq_not_equals, }, + type3classes.Ord.methods['min']: { + 'a=u8': stdlib_types.u8_ord_min, + 'a=u32': stdlib_types.u32_ord_min, + 'a=u64': stdlib_types.u64_ord_min, + 'a=i8': stdlib_types.i8_ord_min, + 'a=i32': stdlib_types.i32_ord_min, + 'a=i64': stdlib_types.i64_ord_min, + 'a=f32': stdlib_types.f32_ord_min, + 'a=f64': stdlib_types.f64_ord_min, + }, + type3classes.Ord.methods['max']: { + 'a=u8': stdlib_types.u8_ord_max, + 'a=u32': stdlib_types.u32_ord_max, + 'a=u64': stdlib_types.u64_ord_max, + 'a=i8': stdlib_types.i8_ord_max, + 'a=i32': stdlib_types.i32_ord_max, + 'a=i64': stdlib_types.i64_ord_max, + 'a=f32': stdlib_types.f32_ord_max, + 'a=f64': stdlib_types.f64_ord_max, + }, + type3classes.Ord.operators['<']: { + 'a=u8': stdlib_types.u8_ord_less_than, + 'a=u32': stdlib_types.u32_ord_less_than, + 'a=u64': stdlib_types.u64_ord_less_than, + 'a=i8': stdlib_types.i8_ord_less_than, + 'a=i32': stdlib_types.i32_ord_less_than, + 'a=i64': stdlib_types.i64_ord_less_than, + 'a=f32': stdlib_types.f32_ord_less_than, + 'a=f64': stdlib_types.f64_ord_less_than, + }, + type3classes.Ord.operators['<=']: { + 'a=u8': stdlib_types.u8_ord_less_than_or_equal, + 'a=u32': stdlib_types.u32_ord_less_than_or_equal, + 'a=u64': stdlib_types.u64_ord_less_than_or_equal, + 'a=i8': stdlib_types.i8_ord_less_than_or_equal, + 'a=i32': stdlib_types.i32_ord_less_than_or_equal, + 'a=i64': stdlib_types.i64_ord_less_than_or_equal, + 'a=f32': stdlib_types.f32_ord_less_than_or_equal, + 'a=f64': stdlib_types.f64_ord_less_than_or_equal, + }, + type3classes.Ord.operators['>']: { + 'a=u8': stdlib_types.u8_ord_greater_than, + 'a=u32': stdlib_types.u32_ord_greater_than, + 'a=u64': stdlib_types.u64_ord_greater_than, + 'a=i8': stdlib_types.i8_ord_greater_than, + 'a=i32': stdlib_types.i32_ord_greater_than, + 'a=i64': stdlib_types.i64_ord_greater_than, + 'a=f32': stdlib_types.f32_ord_greater_than, + 'a=f64': stdlib_types.f64_ord_greater_than, + }, + type3classes.Ord.operators['>=']: { + 'a=u8': stdlib_types.u8_ord_greater_than_or_equal, + 'a=u32': stdlib_types.u32_ord_greater_than_or_equal, + 'a=u64': stdlib_types.u64_ord_greater_than_or_equal, + 'a=i8': stdlib_types.i8_ord_greater_than_or_equal, + 'a=i32': stdlib_types.i32_ord_greater_than_or_equal, + 'a=i64': stdlib_types.i64_ord_greater_than_or_equal, + 'a=f32': stdlib_types.f32_ord_greater_than_or_equal, + 'a=f64': stdlib_types.f64_ord_greater_than_or_equal, + }, type3classes.Floating.methods['sqrt']: { 'a=f32': stdlib_types.f32_floating_sqrt, 'a=f64': stdlib_types.f64_floating_sqrt, @@ -200,10 +260,6 @@ U8_OPERATOR_MAP = { } U32_OPERATOR_MAP = { - '<': 'lt_u', - '>': 'gt_u', - '<=': 'le_u', - '>=': 'ge_u', '<<': 'shl', '>>': 'shr_u', '^': 'xor', @@ -212,10 +268,6 @@ U32_OPERATOR_MAP = { } U64_OPERATOR_MAP = { - '<': 'lt_u', - '>': 'gt_u', - '<=': 'le_u', - '>=': 'ge_u', '<<': 'shl', '>>': 'shr_u', '^': 'xor', @@ -223,18 +275,10 @@ U64_OPERATOR_MAP = { '&': 'and', } -I32_OPERATOR_MAP = { - '<': 'lt_s', - '>': 'gt_s', - '<=': 'le_s', - '>=': 'ge_s', +I32_OPERATOR_MAP: dict[str, str] = { } -I64_OPERATOR_MAP = { - '<': 'lt_s', - '>': 'gt_s', - '<=': 'le_s', - '>=': 'ge_s', +I64_OPERATOR_MAP: dict[str, str] = { } def tuple_instantiation(wgn: WasmGenerator, inp: ourlang.TupleInstantiation) -> None: @@ -923,6 +967,14 @@ def module(inp: ourlang.Module) -> wasm.Module: stdlib_alloc.__alloc__, stdlib_types.__alloc_bytes__, stdlib_types.__subscript_bytes__, + stdlib_types.__u32_ord_min__, + stdlib_types.__u64_ord_min__, + stdlib_types.__i32_ord_min__, + stdlib_types.__i64_ord_min__, + stdlib_types.__u32_ord_max__, + stdlib_types.__u64_ord_max__, + stdlib_types.__i32_ord_max__, + stdlib_types.__i64_ord_max__, stdlib_types.__i32_intnum_abs__, stdlib_types.__i64_intnum_abs__, ] + [ diff --git a/phasm/parser.py b/phasm/parser.py index d4e3bf6..dfe0ae2 100644 --- a/phasm/parser.py +++ b/phasm/parser.py @@ -36,6 +36,7 @@ from .type3 import types as type3types PRELUDE_OPERATORS = { **type3typeclasses.Eq.operators, + **type3typeclasses.Ord.operators, **type3typeclasses.Fractional.operators, **type3typeclasses.Integral.operators, **type3typeclasses.IntNum.operators, @@ -44,6 +45,7 @@ PRELUDE_OPERATORS = { PRELUDE_METHODS = { **type3typeclasses.Eq.methods, + **type3typeclasses.Ord.methods, **type3typeclasses.Floating.methods, **type3typeclasses.Fractional.methods, **type3typeclasses.Integral.methods, @@ -405,12 +407,16 @@ class OurVisitor: if isinstance(node.ops[0], ast.Gt): operator = '>' + elif isinstance(node.ops[0], ast.GtE): + operator = '>=' elif isinstance(node.ops[0], ast.Eq): operator = '==' elif isinstance(node.ops[0], ast.NotEq): operator = '!=' elif isinstance(node.ops[0], ast.Lt): operator = '<' + elif isinstance(node.ops[0], ast.LtE): + operator = '<=' else: raise NotImplementedError(f'Operator {node.ops}') diff --git a/phasm/stdlib/types.py b/phasm/stdlib/types.py index 2361774..186dfb7 100644 --- a/phasm/stdlib/types.py +++ b/phasm/stdlib/types.py @@ -67,6 +67,126 @@ def __subscript_bytes__(g: Generator, adr: i32, ofs: i32) -> i32: return i32('return') # To satisfy mypy +@func_wrapper() +def __u32_ord_min__(g: Generator, x: i32, y: i32) -> i32: + g.local.get(x) + g.local.get(y) + g.i32.lt_u() + + with g.if_(): + g.local.get(x) + g.return_() + + g.local.get(y) + g.return_() + + return i32('return') # To satisfy mypy + +@func_wrapper() +def __u64_ord_min__(g: Generator, x: i64, y: i64) -> i64: + g.local.get(x) + g.local.get(y) + g.i64.lt_u() + + with g.if_(): + g.local.get(x) + g.return_() + + g.local.get(y) + g.return_() + + return i64('return') # To satisfy mypy + +@func_wrapper() +def __i32_ord_min__(g: Generator, x: i32, y: i32) -> i32: + g.local.get(x) + g.local.get(y) + g.i32.lt_s() + + with g.if_(): + g.local.get(x) + g.return_() + + g.local.get(y) + g.return_() + + return i32('return') # To satisfy mypy + +@func_wrapper() +def __i64_ord_min__(g: Generator, x: i64, y: i64) -> i64: + g.local.get(x) + g.local.get(y) + g.i64.lt_s() + + with g.if_(): + g.local.get(x) + g.return_() + + g.local.get(y) + g.return_() + + return i64('return') # To satisfy mypy + +@func_wrapper() +def __u32_ord_max__(g: Generator, x: i32, y: i32) -> i32: + g.local.get(x) + g.local.get(y) + g.i32.gt_u() + + with g.if_(): + g.local.get(x) + g.return_() + + g.local.get(y) + g.return_() + + return i32('return') # To satisfy mypy + +@func_wrapper() +def __u64_ord_max__(g: Generator, x: i64, y: i64) -> i64: + g.local.get(x) + g.local.get(y) + g.i64.gt_u() + + with g.if_(): + g.local.get(x) + g.return_() + + g.local.get(y) + g.return_() + + return i64('return') # To satisfy mypy + +@func_wrapper() +def __i32_ord_max__(g: Generator, x: i32, y: i32) -> i32: + g.local.get(x) + g.local.get(y) + g.i32.gt_s() + + with g.if_(): + g.local.get(x) + g.return_() + + g.local.get(y) + g.return_() + + return i32('return') # To satisfy mypy + +@func_wrapper() +def __i64_ord_max__(g: Generator, x: i64, y: i64) -> i64: + g.local.get(x) + g.local.get(y) + g.i64.gt_s() + + with g.if_(): + g.local.get(x) + g.return_() + + g.local.get(y) + g.return_() + + return i64('return') # To satisfy mypy + @func_wrapper() def __i32_intnum_abs__(g: Generator, x: i32) -> i32: # https://stackoverflow.com/a/14194764 @@ -170,6 +290,153 @@ def f32_eq_not_equals(g: Generator) -> None: def f64_eq_not_equals(g: Generator) -> None: g.f64.ne() +## ### +## class Ord + +def u8_ord_min(g: Generator) -> None: + g.add_statement('call $stdlib.types.__u32_ord_min__') + +def u32_ord_min(g: Generator) -> None: + g.add_statement('call $stdlib.types.__u32_ord_min__') + +def u64_ord_min(g: Generator) -> None: + g.add_statement('call $stdlib.types.__u64_ord_min__') + +def i8_ord_min(g: Generator) -> None: + g.add_statement('call $stdlib.types.__i32_ord_min__') + +def i32_ord_min(g: Generator) -> None: + g.add_statement('call $stdlib.types.__i32_ord_min__') + +def i64_ord_min(g: Generator) -> None: + g.add_statement('call $stdlib.types.__i64_ord_min__') + +def f32_ord_min(g: Generator) -> None: + g.f32.min() + +def f64_ord_min(g: Generator) -> None: + g.f64.min() + +def u8_ord_max(g: Generator) -> None: + g.add_statement('call $stdlib.types.__u32_ord_max__') + +def u32_ord_max(g: Generator) -> None: + g.add_statement('call $stdlib.types.__u32_ord_max__') + +def u64_ord_max(g: Generator) -> None: + g.add_statement('call $stdlib.types.__u64_ord_max__') + +def i8_ord_max(g: Generator) -> None: + g.add_statement('call $stdlib.types.__i32_ord_max__') + +def i32_ord_max(g: Generator) -> None: + g.add_statement('call $stdlib.types.__i32_ord_max__') + +def i64_ord_max(g: Generator) -> None: + g.add_statement('call $stdlib.types.__i64_ord_max__') + +def f32_ord_max(g: Generator) -> None: + g.f32.max() + +def f64_ord_max(g: Generator) -> None: + g.f64.max() + + +def u8_ord_less_than(g: Generator) -> None: + g.i32.lt_u() + +def u32_ord_less_than(g: Generator) -> None: + g.i32.lt_u() + +def u64_ord_less_than(g: Generator) -> None: + g.i64.lt_u() + +def i8_ord_less_than(g: Generator) -> None: + g.i32.lt_s() + +def i32_ord_less_than(g: Generator) -> None: + g.i32.lt_s() + +def i64_ord_less_than(g: Generator) -> None: + g.i64.lt_s() + +def f32_ord_less_than(g: Generator) -> None: + g.f32.lt() + +def f64_ord_less_than(g: Generator) -> None: + g.f64.lt() + +def u8_ord_less_than_or_equal(g: Generator) -> None: + g.i32.le_u() + +def u32_ord_less_than_or_equal(g: Generator) -> None: + g.i32.le_u() + +def u64_ord_less_than_or_equal(g: Generator) -> None: + g.i64.le_u() + +def i8_ord_less_than_or_equal(g: Generator) -> None: + g.i32.le_s() + +def i32_ord_less_than_or_equal(g: Generator) -> None: + g.i32.le_s() + +def i64_ord_less_than_or_equal(g: Generator) -> None: + g.i64.le_s() + +def f32_ord_less_than_or_equal(g: Generator) -> None: + g.f32.le() + +def f64_ord_less_than_or_equal(g: Generator) -> None: + g.f64.le() + +def u8_ord_greater_than(g: Generator) -> None: + g.i32.gt_u() + +def u32_ord_greater_than(g: Generator) -> None: + g.i32.gt_u() + +def u64_ord_greater_than(g: Generator) -> None: + g.i64.gt_u() + +def i8_ord_greater_than(g: Generator) -> None: + g.i32.gt_s() + +def i32_ord_greater_than(g: Generator) -> None: + g.i32.gt_s() + +def i64_ord_greater_than(g: Generator) -> None: + g.i64.gt_s() + +def f32_ord_greater_than(g: Generator) -> None: + g.f32.gt() + +def f64_ord_greater_than(g: Generator) -> None: + g.f64.gt() + +def u8_ord_greater_than_or_equal(g: Generator) -> None: + g.i32.ge_u() + +def u32_ord_greater_than_or_equal(g: Generator) -> None: + g.i32.ge_u() + +def u64_ord_greater_than_or_equal(g: Generator) -> None: + g.i64.ge_u() + +def i8_ord_greater_than_or_equal(g: Generator) -> None: + g.i32.ge_s() + +def i32_ord_greater_than_or_equal(g: Generator) -> None: + g.i32.ge_s() + +def i64_ord_greater_than_or_equal(g: Generator) -> None: + g.i64.ge_s() + +def f32_ord_greater_than_or_equal(g: Generator) -> None: + g.f32.ge() + +def f64_ord_greater_than_or_equal(g: Generator) -> None: + g.f64.ge() ## ### ## class Fractional diff --git a/phasm/type3/typeclasses.py b/phasm/type3/typeclasses.py index ee912f7..638e5fb 100644 --- a/phasm/type3/typeclasses.py +++ b/phasm/type3/typeclasses.py @@ -98,6 +98,16 @@ Eq = Type3Class('Eq', ['a'], methods={}, operators={ '!=': 'a -> a -> bool', }) +Ord = Type3Class('Ord', ['a'], methods={ + 'min': 'a -> a -> a', + 'max': 'a -> a -> a', +}, operators={ + '<': 'a -> a -> bool', + '<=': 'a -> a -> bool', + '>': 'a -> a -> bool', + '>=': 'a -> a -> bool', +}, inherited_classes=[Eq]) + NatNum = Type3Class('NatNum', ['a'], methods={}, operators={ '+': 'a -> a -> a', '-': 'a -> a -> a', diff --git a/phasm/type3/types.py b/phasm/type3/types.py index 7b98897..a5bf68d 100644 --- a/phasm/type3/types.py +++ b/phasm/type3/types.py @@ -6,7 +6,16 @@ constraint generator works with. """ from typing import Any, Dict, Iterable, List, Optional, Protocol, Set, Union -from .typeclasses import Eq, Floating, Fractional, Integral, IntNum, NatNum, Type3Class +from .typeclasses import ( + Eq, + Floating, + Fractional, + Integral, + IntNum, + NatNum, + Ord, + Type3Class, +) TYPE3_ASSERTION_ERROR = 'You must call phasm_type3 after calling phasm_parse before you can call any other method' @@ -43,7 +52,10 @@ class Type3: for cls in self.classes: for inh_cls in cls.inherited_classes: if inh_cls not in self.classes: - raise Exception(f'No instance for ({inh_cls} {self.name})') + raise Exception( + f'No instance for ({inh_cls} {self.name})' + f'; required for ({cls} {self.name})' + ) def __repr__(self) -> str: return f'Type3({repr(self.name)}, {repr(self.classes)})' @@ -247,28 +259,28 @@ The bool type, either True or False Suffixes with an underscores, as it's a Python builtin """ -u8 = PrimitiveType3('u8', [Eq]) +u8 = PrimitiveType3('u8', [Eq, Ord]) """ The unsigned 8-bit integer type. Operations on variables employ modular arithmetic, with modulus 2^8. """ -u32 = PrimitiveType3('u32', [Eq, Integral, NatNum]) +u32 = PrimitiveType3('u32', [Eq, Integral, NatNum, Ord]) """ The unsigned 32-bit integer type. Operations on variables employ modular arithmetic, with modulus 2^32. """ -u64 = PrimitiveType3('u64', [Eq, Integral, NatNum]) +u64 = PrimitiveType3('u64', [Eq, Integral, NatNum, Ord]) """ The unsigned 64-bit integer type. Operations on variables employ modular arithmetic, with modulus 2^64. """ -i8 = PrimitiveType3('i8', [Eq]) +i8 = PrimitiveType3('i8', [Eq, Ord]) """ The signed 8-bit integer type. @@ -276,7 +288,7 @@ Operations on variables employ modular arithmetic, with modulus 2^8, but with the middel point being 0. """ -i32 = PrimitiveType3('i32', [Eq, Integral, IntNum, NatNum]) +i32 = PrimitiveType3('i32', [Eq, Integral, IntNum, NatNum, Ord]) """ The unsigned 32-bit integer type. @@ -284,7 +296,7 @@ Operations on variables employ modular arithmetic, with modulus 2^32, but with the middel point being 0. """ -i64 = PrimitiveType3('i64', [Eq, Integral, IntNum, NatNum]) +i64 = PrimitiveType3('i64', [Eq, Integral, IntNum, NatNum, Ord]) """ The unsigned 64-bit integer type. @@ -292,12 +304,12 @@ Operations on variables employ modular arithmetic, with modulus 2^64, but with the middel point being 0. """ -f32 = PrimitiveType3('f32', [Eq, Floating, Fractional, IntNum, NatNum]) +f32 = PrimitiveType3('f32', [Eq, Floating, Fractional, IntNum, NatNum, Ord]) """ A 32-bits IEEE 754 float, of 32 bits width. """ -f64 = PrimitiveType3('f64', [Eq, Floating, Fractional, IntNum, NatNum]) +f64 = PrimitiveType3('f64', [Eq, Floating, Fractional, IntNum, NatNum, Ord]) """ A 32-bits IEEE 754 float, of 64 bits width. """ diff --git a/phasm/wasmgenerator.py b/phasm/wasmgenerator.py index 8ca9c5f..da813cc 100644 --- a/phasm/wasmgenerator.py +++ b/phasm/wasmgenerator.py @@ -34,15 +34,29 @@ class Generator_i32i64: self.add = functools.partial(self.generator.add_statement, f'{prefix}.add') self.sub = functools.partial(self.generator.add_statement, f'{prefix}.sub') self.mul = functools.partial(self.generator.add_statement, f'{prefix}.mul') + self.div_s = functools.partial(self.generator.add_statement, f'{prefix}.div_s') + self.div_u = functools.partial(self.generator.add_statement, f'{prefix}.div_u') + self.rem_s = functools.partial(self.generator.add_statement, f'{prefix}.rem_s') + self.rem_u = functools.partial(self.generator.add_statement, f'{prefix}.rem_u') + self.and_ = functools.partial(self.generator.add_statement, f'{prefix}.and') + self.or_ = functools.partial(self.generator.add_statement, f'{prefix}.or') + self.xor = functools.partial(self.generator.add_statement, f'{prefix}.xor') + self.shl = functools.partial(self.generator.add_statement, f'{prefix}.shl') self.shr_s = functools.partial(self.generator.add_statement, f'{prefix}.shr_s') self.shr_u = functools.partial(self.generator.add_statement, f'{prefix}.shr_u') + self.rotl = functools.partial(self.generator.add_statement, f'{prefix}.rotl') self.rotr = functools.partial(self.generator.add_statement, f'{prefix}.rotr') - self.xor = functools.partial(self.generator.add_statement, f'{prefix}.xor') # irelop self.eq = functools.partial(self.generator.add_statement, f'{prefix}.eq') self.ne = functools.partial(self.generator.add_statement, f'{prefix}.ne') + self.lt_s = functools.partial(self.generator.add_statement, f'{prefix}.lt_s') self.lt_u = functools.partial(self.generator.add_statement, f'{prefix}.lt_u') + self.gt_s = functools.partial(self.generator.add_statement, f'{prefix}.gt_s') + self.gt_u = functools.partial(self.generator.add_statement, f'{prefix}.gt_u') + self.le_s = functools.partial(self.generator.add_statement, f'{prefix}.le_s') + self.le_u = functools.partial(self.generator.add_statement, f'{prefix}.le_u') + self.ge_s = functools.partial(self.generator.add_statement, f'{prefix}.ge_s') self.ge_u = functools.partial(self.generator.add_statement, f'{prefix}.ge_u') # 2.4.4. Memory Instructions @@ -88,6 +102,10 @@ class Generator_f32f64: # frelop self.eq = functools.partial(self.generator.add_statement, f'{prefix}.eq') self.ne = functools.partial(self.generator.add_statement, f'{prefix}.ne') + self.lt = functools.partial(self.generator.add_statement, f'{prefix}.lt') + self.gt = functools.partial(self.generator.add_statement, f'{prefix}.gt') + self.le = functools.partial(self.generator.add_statement, f'{prefix}.le') + self.ge = functools.partial(self.generator.add_statement, f'{prefix}.ge') # 2.4.4. Memory Instructions self.load = functools.partial(self.generator.add_statement, f'{prefix}.load') diff --git a/tests/integration/helpers.py b/tests/integration/helpers.py index 4cb9515..efdcc68 100644 --- a/tests/integration/helpers.py +++ b/tests/integration/helpers.py @@ -253,7 +253,7 @@ def _load_memory_stored_returned_value( assert isinstance(wasm_value, int), wasm_value return 0 != wasm_value - if ret_type3 in (type3types.i32, type3types.i64): + if ret_type3 in (type3types.i8, type3types.i32, type3types.i64): assert isinstance(wasm_value, int), wasm_value return wasm_value diff --git a/tests/integration/test_lang/test_ord.py b/tests/integration/test_lang/test_ord.py new file mode 100644 index 0000000..5e743d4 --- /dev/null +++ b/tests/integration/test_lang/test_ord.py @@ -0,0 +1,97 @@ +import struct + +import pytest + +from ..helpers import Suite + +TYPE_LIST = [ + 'u8', 'u32', 'u64', + 'i8', 'i32', 'i64', + 'f32', 'f64', +] + +BOUND_MAP = { + 'u8': struct.unpack('', 0, True, ), + (1, '>', 1, False, ), + + (1, '>=', 0, True, ), + (1, '>=', 2, False, ), +]) +def test_ord_operators(type_, test_lft, test_opr, test_rgt, test_out): + code_py = f""" +@exported +def testEntry(lft: {type_}, rgt: {type_}) -> bool: + return lft {test_opr} rgt +""" + + if type_[0] == 'f': + test_lft = float(test_lft) + test_rgt = float(test_rgt) + + result = Suite(code_py).run_code(test_lft, test_rgt) + + assert test_out is result.returned_value + +@pytest.mark.integration_test +@pytest.mark.parametrize('type_', TYPE_LIST) +@pytest.mark.parametrize('test_mth,test_lft,test_rgt,test_out', [ + ('min', 1, 2, 1), + ('min', 2, 1, 1), + ('min', 2, 2, 2), + + ('max', 1, 2, 2), + ('max', 2, 1, 2), + ('max', 2, 2, 2), +]) +def test_ord_methods(type_, test_mth, test_lft, test_rgt, test_out): + code_py = f""" +@exported +def testEntry(lft: {type_}, rgt: {type_}) -> {type_}: + return {test_mth}(lft, rgt) +""" + + if type_[0] == 'f': + test_lft = float(test_lft) + test_rgt = float(test_rgt) + + result = Suite(code_py).run_code(test_lft, test_rgt) + + assert test_out == result.returned_value + +@pytest.mark.integration_test +@pytest.mark.parametrize('type_', TYPE_LIST) +@pytest.mark.parametrize('test_mth', ['min', 'max']) +def test_ord_bounds_min_max(type_, test_mth): + code_py = f""" +@exported +def testEntry(lft: {type_}, rgt: {type_}) -> {type_}: + return {test_mth}(lft, rgt) +""" + + test_lft, test_rgt = BOUND_MAP[type_] + + result = Suite(code_py).run_code(test_lft, test_rgt) + + if 'min' == test_mth: + assert test_lft == result.returned_value + else: + assert test_rgt == result.returned_value