diff --git a/examples/crc32.py b/examples/crc32.py index ff4d2f9..781bf21 100644 --- a/examples/crc32.py +++ b/examples/crc32.py @@ -82,7 +82,7 @@ _CRC32_Table: u32[256] = [ ] def _crc32_f(crc: u32, byt: u8) -> u32: - return (crc >> 8) ^ _CRC32_Table[(crc & 0xFF) ^ byt] + return (crc >> 8) ^ _CRC32_Table[(crc & 0xFF) ^ u32(byt)] @exported def crc32(data: bytes) -> u32: diff --git a/phasm/compiler.py b/phasm/compiler.py index 4f810b6..723da8a 100644 --- a/phasm/compiler.py +++ b/phasm/compiler.py @@ -77,8 +77,11 @@ OPERATOR_MAP = { U8_OPERATOR_MAP = { # Under the hood, this is an i32 - # Implementing XOR, OR, AND is fine since the 3 remaining + # Implementing Right Shift XOR, OR, AND is fine since the 3 remaining # bytes stay zero after this operation + # Since it's unsigned an unsigned value, Logical or Arithmetic shift right + # are the same operation + '>>': 'shr_u', '^': 'xor', '|': 'or', '&': 'and', @@ -89,6 +92,8 @@ U32_OPERATOR_MAP = { '>': 'gt_u', '<=': 'le_u', '>=': 'ge_u', + '<<': 'shl', + '>>': 'shr_u', '^': 'xor', '|': 'or', '&': 'and', @@ -99,6 +104,8 @@ U64_OPERATOR_MAP = { '>': 'gt_u', '<=': 'le_u', '>=': 'ge_u', + '<<': 'shl', + '>>': 'shr_u', '^': 'xor', '|': 'or', '&': 'and', diff --git a/phasm/parser.py b/phasm/parser.py index 14e1ba2..f1de44f 100644 --- a/phasm/parser.py +++ b/phasm/parser.py @@ -244,6 +244,10 @@ class OurVisitor: operator = '-' elif isinstance(node.op, ast.Mult): operator = '*' + elif isinstance(node.op, ast.LShift): + operator = '<<' + elif isinstance(node.op, ast.RShift): + operator = '>>' elif isinstance(node.op, ast.BitOr): operator = '|' elif isinstance(node.op, ast.BitXor): @@ -433,16 +437,14 @@ class OurVisitor: if func.returns.__class__ != func.posonlyargs[0][1].__class__: _raise_static_error(node, f'Expected a foldable function, {func.name} returns a {codestyle.type_(func.returns)} but expects a {codestyle.type_(func.posonlyargs[0][1])}') - t_u8 = module.types['u8'] - - if t_u8.__class__ != func.posonlyargs[1][1].__class__: + if module.types['u8'].__class__ != func.posonlyargs[1][1].__class__: _raise_static_error(node, 'Only folding over bytes (u8) is supported at this time') return Fold( exp_type, Fold.Direction.LEFT, func, - self.visit_Module_FunctionDef_expr(module, function, our_locals, t_u8, node.args[1]), + self.visit_Module_FunctionDef_expr(module, function, our_locals, func.returns, node.args[1]), self.visit_Module_FunctionDef_expr(module, function, our_locals, module.types['bytes'], node.args[2]), ) else: @@ -550,7 +552,7 @@ class OurVisitor: _raise_static_error(node, 'Expected integer value') if node.value < 0 or node.value > 255: - _raise_static_error(node, 'Integer value out of range') + _raise_static_error(node, f'Integer value out of range; expected 0..255, actual {node.value}') return ConstantUInt8(exp_type, node.value) diff --git a/tests/integration/test_simple.py b/tests/integration/test_simple.py index 2669eee..b6bfbc5 100644 --- a/tests/integration/test_simple.py +++ b/tests/integration/test_simple.py @@ -60,6 +60,34 @@ def testEntry() -> {type_}: assert 7 == result.returned_value assert TYPE_MAP[type_] == type(result.returned_value) +@pytest.mark.integration_test +@pytest.mark.parametrize('type_', ['u32', 'u64']) # FIXME: Support u8, requires an extra AND operation +def test_logical_left_shift(type_): + code_py = f""" +@exported +def testEntry() -> {type_}: + return 10 << 3 +""" + + result = Suite(code_py).run_code() + + assert 80 == result.returned_value + assert TYPE_MAP[type_] == type(result.returned_value) + +@pytest.mark.integration_test +@pytest.mark.parametrize('type_', ['u8', 'u32', 'u64']) +def test_logical_right_shift(type_): + code_py = f""" +@exported +def testEntry() -> {type_}: + return 10 >> 3 +""" + + result = Suite(code_py).run_code() + + assert 1 == result.returned_value + assert TYPE_MAP[type_] == type(result.returned_value) + @pytest.mark.integration_test @pytest.mark.parametrize('type_', ['u8', 'u32', 'u64']) def test_bitwise_or(type_):