def uint_type_cast_resolver(dtype, cast_type): if typeof(dtype, Ufixp): if cast_type.specified: if cast_type.width < dtype.integer: raise get_type_error(dtype, cast_type, [ f"fixed-point integer part (width '{dtype.integer}') is larger than target Uint", f"FIX: to force cast to smaller Uint, cast to generic Uint first and then code() as '{repr(cast_type)}'" ]) return cast_type elif dtype.integer <= 0: raise get_type_error(dtype, cast_type, [ f"fixed-point has no integer part", f"FIX: to force cast to Uint, supply Uint width explicitly" ]) else: return Uint[dtype.integer] if typeof(dtype, Uint): if not cast_type.specified: return dtype elif dtype.width > cast_type.width: raise get_type_error( dtype, cast_type, [f"{repr(dtype)} is larger then {repr(cast_type)}"]) else: return cast_type raise get_type_error(dtype, cast_type)
def union_type_cast_resolver(dtype, cast_type): if (typeof(dtype, Union) and (len(cast_type.args) == 0)): return dtype if typeof(dtype, Tuple): if len(dtype) != 2: raise get_type_error(dtype, cast_type, [ f"only Tuple with exactly 2 elements can be converted to Union" ]) if typeof(cast_type, Maybe): if dtype.args[1].width != 1: raise get_type_error(dtype, cast_type, [ f"only Tuple with 1 bit wide second argument can be converted to Maybe" ]) if not cast_type.specified: return Maybe[dtype.args[0]] data_type = cast(dtype.args[0], cast_type.types[1]) return Maybe[data_type] if len(cast_type.types) != 0: cast_ctrl = cast_type.ctrl types = tuple(cast_type.types) if cast_type.data.width < dtype.args[0].width: raise get_type_error(dtype, cast_type, [ f"Tuple first element larger than target Union data field" ]) try: ctrl = cast(dtype.args[1], cast_ctrl) except TypeError as e: raise TypeError( f"{str(e)}\n - when casting '{repr(dtype)}' to '{repr(cast_type)}'" ) else: ctrl = cast(dtype.args[1], Uint) if ctrl.width > 6: raise TypeError(f'Casting to large Union with {2**ctrl.width}' f' subtypes from {dtype}') types = (dtype.args[0], ) * (2**ctrl.width) res = Union[types] return res if typeof(cast_type, Maybe): if not (len(dtype.types) == 2 and typeof(dtype.types[0], Unit)): raise get_type_error(dtype, cast_type, [ f"only Union's with with the form 'Union[Unit, Any]' can be converted to Maybe" ]) return Maybe[dtype.types[1]] raise get_type_error(dtype, cast_type)
def ufixp_type_cast_resolver(dtype, cast_type): if typeof(dtype, Ufixp): if not cast_type.specified: return dtype if dtype.integer > cast_type.integer: raise get_type_error(dtype, cast_type, [ f"fixed-point integer part is larger than target's integer part ('{dtype.integer}' > '{cast_type.integer})", ]) if dtype.fract > cast_type.fract: raise get_type_error(dtype, cast_type, [ f"fixed-point fraction part is larger than target's fraction part ('{dtype.fract}' > '{cast_type.fract})", ]) return cast_type if typeof(dtype, Uint): if not cast_type.specified: return Ufixp[dtype.width, dtype.width] if dtype.width > cast_type.integer: raise get_type_error(dtype, cast_type, [ f"integer is larger than fixed-point integer part ('{dtype.width}' > '{cast_type.integer})", ]) return cast_type raise get_type_error(dtype, cast_type)
def tuple_type_cast_resolver(dtype, cast_type): if typeof(dtype, Tuple) and len(cast_type) == 0: return dtype if typeof(dtype, (Queue, Union, Tuple)): fields = [d for d in dtype] elif typeof(dtype, Array): fields = [dtype.data] * len(dtype) else: raise get_type_error(dtype, cast_type) if len(cast_type) == 0: return Tuple[tuple(fields)] elif len(cast_type) != len(fields): comp = 'less' if len(cast_type) < len(fields) else 'more' raise get_type_error(dtype, cast_type, [ f"target Tuple has {comp} elements ({len(cast_type)}) than needed ({len(fields)})" ]) else: try: cast_fields = [ cast(dt, ct) for dt, ct in zip(fields, cast_type.args) ] except TypeError as e: raise TypeError( f"{str(e)}\n - when casting '{repr(dtype)}' to '{repr(cast_type)}'" ) if typeof(cast_type, Tuple): return Tuple[dict(zip(cast_type.fields, cast_fields))] else: return Tuple[tuple(cast_fields)] raise get_type_error(dtype, cast_type)
def array_type_cast_resolver(dtype, cast_type): if (typeof(dtype, Array) and (len(cast_type.args) == 0)): return dtype if typeof(dtype, Tuple): if len(cast_type.args) >= 1: arr_dtype = cast(dtype.args[0], cast_type.dtype) else: arr_dtype = dtype.args[0] if len(cast_type.args) == 2: if len(dtype) != len(cast_type): comp = 'less' if len(cast_type) < len(dtype) else 'more' raise get_type_error(dtype, cast_type, [ f"target Array has {comp} elements ({len(cast_type)}) than Tuple ({len(dtype)})" ]) try: for t in dtype.args: cast(t, arr_dtype) except TypeError as e: raise TypeError( f"{str(e)}\n - when casting '{repr(dtype)}' to '{repr(cast_type)}'" ) return Array[arr_dtype, len(dtype)] if typeof(dtype, Array): dlen, clen = len(dtype), len(cast_type) if dlen != clen: comp = 'less' if clen < dlen else 'more' err_detail = [ f"target Array has {comp} elements ({len(cast_type)}) than source ({len(dtype)})" ] if dlen % clen == 0: subarray = Array[dtype.data, dlen // clen] try: subarray = cast(subarray, cast_type.data) return Array[subarray, clen] except TypeError as e: err_detail.append(str(e)) raise get_type_error(dtype, cast_type, err_detail) try: arr_dtype = cast(dtype.data, cast_type.data) except TypeError as e: raise TypeError( f"{str(e)}\n - when casting '{repr(dtype)}' to '{repr(cast_type)}'" ) return Array[arr_dtype, len(dtype)] raise get_type_error(dtype, cast_type)
def int_type_cast_resolver(dtype, cast_type): if typeof(dtype, Fixpnumber): int_part = dtype.integer if not dtype.signed: int_part += 1 if cast_type.specified: if not dtype.signed and cast_type.width == dtype.integer: raise get_type_error(dtype, cast_type, ( f"Int needs to be one bit larger (width {int_part}) to represent unsigned fixed-point integer part (width {dtype.integer})", f"FIX: to force cast to smaller Int, cast to generic Int first and then code() as '{repr(cast_type)}'" )) elif cast_type.width < dtype.integer: raise get_type_error(dtype, cast_type, ( f"fixed-point integer part (width '{dtype.integer}') is larger than target Int", f"FIX: to force cast to smaller Int, cast to generic Int first and then code() as '{repr(cast_type)}'" )) return cast_type elif dtype.integer <= 0: raise get_type_error( dtype, cast_type, (f"fixed-point has no integer part", f"FIX: to force cast to Int, supply Int width explicitly")) else: return Int[int_part] if typeof(dtype, Integer): width = dtype.width if not dtype.signed: width += 1 if not cast_type.specified: return Int[width] elif not dtype.signed and cast_type.width == dtype.width: raise get_type_error(dtype, cast_type, ( f"Int needs to be one bit larger (width {width}) to represent unsigned integer (width {dtype.width})", f"FIX: to force cast to smaller Int, cast to generic Int first and then code() as '{repr(cast_type)}'" )) elif cast_type.width < dtype.width: raise get_type_error( dtype, cast_type, [f"{repr(dtype)} is larger then {repr(cast_type)}"]) else: return cast_type raise get_type_error(dtype, cast_type)
def float_value_cast_resolver(val, cast_type): if typeof(type(val), (int, float, Number)): return cast_type(val) raise Exception( f'Only numbers can be converted to Float, not {val} of the type' f' {repr(type(val))}')
def signed(dtype_or_val): if dtype_or_val.signed: return dtype_or_val if is_type(dtype_or_val): if typeof(dtype_or_val, Uint): return cast(dtype_or_val, Int)
def int_value_cast_resolver(val, cast_type): val_type = type(val) if not is_type(val_type) and isinstance(val, (int, float)): return cast_type(int(val)) cast_type = int_type_cast_resolver(val_type, cast_type) if typeof(val_type, Fixpnumber): if val_type.fract >= 0: return cast_type.decode(val.code() >> val_type.fract) else: return cast_type.decode(val.code() << (-val_type.fract)) if typeof(val_type, Integer): return cast_type(val) raise get_value_error(val, cast_type)
def fixpnumber_type_cast_resolver(dtype, cast_type): if not typeof(dtype, Integral): raise get_type_error(dtype, cast_type) if dtype.signed: return fixp_type_cast_resolver(dtype, Fixp[cast_type.__args__]) else: return ufixp_type_cast_resolver(dtype, Ufixp[cast_type.__args__])
def array_type_cast_resolver(dtype, cast_type): if (typeof(dtype, Array) and (len(cast_type.args) == 0)): return dtype if typeof(dtype, Tuple): if len(cast_type.args) >= 1: arr_dtype = cast(dtype.args[0], cast_type.dtype) else: arr_dtype = dtype.args[0] if len(cast_type.args) == 2: if len(dtype) != len(cast_type): comp = 'less' if len(cast_type) < len(dtype) else 'more' raise get_type_error(dtype, cast_type, [ f"target Array has {comp} elements ({len(cast_type)}) than Tuple ({len(dtype)})" ]) try: for t in dtype.args: cast(t, arr_dtype) except TypeError as e: raise TypeError( f"{str(e)}\n - when casting '{repr(dtype)}' to '{repr(cast_type)}'" ) return Array[arr_dtype, len(dtype)] if typeof(dtype, Array): if len(dtype) != len(cast_type): comp = 'less' if len(cast_type) < len(dtype) else 'more' raise get_type_error(dtype, cast_type, [ f"target Array has {comp} elements ({len(cast_type)}) than source ({len(dtype)})" ]) try: arr_dtype = cast(dtype.data, cast_type.data) except TypeError as e: raise TypeError( f"{str(e)}\n - when casting '{repr(dtype)}' to '{repr(cast_type)}'" ) return Array[arr_dtype, len(dtype)] raise get_type_error(dtype, cast_type)
def integer_value_cast_resolver(val, cast_type): if isinstance(val, int): return cast_type(val) if typeof(type(val), Integer): if not cast_type.specified: cast_type = cast_type[val.width] tout_range = (1 << int(cast_type)) val = int(val) & (tout_range - 1) if typeof(cast_type, Int): max_uint = tout_range / 2 - 1 if val > max_uint: val -= tout_range return cast_type(val) raise get_value_error(val, cast_type)
def queue_type_cast_resolver(dtype, cast_type): if typeof(dtype, Queue): if len(cast_type.args) == 0: return dtype if dtype.lvl != cast_type.lvl: raise get_type_error(dtype, cast_type, [ f"Queue level ({dtype.lvl}) must match the cast Queue lelve ({cast_type.lvl})" ]) try: return Queue[cast(dtype.data, cast_type.data), cast_type.lvl] except TypeError as e: raise TypeError( f"{str(e)}\n - when casting '{repr(dtype)}' to '{repr(cast_type)}'" ) if typeof(dtype, Tuple): if len(dtype) != 2: raise get_type_error(dtype, cast_type, [ f"only Tuple with exactly 2 elements can be converted to Queue" ]) lvl = cast(dtype[1], Uint).width if len(cast_type.args) != 0: if cast_type.lvl != lvl: raise get_type_error(dtype, cast_type, [ f"second Tuple element width ({lvl}) must match Queue level ({cast_type.lvl})" ]) try: data = cast(dtype.args[0], cast_type.data) except TypeError as e: raise TypeError( f"{str(e)}\n - when casting '{repr(dtype)}' to '{repr(cast_type)}'" ) else: data = dtype.args[0] return Queue[data, lvl] raise get_type_error(dtype, cast_type)
def value_cast(val, cast_type): if type(val) == cast_type: return cast_type(val) for templ in value_cast_resolvers: if typeof(cast_type, templ): return value_cast_resolvers[templ](val, cast_type) raise ValueError( f"Type '{repr(cast_type)}' unsupported, cannot cast value '{val}' " f"of type '{repr(type(val))}'")
def type_cast(dtype, cast_type): if dtype == cast_type: return cast_type if isinstance(cast_type, str): return dtype for templ in type_cast_resolvers: if typeof(cast_type, templ): return type_cast_resolvers[templ](dtype, cast_type) raise TypeError(f"Cannot cast '{repr(dtype)}' to '{repr(cast_type)}'")
def fixp_type_cast_resolver(dtype, cast_type): if typeof(dtype, Fixpnumber): if not cast_type.specified: if dtype.signed: return dtype else: return Fixp[dtype.integer + 1, dtype.width + 1] int_part = dtype.integer if not dtype.signed: int_part += 1 if int_part > cast_type.integer: raise get_type_error(dtype, cast_type, [ f"needed target's integer part >= {int_part}", ]) if dtype.fract > cast_type.fract: raise get_type_error(dtype, cast_type, [ f"fixed-point fraction part is larger than target's fraction part ('{dtype.fract}' > '{cast_type.fract})", ]) return cast_type if typeof(dtype, Integer): dtype = type_cast(dtype, Int) if not cast_type.specified: return Fixp[dtype.width, dtype.width] if dtype.width > cast_type.integer: raise get_type_error(dtype, cast_type, [ f"integer is larger than fixed-point integer part ('{dtype.width}' > '{cast_type.integer})", ]) return cast_type raise get_type_error(dtype, cast_type)
def _get_match_conds_rec(t, pat, matches): # Ignore type template arguments, i.e.: 'T2' in Tuple[1, 2, 3, 'T2'] if isinstance(t, str): return t if (t == Any) or (pat == Any): return t if t == pat: return t if isinstance(pat, T): t = _get_match_conds_rec(t, pat.__bound__, matches) pat = pat.__name__ # Did we reach the parameter name? if isinstance(pat, str): if pat in matches: # If the parameter name is already bound, check if two deductions # are same # if repr(t) != repr(matches[pat]) and t != Any and matches[pat] != Any: if t != matches[pat] and t != Any and matches[pat] != Any: raise TypeMatchError( f'Ambiguous match for parameter "{pat}": {type_repr(t)} ' f"and {type_repr(matches[pat])}") else: try: # TODO: Should probably use globals of the string. See: Python # 3.10. typing.get_type_hints() res = eval(pat, reg['gear/type_arith'], matches) if t != res: raise TypeMatchError( f"{type_repr(t)} cannot be matched to {type_repr(res)}" ) except Exception as e: matches[pat] = t return t if not (isinstance(t, GenericMeta) and isinstance(pat, GenericMeta) and typeof(t.base, pat.base)): raise TypeMatchError("{} cannot be matched to {}".format( type_repr(t), type_repr(pat))) # TODO: There might be multiple levels of inheritance when the types are # created, maybe they are compatible base on some of their more distant # base classes if pat.args: if len(t.args) != len(pat.args): raise TypeMatchError("{} cannot be matched to {}".format( type_repr(t), type_repr(pat))) args = [] for ta, pa in zip(t.args, pat.args): try: res = _get_match_conds_rec(ta, pa, matches) args.append(res) except TypeMatchError as e: raise TypeMatchError( f'{str(e)}\n - when matching {repr(t)} to {repr(pat)}') # if hasattr(pat, '__parameters__'): args = {name: a for name, a in zip(pat.fields, args)} else: args = t.args # TODO: Revisit this Don't create a new type class when class has no # specified templates, so that we don't end up with multiple different # base class objects, that cannot be correctly tested with "issubclass" if not args and not t.args: return t else: return t.__class__(t.__name__, t.__bases__, dict(t.__dict__), args=args)
def float_type_cast_resolver(dtype, cast_type): if typeof(dtype, Number): return cast_type raise get_type_error(dtype, cast_type)
def plain_int_type_cast_resolver(dtype, cast_type): if typeof(dtype, (Uint, Int)): return int raise get_type_error(dtype, cast_type)