def op(data_type: DataType, *params) -> List[Optional[int]]: result = arith_op(*params) if result > data_type.max_value( is_signed) or result < data_type.min_value(is_signed): return [None] else: return [result]
def _handle_load(self, load: Tree) -> List[Instruction]: target_name, source_name, offset_lit, type_name = load.children source_idx, _ = self._get_item(source_name, True) offset = int(offset_lit) type_match = self.REG_TYPE.match(type_name) is_signed = type_match.group(1) == 'i' bit_size = int(type_match.group(2)) self._push(CompilerBeltItem(target_name, is_signed, False)) return [InsLoad(DataType(bit_size), source_idx, offset)]
def op(data_type: DataType, *params) -> List[Optional[int]]: result = arith_op(*params) num_bytes = data_type.num_bytes() wide_bytes = result.to_bytes(num_bytes, 'little', signed=is_signed) return [ int.from_bytes(wide_bytes[num_bytes:], 'little', signed=is_signed), int.from_bytes(wide_bytes[:num_bytes], 'little', signed=is_signed), ]
def _handle_lit(self, names: List[str], lit: Tree) -> List[Instruction]: assigned_name, = names lit, = lit.children lit = lit.replace('_', '') if assigned_name.startswith('$'): raise ValueError('Cannot assign literals to locals (yet?)') m = self.REG_LIT.match(lit) num = int(m.group(1)) is_signed = m.group(2) == 'i' bit_size = int(m.group(3)) self._push(CompilerBeltItem(assigned_name, is_signed, False)) return [ InsConst( BeltNum.from_signed(Integer(num), DataType(bit_size), is_signed)) ]
def _handle_call(self, names: List[str], call: Tree) -> List[Instruction]: call_name, params = call.children params = params.children if call_name == 'is_err': if len(params) != 1: raise ValueError(f'{call_name} takes exactly 1 argument') item_name, = params result_name, = names item_idx, item = self._get_item(item_name, False) self._push(CompilerBeltItem(result_name, False, False)) return [InsIsErr(item_idx)] elif call_name == 'length': if len(params) != 1: raise ValueError(f'{call_name} takes exactly 1 argument') slice_name, = params result_name, = names slice_idx, _ = self._get_item(slice_name, True) self._push(CompilerBeltItem(result_name, False, False)) return [InsSliceLen(slice_idx)] elif call_name in {'trim_l', 'trim_r', 'shrink'}: if len(params) != 2: raise ValueError(f'{call_name} takes exactly 2 argument') slice_name, num_bytes_name = params result_name, = names slice_idx, _ = self._get_item(slice_name, True) num_bytes_idx, _ = self._get_item(num_bytes_name, False) self._push(CompilerBeltItem(result_name, None, True)) return [ InsSliceOp( slice_idx, num_bytes_idx, { 'trim_l': BeltSlice.trim_l, 'trim_r': BeltSlice.trim_r, 'shrink': BeltSlice.shrink, }[call_name]) ] elif call_name == 'divmod': if len(params) != 2: raise ValueError(f'{call_name} takes exactly 2 argument') a_name, b_name = params div_name, mod_name = names a_idx, a = self._get_item(a_name, False) b_idx, b = self._get_item(b_name, False) if a.is_signed != b.is_signed: raise ValueError( f'Incompatible operands, {a_name} {"is" if a.is_signed else "is not"} signed, ' f'but {b_name} {"is" if b.is_signed else "is not"}.') self._push(CompilerBeltItem(div_name, a.is_signed, False)) self._push(CompilerBeltItem(mod_name, a.is_signed, False)) return [ InsNAryOp([a_idx, b_idx], a.is_signed, lambda _, x, y: [None, None] if y == 0 else divmod(x, y)) ] elif call_name in {'rotl', 'rotr', 'clz', 'ctz', 'popcnt'}: raise NotImplemented else: match_cast = self.REG_CAST.match(call_name) if match_cast is not None: call_name = match_cast.group(1) bit_size = int(match_cast.group(2)) data_type = DataType(bit_size) item_name, = params result, = names item_idx, item = self._get_item(item_name, False) self._push(CompilerBeltItem(result, item.is_signed, False)) if call_name == 'cast_extend': if bit_size == 8: raise ValueError("Cannot use cast_extend8") func = BeltNum.extend elif call_name == 'cast_wrap': def func(num: BeltNum, _data_type: DataType, _: bool) -> BeltNum: return num.wrap(_data_type) if bit_size == 64: raise ValueError("Cannot use cast_wrap8") elif call_name == 'cast_sat': if bit_size == 64: raise ValueError("Cannot use cast_sat8") func = BeltNum.cast_sat elif call_name == 'cast_checked': if bit_size == 64: raise ValueError("Cannot use cast_checked8") func = BeltNum.cast_checked else: raise ValueError('Unreachable') return [InsConvert(item_idx, data_type, item.is_signed, func)] else: raise ValueError(f"Unknown function {call_name}")
def test_data_type_min(data_type: DataType, is_signed: bool, expected: int): assert data_type.min_value(is_signed) == expected