Compare commits
1 Commits
1bcd7cba30
...
23ca1799b2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
23ca1799b2 |
3
TODO.md
3
TODO.md
@ -16,7 +16,8 @@
|
|||||||
- Does Subscript do what we want? It's a language feature rather a normal typed thing. How would you implement your own Subscript-able type?
|
- Does Subscript do what we want? It's a language feature rather a normal typed thing. How would you implement your own Subscript-able type?
|
||||||
- Clean up Subscript implementation - it's half implemented in the compiler. Makes more sense to move more parts to stdlib_types.
|
- Clean up Subscript implementation - it's half implemented in the compiler. Makes more sense to move more parts to stdlib_types.
|
||||||
- Have a set of rules or guidelines for the constraint comments, they're messy.
|
- Have a set of rules or guidelines for the constraint comments, they're messy.
|
||||||
- Why is expression_subscript_bytes using a helper method but expression_subscript_static_array is not?
|
- Do we need to store the placeholders on the expressions? They're only temporary while the type checker is running
|
||||||
|
- Might not even need to store them at all outside the generated constraints?
|
||||||
|
|
||||||
- Parser is putting stuff in ModuleDataBlock
|
- Parser is putting stuff in ModuleDataBlock
|
||||||
- Surely the compiler should build data blocks
|
- Surely the compiler should build data blocks
|
||||||
|
|||||||
@ -6,7 +6,7 @@ It's intented to be a "any color, as long as it's black" kind of renderer
|
|||||||
from typing import Generator
|
from typing import Generator
|
||||||
|
|
||||||
from . import ourlang, prelude
|
from . import ourlang, prelude
|
||||||
from .type3.types import Type3, TypeApplication_Struct
|
from .type3.types import Type3
|
||||||
|
|
||||||
|
|
||||||
def phasm_render(inp: ourlang.Module) -> str:
|
def phasm_render(inp: ourlang.Module) -> str:
|
||||||
@ -30,10 +30,11 @@ def struct_definition(inp: ourlang.StructDefinition) -> str:
|
|||||||
"""
|
"""
|
||||||
Render: TypeStruct's definition
|
Render: TypeStruct's definition
|
||||||
"""
|
"""
|
||||||
assert isinstance(inp.struct_type3.application, TypeApplication_Struct)
|
st_args = prelude.struct.did_construct(inp.struct_type3)
|
||||||
|
assert st_args is not None
|
||||||
|
|
||||||
result = f'class {inp.struct_type3.name}:\n'
|
result = f'class {inp.struct_type3.name}:\n'
|
||||||
for mem, typ in inp.struct_type3.application.arguments:
|
for mem, typ in st_args.items():
|
||||||
result += f' {mem}: {type3(typ)}\n'
|
result += f' {mem}: {type3(typ)}\n'
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|||||||
@ -11,7 +11,6 @@ from .stdlib import types as stdlib_types
|
|||||||
from .type3 import functions as type3functions
|
from .type3 import functions as type3functions
|
||||||
from .type3 import typeclasses as type3classes
|
from .type3 import typeclasses as type3classes
|
||||||
from .type3 import types as type3types
|
from .type3 import types as type3types
|
||||||
from .type3.routers import NoRouteForTypeException, TypeApplicationRouter
|
|
||||||
from .wasmgenerator import Generator as WasmGenerator
|
from .wasmgenerator import Generator as WasmGenerator
|
||||||
|
|
||||||
TYPE3_ASSERTION_ERROR = 'You must call phasm_type3 after calling phasm_parse before your program can be compiled'
|
TYPE3_ASSERTION_ERROR = 'You must call phasm_type3 after calling phasm_parse before your program can be compiled'
|
||||||
@ -30,6 +29,243 @@ LOAD_STORE_TYPE_MAP = {
|
|||||||
'bytes': 'i32', # Bytes are passed around as pointers
|
'bytes': 'i32', # Bytes are passed around as pointers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# For now this is nice & clean, but this will get messy quick
|
||||||
|
# Especially once we get functions with polymorphying applied types
|
||||||
|
INSTANCES = {
|
||||||
|
prelude.Eq.operators['==']: {
|
||||||
|
'a=u8': stdlib_types.u8_eq_equals,
|
||||||
|
'a=u32': stdlib_types.u32_eq_equals,
|
||||||
|
'a=u64': stdlib_types.u64_eq_equals,
|
||||||
|
'a=i8': stdlib_types.i8_eq_equals,
|
||||||
|
'a=i32': stdlib_types.i32_eq_equals,
|
||||||
|
'a=i64': stdlib_types.i64_eq_equals,
|
||||||
|
'a=f32': stdlib_types.f32_eq_equals,
|
||||||
|
'a=f64': stdlib_types.f64_eq_equals,
|
||||||
|
},
|
||||||
|
prelude.Eq.operators['!=']: {
|
||||||
|
'a=u8': stdlib_types.u8_eq_not_equals,
|
||||||
|
'a=u32': stdlib_types.u32_eq_not_equals,
|
||||||
|
'a=u64': stdlib_types.u64_eq_not_equals,
|
||||||
|
'a=i8': stdlib_types.i8_eq_not_equals,
|
||||||
|
'a=i32': stdlib_types.i32_eq_not_equals,
|
||||||
|
'a=i64': stdlib_types.i64_eq_not_equals,
|
||||||
|
'a=f32': stdlib_types.f32_eq_not_equals,
|
||||||
|
'a=f64': stdlib_types.f64_eq_not_equals,
|
||||||
|
},
|
||||||
|
prelude.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,
|
||||||
|
},
|
||||||
|
prelude.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,
|
||||||
|
},
|
||||||
|
prelude.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,
|
||||||
|
},
|
||||||
|
prelude.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,
|
||||||
|
},
|
||||||
|
prelude.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,
|
||||||
|
},
|
||||||
|
prelude.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,
|
||||||
|
},
|
||||||
|
prelude.Bits.methods['shl']: {
|
||||||
|
'a=u8': stdlib_types.u8_bits_logical_shift_left,
|
||||||
|
'a=u32': stdlib_types.u32_bits_logical_shift_left,
|
||||||
|
'a=u64': stdlib_types.u64_bits_logical_shift_left,
|
||||||
|
},
|
||||||
|
prelude.Bits.methods['shr']: {
|
||||||
|
'a=u8': stdlib_types.u8_bits_logical_shift_right,
|
||||||
|
'a=u32': stdlib_types.u32_bits_logical_shift_right,
|
||||||
|
'a=u64': stdlib_types.u64_bits_logical_shift_right,
|
||||||
|
},
|
||||||
|
prelude.Bits.methods['rotl']: {
|
||||||
|
'a=u8': stdlib_types.u8_bits_rotate_left,
|
||||||
|
'a=u32': stdlib_types.u32_bits_rotate_left,
|
||||||
|
'a=u64': stdlib_types.u64_bits_rotate_left,
|
||||||
|
},
|
||||||
|
prelude.Bits.methods['rotr']: {
|
||||||
|
'a=u8': stdlib_types.u8_bits_rotate_right,
|
||||||
|
'a=u32': stdlib_types.u32_bits_rotate_right,
|
||||||
|
'a=u64': stdlib_types.u64_bits_rotate_right,
|
||||||
|
},
|
||||||
|
prelude.Bits.operators['&']: {
|
||||||
|
'a=u8': stdlib_types.u8_bits_bitwise_and,
|
||||||
|
'a=u32': stdlib_types.u32_bits_bitwise_and,
|
||||||
|
'a=u64': stdlib_types.u64_bits_bitwise_and,
|
||||||
|
},
|
||||||
|
prelude.Bits.operators['|']: {
|
||||||
|
'a=u8': stdlib_types.u8_bits_bitwise_or,
|
||||||
|
'a=u32': stdlib_types.u32_bits_bitwise_or,
|
||||||
|
'a=u64': stdlib_types.u64_bits_bitwise_or,
|
||||||
|
},
|
||||||
|
prelude.Bits.operators['^']: {
|
||||||
|
'a=u8': stdlib_types.u8_bits_bitwise_xor,
|
||||||
|
'a=u32': stdlib_types.u32_bits_bitwise_xor,
|
||||||
|
'a=u64': stdlib_types.u64_bits_bitwise_xor,
|
||||||
|
},
|
||||||
|
prelude.Floating.methods['sqrt']: {
|
||||||
|
'a=f32': stdlib_types.f32_floating_sqrt,
|
||||||
|
'a=f64': stdlib_types.f64_floating_sqrt,
|
||||||
|
},
|
||||||
|
prelude.Fractional.methods['ceil']: {
|
||||||
|
'a=f32': stdlib_types.f32_fractional_ceil,
|
||||||
|
'a=f64': stdlib_types.f64_fractional_ceil,
|
||||||
|
},
|
||||||
|
prelude.Fractional.methods['floor']: {
|
||||||
|
'a=f32': stdlib_types.f32_fractional_floor,
|
||||||
|
'a=f64': stdlib_types.f64_fractional_floor,
|
||||||
|
},
|
||||||
|
prelude.Fractional.methods['trunc']: {
|
||||||
|
'a=f32': stdlib_types.f32_fractional_trunc,
|
||||||
|
'a=f64': stdlib_types.f64_fractional_trunc,
|
||||||
|
},
|
||||||
|
prelude.Fractional.methods['nearest']: {
|
||||||
|
'a=f32': stdlib_types.f32_fractional_nearest,
|
||||||
|
'a=f64': stdlib_types.f64_fractional_nearest,
|
||||||
|
},
|
||||||
|
prelude.Fractional.operators['/']: {
|
||||||
|
'a=f32': stdlib_types.f32_fractional_div,
|
||||||
|
'a=f64': stdlib_types.f64_fractional_div,
|
||||||
|
},
|
||||||
|
prelude.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,
|
||||||
|
},
|
||||||
|
prelude.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,
|
||||||
|
},
|
||||||
|
prelude.IntNum.methods['abs']: {
|
||||||
|
'a=i32': stdlib_types.i32_intnum_abs,
|
||||||
|
'a=i64': stdlib_types.i64_intnum_abs,
|
||||||
|
'a=f32': stdlib_types.f32_intnum_abs,
|
||||||
|
'a=f64': stdlib_types.f64_intnum_abs,
|
||||||
|
},
|
||||||
|
prelude.IntNum.methods['neg']: {
|
||||||
|
'a=i32': stdlib_types.i32_intnum_neg,
|
||||||
|
'a=i64': stdlib_types.i64_intnum_neg,
|
||||||
|
'a=f32': stdlib_types.f32_intnum_neg,
|
||||||
|
'a=f64': stdlib_types.f64_intnum_neg,
|
||||||
|
},
|
||||||
|
prelude.NatNum.operators['+']: {
|
||||||
|
'a=u32': stdlib_types.u32_natnum_add,
|
||||||
|
'a=u64': stdlib_types.u64_natnum_add,
|
||||||
|
'a=i32': stdlib_types.i32_natnum_add,
|
||||||
|
'a=i64': stdlib_types.i64_natnum_add,
|
||||||
|
'a=f32': stdlib_types.f32_natnum_add,
|
||||||
|
'a=f64': stdlib_types.f64_natnum_add,
|
||||||
|
},
|
||||||
|
prelude.NatNum.operators['-']: {
|
||||||
|
'a=u32': stdlib_types.u32_natnum_sub,
|
||||||
|
'a=u64': stdlib_types.u64_natnum_sub,
|
||||||
|
'a=i32': stdlib_types.i32_natnum_sub,
|
||||||
|
'a=i64': stdlib_types.i64_natnum_sub,
|
||||||
|
'a=f32': stdlib_types.f32_natnum_sub,
|
||||||
|
'a=f64': stdlib_types.f64_natnum_sub,
|
||||||
|
},
|
||||||
|
prelude.NatNum.operators['*']: {
|
||||||
|
'a=u32': stdlib_types.u32_natnum_mul,
|
||||||
|
'a=u64': stdlib_types.u64_natnum_mul,
|
||||||
|
'a=i32': stdlib_types.i32_natnum_mul,
|
||||||
|
'a=i64': stdlib_types.i64_natnum_mul,
|
||||||
|
'a=f32': stdlib_types.f32_natnum_mul,
|
||||||
|
'a=f64': stdlib_types.f64_natnum_mul,
|
||||||
|
},
|
||||||
|
prelude.NatNum.operators['<<']: {
|
||||||
|
'a=u32': stdlib_types.u32_natnum_arithmic_shift_left,
|
||||||
|
'a=u64': stdlib_types.u64_natnum_arithmic_shift_left,
|
||||||
|
'a=i32': stdlib_types.i32_natnum_arithmic_shift_left,
|
||||||
|
'a=i64': stdlib_types.i64_natnum_arithmic_shift_left,
|
||||||
|
'a=f32': stdlib_types.f32_natnum_arithmic_shift_left,
|
||||||
|
'a=f64': stdlib_types.f64_natnum_arithmic_shift_left,
|
||||||
|
},
|
||||||
|
prelude.NatNum.operators['>>']: {
|
||||||
|
'a=u32': stdlib_types.u32_natnum_arithmic_shift_right,
|
||||||
|
'a=u64': stdlib_types.u64_natnum_arithmic_shift_right,
|
||||||
|
'a=i32': stdlib_types.i32_natnum_arithmic_shift_right,
|
||||||
|
'a=i64': stdlib_types.i64_natnum_arithmic_shift_right,
|
||||||
|
'a=f32': stdlib_types.f32_natnum_arithmic_shift_right,
|
||||||
|
'a=f64': stdlib_types.f64_natnum_arithmic_shift_right,
|
||||||
|
},
|
||||||
|
prelude.Sized_.methods['len']: {
|
||||||
|
'a=bytes': stdlib_types.bytes_sized_len,
|
||||||
|
},
|
||||||
|
prelude.Extendable.methods['extend']: {
|
||||||
|
'a=u8,b=u32': stdlib_types.u8_u32_extend,
|
||||||
|
'a=u8,b=u64': stdlib_types.u8_u64_extend,
|
||||||
|
'a=u32,b=u64': stdlib_types.u32_u64_extend,
|
||||||
|
'a=i8,b=i32': stdlib_types.i8_i32_extend,
|
||||||
|
'a=i8,b=i64': stdlib_types.i8_i64_extend,
|
||||||
|
'a=i32,b=i64': stdlib_types.i32_i64_extend,
|
||||||
|
},
|
||||||
|
prelude.Extendable.methods['wrap']: {
|
||||||
|
'a=u8,b=u32': stdlib_types.u8_u32_wrap,
|
||||||
|
'a=u8,b=u64': stdlib_types.u8_u64_wrap,
|
||||||
|
'a=u32,b=u64': stdlib_types.u32_u64_wrap,
|
||||||
|
'a=i8,b=i32': stdlib_types.i8_i32_wrap,
|
||||||
|
'a=i8,b=i64': stdlib_types.i8_i64_wrap,
|
||||||
|
'a=i32,b=i64': stdlib_types.i32_i64_wrap,
|
||||||
|
},
|
||||||
|
prelude.Promotable.methods['promote']: {
|
||||||
|
'a=f32,b=f64': stdlib_types.f32_f64_promote,
|
||||||
|
},
|
||||||
|
prelude.Promotable.methods['demote']: {
|
||||||
|
'a=f32,b=f64': stdlib_types.f32_f64_demote,
|
||||||
|
},
|
||||||
|
prelude.Foldable.methods['sum']: {
|
||||||
|
'a=i32,t=i32[4]': stdlib_types.static_array_i32_4_sum,
|
||||||
|
'a=i32,t=i32[5]': stdlib_types.static_array_i32_5_sum,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
def phasm_compile(inp: ourlang.Module) -> wasm.Module:
|
def phasm_compile(inp: ourlang.Module) -> wasm.Module:
|
||||||
"""
|
"""
|
||||||
Public method for compiling a parsed Phasm module into
|
Public method for compiling a parsed Phasm module into
|
||||||
@ -98,24 +334,19 @@ def tuple_instantiation(wgn: WasmGenerator, inp: ourlang.TupleInstantiation) ->
|
|||||||
"""
|
"""
|
||||||
assert inp.type3 is not None, TYPE3_ASSERTION_ERROR
|
assert inp.type3 is not None, TYPE3_ASSERTION_ERROR
|
||||||
|
|
||||||
args: tuple[type3types.Type3, ...]
|
args: list[type3types.Type3] = []
|
||||||
|
|
||||||
if isinstance(inp.type3.application, type3types.TypeApplication_TypeStar):
|
sa_args = prelude.static_array.did_construct(inp.type3)
|
||||||
# Possibly paranoid assert. If we have a future variadic type,
|
if sa_args is not None:
|
||||||
# does it also do this tuple instantation like this?
|
sa_type, sa_len = sa_args
|
||||||
assert isinstance(inp.type3.application.constructor, type3types.TypeConstructor_Tuple)
|
args = [sa_type for _ in range(sa_len.value)]
|
||||||
|
|
||||||
args = inp.type3.application.arguments
|
if not args:
|
||||||
elif isinstance(inp.type3.application, type3types.TypeApplication_TypeInt):
|
tp_args = prelude.tuple_.did_construct(inp.type3)
|
||||||
# Possibly paranoid assert. If we have a future type of kind * -> Int -> *,
|
if tp_args is None:
|
||||||
# does it also do this tuple instantation like this?
|
raise NotImplementedError
|
||||||
assert isinstance(inp.type3.application.constructor, type3types.TypeConstructor_StaticArray)
|
|
||||||
|
|
||||||
sa_type, sa_len = inp.type3.application.arguments
|
args = list(tp_args)
|
||||||
|
|
||||||
args = tuple(sa_type for _ in range(sa_len.value))
|
|
||||||
else:
|
|
||||||
raise NotImplementedError('tuple_instantiation', inp.type3)
|
|
||||||
|
|
||||||
comment_elements = ''
|
comment_elements = ''
|
||||||
for element in inp.elements:
|
for element in inp.elements:
|
||||||
@ -151,78 +382,6 @@ def tuple_instantiation(wgn: WasmGenerator, inp: ourlang.TupleInstantiation) ->
|
|||||||
# Return the allocated address
|
# Return the allocated address
|
||||||
wgn.local.get(tmp_var)
|
wgn.local.get(tmp_var)
|
||||||
|
|
||||||
def expression_subscript_bytes(
|
|
||||||
attrs: tuple[WasmGenerator, ourlang.Subscript],
|
|
||||||
) -> None:
|
|
||||||
wgn, inp = attrs
|
|
||||||
|
|
||||||
expression(wgn, inp.varref)
|
|
||||||
expression(wgn, inp.index)
|
|
||||||
wgn.call(stdlib_types.__subscript_bytes__)
|
|
||||||
|
|
||||||
def expression_subscript_static_array(
|
|
||||||
attrs: tuple[WasmGenerator, ourlang.Subscript],
|
|
||||||
args: tuple[type3types.Type3, type3types.IntType3],
|
|
||||||
) -> None:
|
|
||||||
wgn, inp = attrs
|
|
||||||
|
|
||||||
el_type, el_len = args
|
|
||||||
|
|
||||||
# OPTIMIZE: If index is a constant, we can use offset instead of multiply
|
|
||||||
# and we don't need to do the out of bounds check
|
|
||||||
|
|
||||||
expression(wgn, inp.varref)
|
|
||||||
|
|
||||||
tmp_var = wgn.temp_var_i32('index')
|
|
||||||
expression(wgn, inp.index)
|
|
||||||
wgn.local.tee(tmp_var)
|
|
||||||
|
|
||||||
# Out of bounds check based on el_len.value
|
|
||||||
wgn.i32.const(el_len.value)
|
|
||||||
wgn.i32.ge_u()
|
|
||||||
with wgn.if_():
|
|
||||||
wgn.unreachable(comment='Out of bounds')
|
|
||||||
|
|
||||||
wgn.local.get(tmp_var)
|
|
||||||
wgn.i32.const(calculate_alloc_size(el_type))
|
|
||||||
wgn.i32.mul()
|
|
||||||
wgn.i32.add()
|
|
||||||
|
|
||||||
mtyp = LOAD_STORE_TYPE_MAP[el_type.name]
|
|
||||||
|
|
||||||
wgn.add_statement(f'{mtyp}.load')
|
|
||||||
|
|
||||||
def expression_subscript_tuple(
|
|
||||||
attrs: tuple[WasmGenerator, ourlang.Subscript],
|
|
||||||
args: tuple[type3types.Type3, ...],
|
|
||||||
) -> None:
|
|
||||||
wgn, inp = attrs
|
|
||||||
|
|
||||||
assert isinstance(inp.index, ourlang.ConstantPrimitive)
|
|
||||||
assert isinstance(inp.index.value, int)
|
|
||||||
|
|
||||||
offset = 0
|
|
||||||
for el_type in args[0:inp.index.value]:
|
|
||||||
assert el_type is not None, TYPE3_ASSERTION_ERROR
|
|
||||||
offset += calculate_alloc_size(el_type)
|
|
||||||
|
|
||||||
el_type = args[inp.index.value]
|
|
||||||
assert el_type is not None, TYPE3_ASSERTION_ERROR
|
|
||||||
|
|
||||||
expression(wgn, inp.varref)
|
|
||||||
|
|
||||||
if (prelude.InternalPassAsPointer, (el_type, )) in prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING:
|
|
||||||
mtyp = 'i32'
|
|
||||||
else:
|
|
||||||
mtyp = LOAD_STORE_TYPE_MAP[el_type.name]
|
|
||||||
|
|
||||||
wgn.add_statement(f'{mtyp}.load', f'offset={offset}')
|
|
||||||
|
|
||||||
SUBSCRIPT_ROUTER = TypeApplicationRouter[tuple[WasmGenerator, ourlang.Subscript], None]()
|
|
||||||
SUBSCRIPT_ROUTER.add_n(prelude.bytes_, expression_subscript_bytes)
|
|
||||||
SUBSCRIPT_ROUTER.add(prelude.static_array, expression_subscript_static_array)
|
|
||||||
SUBSCRIPT_ROUTER.add(prelude.tuple_, expression_subscript_tuple)
|
|
||||||
|
|
||||||
def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
||||||
"""
|
"""
|
||||||
Compile: Any expression
|
Compile: Any expression
|
||||||
@ -292,6 +451,8 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
|||||||
expression(wgn, inp.left)
|
expression(wgn, inp.left)
|
||||||
expression(wgn, inp.right)
|
expression(wgn, inp.right)
|
||||||
|
|
||||||
|
assert inp.type3 is not None, TYPE3_ASSERTION_ERROR
|
||||||
|
|
||||||
type_var_map: Dict[Union[type3functions.TypeVariable, type3functions.TypeConstructorVariable], type3types.Type3] = {}
|
type_var_map: Dict[Union[type3functions.TypeVariable, type3functions.TypeConstructorVariable], type3types.Type3] = {}
|
||||||
|
|
||||||
for type_var, arg_expr in zip(inp.operator.signature.args, [inp.left, inp.right, inp], strict=True):
|
for type_var, arg_expr in zip(inp.operator.signature.args, [inp.left, inp.right, inp], strict=True):
|
||||||
@ -302,10 +463,18 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
|||||||
assert arg_expr.type3 is not None, TYPE3_ASSERTION_ERROR
|
assert arg_expr.type3 is not None, TYPE3_ASSERTION_ERROR
|
||||||
type_var_map[type_var] = arg_expr.type3
|
type_var_map[type_var] = arg_expr.type3
|
||||||
|
|
||||||
router = prelude.PRELUDE_TYPE_CLASS_INSTANCE_METHODS[inp.operator]
|
instance_key = ','.join(
|
||||||
router(wgn, type_var_map)
|
f'{k.letter}={v.name}'
|
||||||
|
for k, v in type_var_map.items()
|
||||||
|
)
|
||||||
|
|
||||||
|
instance = INSTANCES.get(inp.operator, {}).get(instance_key, None)
|
||||||
|
if instance is not None:
|
||||||
|
instance(wgn)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
raise NotImplementedError(inp.operator, instance_key)
|
||||||
|
|
||||||
if isinstance(inp, ourlang.FunctionCall):
|
if isinstance(inp, ourlang.FunctionCall):
|
||||||
for arg in inp.arguments:
|
for arg in inp.arguments:
|
||||||
expression(wgn, arg)
|
expression(wgn, arg)
|
||||||
@ -326,13 +495,18 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
|||||||
|
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
router = prelude.PRELUDE_TYPE_CLASS_INSTANCE_METHODS[inp.function]
|
instance_key = ','.join(
|
||||||
try:
|
f'{k.letter}={v.name}'
|
||||||
router(wgn, type_var_map)
|
for k, v in sorted(type_var_map.items(), key=lambda x: x[0].letter)
|
||||||
except NoRouteForTypeException:
|
)
|
||||||
raise NotImplementedError(str(inp.function), type_var_map)
|
|
||||||
|
instance = INSTANCES.get(inp.function, {}).get(instance_key, None)
|
||||||
|
if instance is not None:
|
||||||
|
instance(wgn)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
raise NotImplementedError(inp.function, instance_key)
|
||||||
|
|
||||||
wgn.add_statement('call', '${}'.format(inp.function.name))
|
wgn.add_statement('call', '${}'.format(inp.function.name))
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -343,22 +517,81 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
|||||||
if isinstance(inp, ourlang.Subscript):
|
if isinstance(inp, ourlang.Subscript):
|
||||||
assert inp.varref.type3 is not None, TYPE3_ASSERTION_ERROR
|
assert inp.varref.type3 is not None, TYPE3_ASSERTION_ERROR
|
||||||
|
|
||||||
# Type checker guarantees we don't get routing errors
|
if inp.varref.type3 is prelude.bytes_:
|
||||||
SUBSCRIPT_ROUTER((wgn, inp, ), inp.varref.type3)
|
expression(wgn, inp.varref)
|
||||||
|
expression(wgn, inp.index)
|
||||||
|
wgn.call(stdlib_types.__subscript_bytes__)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
assert inp.varref.type3 is not None, TYPE3_ASSERTION_ERROR
|
||||||
|
|
||||||
|
sa_args = prelude.static_array.did_construct(inp.varref.type3)
|
||||||
|
if sa_args is not None:
|
||||||
|
el_type, el_len = sa_args
|
||||||
|
|
||||||
|
# OPTIMIZE: If index is a constant, we can use offset instead of multiply
|
||||||
|
# and we don't need to do the out of bounds check
|
||||||
|
|
||||||
|
expression(wgn, inp.varref)
|
||||||
|
|
||||||
|
tmp_var = wgn.temp_var_i32('index')
|
||||||
|
expression(wgn, inp.index)
|
||||||
|
wgn.local.tee(tmp_var)
|
||||||
|
|
||||||
|
# Out of bounds check based on el_len.value
|
||||||
|
wgn.i32.const(el_len.value)
|
||||||
|
wgn.i32.ge_u()
|
||||||
|
with wgn.if_():
|
||||||
|
wgn.unreachable(comment='Out of bounds')
|
||||||
|
|
||||||
|
wgn.local.get(tmp_var)
|
||||||
|
wgn.i32.const(calculate_alloc_size(el_type))
|
||||||
|
wgn.i32.mul()
|
||||||
|
wgn.i32.add()
|
||||||
|
|
||||||
|
mtyp = LOAD_STORE_TYPE_MAP[el_type.name]
|
||||||
|
|
||||||
|
wgn.add_statement(f'{mtyp}.load')
|
||||||
|
return
|
||||||
|
|
||||||
|
tp_args = prelude.tuple_.did_construct(inp.varref.type3)
|
||||||
|
if tp_args is not None:
|
||||||
|
assert isinstance(inp.index, ourlang.ConstantPrimitive)
|
||||||
|
assert isinstance(inp.index.value, int)
|
||||||
|
|
||||||
|
offset = 0
|
||||||
|
for el_type in tp_args[0:inp.index.value]:
|
||||||
|
assert el_type is not None, TYPE3_ASSERTION_ERROR
|
||||||
|
offset += calculate_alloc_size(el_type)
|
||||||
|
|
||||||
|
el_type = tp_args[inp.index.value]
|
||||||
|
assert el_type is not None, TYPE3_ASSERTION_ERROR
|
||||||
|
|
||||||
|
expression(wgn, inp.varref)
|
||||||
|
|
||||||
|
if (prelude.InternalPassAsPointer, (el_type, )) in prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING:
|
||||||
|
mtyp = 'i32'
|
||||||
|
else:
|
||||||
|
mtyp = LOAD_STORE_TYPE_MAP[el_type.name]
|
||||||
|
|
||||||
|
wgn.add_statement(f'{mtyp}.load', f'offset={offset}')
|
||||||
|
return
|
||||||
|
|
||||||
|
raise NotImplementedError(expression, inp, inp.varref.type3)
|
||||||
|
|
||||||
if isinstance(inp, ourlang.AccessStructMember):
|
if isinstance(inp, ourlang.AccessStructMember):
|
||||||
assert inp.struct_type3 is not None, TYPE3_ASSERTION_ERROR
|
assert inp.struct_type3 is not None, TYPE3_ASSERTION_ERROR
|
||||||
|
|
||||||
assert isinstance(inp.struct_type3.application, type3types.TypeApplication_Struct)
|
st_args = prelude.struct.did_construct(inp.struct_type3)
|
||||||
|
assert st_args is not None
|
||||||
|
|
||||||
member_type = dict(inp.struct_type3.application.arguments)[inp.member]
|
member_type = st_args[inp.member]
|
||||||
|
|
||||||
mtyp = LOAD_STORE_TYPE_MAP[member_type.name]
|
mtyp = LOAD_STORE_TYPE_MAP[member_type.name]
|
||||||
|
|
||||||
expression(wgn, inp.varref)
|
expression(wgn, inp.varref)
|
||||||
wgn.add_statement(f'{mtyp}.load', 'offset=' + str(calculate_member_offset(
|
wgn.add_statement(f'{mtyp}.load', 'offset=' + str(calculate_member_offset(
|
||||||
inp.struct_type3.name, inp.struct_type3.application.arguments, inp.member
|
inp.struct_type3.name, st_args, inp.member
|
||||||
)))
|
)))
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -734,9 +967,8 @@ def module(inp: ourlang.Module) -> wasm.Module:
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
def _generate_struct_constructor(wgn: WasmGenerator, inp: ourlang.StructConstructor) -> None:
|
def _generate_struct_constructor(wgn: WasmGenerator, inp: ourlang.StructConstructor) -> None:
|
||||||
assert isinstance(inp.struct_type3.application, type3types.TypeApplication_Struct)
|
st_args = prelude.struct.did_construct(inp.struct_type3)
|
||||||
|
assert st_args is not None
|
||||||
st_args = inp.struct_type3.application.arguments
|
|
||||||
|
|
||||||
tmp_var = wgn.temp_var_i32('struct_adr')
|
tmp_var = wgn.temp_var_i32('struct_adr')
|
||||||
|
|
||||||
@ -746,7 +978,7 @@ def _generate_struct_constructor(wgn: WasmGenerator, inp: ourlang.StructConstruc
|
|||||||
wgn.local.set(tmp_var)
|
wgn.local.set(tmp_var)
|
||||||
|
|
||||||
# Store each member individually
|
# Store each member individually
|
||||||
for memname, mtyp3 in st_args:
|
for memname, mtyp3 in st_args.items():
|
||||||
mtyp: Optional[str]
|
mtyp: Optional[str]
|
||||||
if (prelude.InternalPassAsPointer, (mtyp3, )) in prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING:
|
if (prelude.InternalPassAsPointer, (mtyp3, )) in prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING:
|
||||||
mtyp = 'i32'
|
mtyp = 'i32'
|
||||||
|
|||||||
@ -7,7 +7,7 @@ from typing import Dict, Iterable, List, Optional, Union
|
|||||||
from . import prelude
|
from . import prelude
|
||||||
from .type3.functions import FunctionSignature, TypeVariableContext
|
from .type3.functions import FunctionSignature, TypeVariableContext
|
||||||
from .type3.typeclasses import Type3ClassMethod
|
from .type3.typeclasses import Type3ClassMethod
|
||||||
from .type3.types import Type3, TypeApplication_Struct
|
from .type3.types import Type3
|
||||||
|
|
||||||
|
|
||||||
class Expression:
|
class Expression:
|
||||||
@ -341,9 +341,9 @@ class StructConstructor(Function):
|
|||||||
def __init__(self, struct_type3: Type3) -> None:
|
def __init__(self, struct_type3: Type3) -> None:
|
||||||
super().__init__(f'@{struct_type3.name}@__init___@', -1)
|
super().__init__(f'@{struct_type3.name}@__init___@', -1)
|
||||||
|
|
||||||
assert isinstance(struct_type3.application, TypeApplication_Struct)
|
st_args = prelude.struct.did_construct(struct_type3)
|
||||||
|
assert st_args is not None
|
||||||
for mem, typ in struct_type3.application.arguments:
|
for mem, typ in st_args.items():
|
||||||
self.posonlyargs.append(FunctionParam(mem, typ, ))
|
self.posonlyargs.append(FunctionParam(mem, typ, ))
|
||||||
self.signature.args.append(typ)
|
self.signature.args.append(typ)
|
||||||
|
|
||||||
|
|||||||
@ -246,7 +246,7 @@ class OurVisitor:
|
|||||||
|
|
||||||
members[stmt.target.id] = self.visit_type(module, stmt.annotation)
|
members[stmt.target.id] = self.visit_type(module, stmt.annotation)
|
||||||
|
|
||||||
return StructDefinition(prelude.struct(node.name, tuple(members.items())), node.lineno)
|
return StructDefinition(prelude.struct(node.name, members), node.lineno)
|
||||||
|
|
||||||
def pre_visit_Module_AnnAssign(self, module: Module, node: ast.AnnAssign) -> ModuleConstantDef:
|
def pre_visit_Module_AnnAssign(self, module: Module, node: ast.AnnAssign) -> ModuleConstantDef:
|
||||||
if not isinstance(node.target, ast.Name):
|
if not isinstance(node.target, ast.Name):
|
||||||
|
|||||||
@ -1,115 +1,67 @@
|
|||||||
"""
|
"""
|
||||||
The prelude are all the builtin types, type classes and methods
|
The prelude are all the builtin types, type classes and methods
|
||||||
"""
|
"""
|
||||||
from typing import Callable
|
from typing import Any, Union
|
||||||
from warnings import warn
|
|
||||||
|
|
||||||
from phasm.stdlib import types as stdtypes
|
from ..type3.functions import (
|
||||||
from phasm.wasmgenerator import Generator
|
Constraint_TypeClassInstanceExists,
|
||||||
|
TypeConstructorVariable,
|
||||||
from ..type3.functions import TypeVariable
|
TypeVariable,
|
||||||
from ..type3.routers import FunctionSignatureRouter
|
)
|
||||||
from ..type3.typeclasses import Type3Class, Type3ClassMethod
|
from ..type3.typeclasses import Type3Class
|
||||||
from ..type3.types import (
|
from ..type3.types import (
|
||||||
IntType3,
|
IntType3,
|
||||||
Type3,
|
Type3,
|
||||||
TypeApplication_Nullary,
|
TypeConstructor,
|
||||||
TypeConstructor_StaticArray,
|
TypeConstructor_StaticArray,
|
||||||
TypeConstructor_Struct,
|
TypeConstructor_Struct,
|
||||||
TypeConstructor_Tuple,
|
TypeConstructor_Tuple,
|
||||||
)
|
)
|
||||||
|
|
||||||
PRELUDE_TYPE_CLASS_INSTANCES_EXISTING: set[tuple[Type3Class, tuple[Union[Type3, TypeConstructor_Base[Any], TypeConstructor_Struct], ...]]] = set()
|
PRELUDE_TYPE_CLASS_INSTANCES_EXISTING: set[tuple[Type3Class, tuple[Union[Type3, TypeConstructor[Any], TypeConstructor_Struct], ...]]] = set()
|
||||||
|
|
||||||
PRELUDE_TYPE_CLASS_INSTANCE_METHODS: dict[Type3ClassMethod, FunctionSignatureRouter[Generator, None]] = {}
|
|
||||||
|
|
||||||
class MissingImplementationException(Exception):
|
def instance_type_class(cls: Type3Class, *typ: Union[Type3, TypeConstructor[Any], TypeConstructor_Struct]) -> None:
|
||||||
pass
|
|
||||||
|
|
||||||
class MissingImplementationWarning(Warning):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def instance_type_class(
|
|
||||||
cls: Type3Class,
|
|
||||||
*typ: Type3,
|
|
||||||
methods: dict[str, Callable[[Generator], None]] = {},
|
|
||||||
operators: dict[str, Callable[[Generator], None]] = {},
|
|
||||||
) -> None:
|
|
||||||
global PRELUDE_TYPE_CLASS_INSTANCES_EXISTING
|
global PRELUDE_TYPE_CLASS_INSTANCES_EXISTING
|
||||||
global PRELUDE_TYPE_CLASS_INSTANCE_METHODS
|
|
||||||
|
|
||||||
assert len(cls.args) == len(typ)
|
|
||||||
|
|
||||||
type_var_map = {}
|
|
||||||
for arg_tv, arg_tp in zip(cls.args, typ, strict=True):
|
|
||||||
type_var_map[arg_tv] = arg_tp
|
|
||||||
|
|
||||||
# TODO: Check for required existing instantiations
|
# TODO: Check for required existing instantiations
|
||||||
|
|
||||||
PRELUDE_TYPE_CLASS_INSTANCES_EXISTING.add((cls, tuple(typ), ))
|
PRELUDE_TYPE_CLASS_INSTANCES_EXISTING.add((cls, tuple(typ), ))
|
||||||
|
|
||||||
for method_name, method in cls.methods.items():
|
none = Type3('none')
|
||||||
router = PRELUDE_TYPE_CLASS_INSTANCE_METHODS.get(method)
|
|
||||||
if router is None:
|
|
||||||
router = FunctionSignatureRouter[Generator, None](method.signature)
|
|
||||||
PRELUDE_TYPE_CLASS_INSTANCE_METHODS[method] = router
|
|
||||||
|
|
||||||
try:
|
|
||||||
generator = methods[method_name]
|
|
||||||
except KeyError:
|
|
||||||
warn(MissingImplementationWarning(str(method), cls.name + ' ' + ' '.join(x.name for x in typ)))
|
|
||||||
continue
|
|
||||||
|
|
||||||
router.add(type_var_map, generator)
|
|
||||||
|
|
||||||
for operator_name, operator in cls.operators.items():
|
|
||||||
router = PRELUDE_TYPE_CLASS_INSTANCE_METHODS.get(operator)
|
|
||||||
if router is None:
|
|
||||||
router = FunctionSignatureRouter[Generator, None](operator.signature)
|
|
||||||
PRELUDE_TYPE_CLASS_INSTANCE_METHODS[operator] = router
|
|
||||||
|
|
||||||
try:
|
|
||||||
generator = operators[operator_name]
|
|
||||||
except KeyError:
|
|
||||||
warn(MissingImplementationWarning(str(operator), cls.name + ' ' + ' '.join(x.name for x in typ)))
|
|
||||||
continue
|
|
||||||
|
|
||||||
router.add(type_var_map, generator)
|
|
||||||
|
|
||||||
none = Type3('none', TypeApplication_Nullary(None, None))
|
|
||||||
"""
|
"""
|
||||||
The none type, for when functions simply don't return anything. e.g., IO().
|
The none type, for when functions simply don't return anything. e.g., IO().
|
||||||
"""
|
"""
|
||||||
|
|
||||||
bool_ = Type3('bool', TypeApplication_Nullary(None, None))
|
bool_ = Type3('bool')
|
||||||
"""
|
"""
|
||||||
The bool type, either True or False
|
The bool type, either True or False
|
||||||
|
|
||||||
Suffixes with an underscores, as it's a Python builtin
|
Suffixes with an underscores, as it's a Python builtin
|
||||||
"""
|
"""
|
||||||
|
|
||||||
u8 = Type3('u8', TypeApplication_Nullary(None, None))
|
u8 = Type3('u8')
|
||||||
"""
|
"""
|
||||||
The unsigned 8-bit integer type.
|
The unsigned 8-bit integer type.
|
||||||
|
|
||||||
Operations on variables employ modular arithmetic, with modulus 2^8.
|
Operations on variables employ modular arithmetic, with modulus 2^8.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
u32 = Type3('u32', TypeApplication_Nullary(None, None))
|
u32 = Type3('u32')
|
||||||
"""
|
"""
|
||||||
The unsigned 32-bit integer type.
|
The unsigned 32-bit integer type.
|
||||||
|
|
||||||
Operations on variables employ modular arithmetic, with modulus 2^32.
|
Operations on variables employ modular arithmetic, with modulus 2^32.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
u64 = Type3('u64', TypeApplication_Nullary(None, None))
|
u64 = Type3('u64')
|
||||||
"""
|
"""
|
||||||
The unsigned 64-bit integer type.
|
The unsigned 64-bit integer type.
|
||||||
|
|
||||||
Operations on variables employ modular arithmetic, with modulus 2^64.
|
Operations on variables employ modular arithmetic, with modulus 2^64.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
i8 = Type3('i8', TypeApplication_Nullary(None, None))
|
i8 = Type3('i8')
|
||||||
"""
|
"""
|
||||||
The signed 8-bit integer type.
|
The signed 8-bit integer type.
|
||||||
|
|
||||||
@ -117,7 +69,7 @@ Operations on variables employ modular arithmetic, with modulus 2^8, but
|
|||||||
with the middel point being 0.
|
with the middel point being 0.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
i32 = Type3('i32', TypeApplication_Nullary(None, None))
|
i32 = Type3('i32')
|
||||||
"""
|
"""
|
||||||
The unsigned 32-bit integer type.
|
The unsigned 32-bit integer type.
|
||||||
|
|
||||||
@ -125,7 +77,7 @@ Operations on variables employ modular arithmetic, with modulus 2^32, but
|
|||||||
with the middel point being 0.
|
with the middel point being 0.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
i64 = Type3('i64', TypeApplication_Nullary(None, None))
|
i64 = Type3('i64')
|
||||||
"""
|
"""
|
||||||
The unsigned 64-bit integer type.
|
The unsigned 64-bit integer type.
|
||||||
|
|
||||||
@ -133,17 +85,17 @@ Operations on variables employ modular arithmetic, with modulus 2^64, but
|
|||||||
with the middel point being 0.
|
with the middel point being 0.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
f32 = Type3('f32', TypeApplication_Nullary(None, None))
|
f32 = Type3('f32')
|
||||||
"""
|
"""
|
||||||
A 32-bits IEEE 754 float, of 32 bits width.
|
A 32-bits IEEE 754 float, of 32 bits width.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
f64 = Type3('f64', TypeApplication_Nullary(None, None))
|
f64 = Type3('f64')
|
||||||
"""
|
"""
|
||||||
A 32-bits IEEE 754 float, of 64 bits width.
|
A 32-bits IEEE 754 float, of 64 bits width.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
bytes_ = Type3('bytes', TypeApplication_Nullary(None, None))
|
bytes_ = Type3('bytes')
|
||||||
"""
|
"""
|
||||||
This is a runtime-determined length piece of memory that can be indexed at runtime.
|
This is a runtime-determined length piece of memory that can be indexed at runtime.
|
||||||
"""
|
"""
|
||||||
@ -172,7 +124,7 @@ It should be applied with zero or more arguments. It has a compile time
|
|||||||
determined length, and each argument can be different.
|
determined length, and each argument can be different.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def st_on_create(args: tuple[tuple[str, Type3], ...], typ: Type3) -> None:
|
def st_on_create(typ: Type3) -> None:
|
||||||
instance_type_class(InternalPassAsPointer, typ)
|
instance_type_class(InternalPassAsPointer, typ)
|
||||||
|
|
||||||
struct = TypeConstructor_Struct('struct', on_create=st_on_create)
|
struct = TypeConstructor_Struct('struct', on_create=st_on_create)
|
||||||
@ -198,9 +150,9 @@ PRELUDE_TYPES: dict[str, Type3] = {
|
|||||||
a = TypeVariable('a')
|
a = TypeVariable('a')
|
||||||
b = TypeVariable('b')
|
b = TypeVariable('b')
|
||||||
|
|
||||||
t = TypeConstructorVariable('t')
|
t = TypeConstructorVariable('t', [])
|
||||||
|
|
||||||
InternalPassAsPointer = Type3Class('InternalPassAsPointer', (a, ), methods={}, operators={})
|
InternalPassAsPointer = Type3Class('InternalPassAsPointer', [a], methods={}, operators={})
|
||||||
"""
|
"""
|
||||||
Internal type class to keep track which types we pass arounds as a pointer.
|
Internal type class to keep track which types we pass arounds as a pointer.
|
||||||
"""
|
"""
|
||||||
@ -210,46 +162,22 @@ instance_type_class(InternalPassAsPointer, bytes_)
|
|||||||
# instance_type_class(InternalPassAsPointer, tuple_)
|
# instance_type_class(InternalPassAsPointer, tuple_)
|
||||||
# instance_type_class(InternalPassAsPointer, struct)
|
# instance_type_class(InternalPassAsPointer, struct)
|
||||||
|
|
||||||
Eq = Type3Class('Eq', (a, ), methods={}, operators={
|
Eq = Type3Class('Eq', [a], methods={}, operators={
|
||||||
'==': [a, a, bool_],
|
'==': [a, a, bool_],
|
||||||
'!=': [a, a, bool_],
|
'!=': [a, a, bool_],
|
||||||
# FIXME: Do we want to expose 'eqz'? Or is that a compiler optimization?
|
# FIXME: Do we want to expose 'eqz'? Or is that a compiler optimization?
|
||||||
})
|
})
|
||||||
|
|
||||||
instance_type_class(Eq, u8, operators={
|
instance_type_class(Eq, u8)
|
||||||
'==': stdtypes.u8_eq_equals,
|
instance_type_class(Eq, u32)
|
||||||
'!=': stdtypes.u8_eq_not_equals,
|
instance_type_class(Eq, u64)
|
||||||
})
|
instance_type_class(Eq, i8)
|
||||||
instance_type_class(Eq, u32, operators={
|
instance_type_class(Eq, i32)
|
||||||
'==': stdtypes.u32_eq_equals,
|
instance_type_class(Eq, i64)
|
||||||
'!=': stdtypes.u32_eq_not_equals,
|
instance_type_class(Eq, f32)
|
||||||
})
|
instance_type_class(Eq, f64)
|
||||||
instance_type_class(Eq, u64, operators={
|
|
||||||
'==': stdtypes.u64_eq_equals,
|
|
||||||
'!=': stdtypes.u64_eq_not_equals,
|
|
||||||
})
|
|
||||||
instance_type_class(Eq, i8, operators={
|
|
||||||
'==': stdtypes.i8_eq_equals,
|
|
||||||
'!=': stdtypes.i8_eq_not_equals,
|
|
||||||
})
|
|
||||||
instance_type_class(Eq, i32, operators={
|
|
||||||
'==': stdtypes.i32_eq_equals,
|
|
||||||
'!=': stdtypes.i32_eq_not_equals,
|
|
||||||
})
|
|
||||||
instance_type_class(Eq, i64, operators={
|
|
||||||
'==': stdtypes.i64_eq_equals,
|
|
||||||
'!=': stdtypes.i64_eq_not_equals,
|
|
||||||
})
|
|
||||||
instance_type_class(Eq, f32, operators={
|
|
||||||
'==': stdtypes.f32_eq_equals,
|
|
||||||
'!=': stdtypes.f32_eq_not_equals,
|
|
||||||
})
|
|
||||||
instance_type_class(Eq, f64, operators={
|
|
||||||
'==': stdtypes.f64_eq_equals,
|
|
||||||
'!=': stdtypes.f64_eq_not_equals,
|
|
||||||
})
|
|
||||||
|
|
||||||
Ord = Type3Class('Ord', (a, ), methods={
|
Ord = Type3Class('Ord', [a], methods={
|
||||||
'min': [a, a, a],
|
'min': [a, a, a],
|
||||||
'max': [a, a, a],
|
'max': [a, a, a],
|
||||||
}, operators={
|
}, operators={
|
||||||
@ -259,80 +187,16 @@ Ord = Type3Class('Ord', (a, ), methods={
|
|||||||
'>=': [a, a, bool_],
|
'>=': [a, a, bool_],
|
||||||
}, inherited_classes=[Eq])
|
}, inherited_classes=[Eq])
|
||||||
|
|
||||||
instance_type_class(Ord, u8, methods={
|
instance_type_class(Ord, u8)
|
||||||
'min': stdtypes.u8_ord_min,
|
instance_type_class(Ord, u32)
|
||||||
'max': stdtypes.u8_ord_max,
|
instance_type_class(Ord, u64)
|
||||||
}, operators={
|
instance_type_class(Ord, i8)
|
||||||
'<': stdtypes.u8_ord_less_than,
|
instance_type_class(Ord, i32)
|
||||||
'<=': stdtypes.u8_ord_less_than_or_equal,
|
instance_type_class(Ord, i64)
|
||||||
'>': stdtypes.u8_ord_greater_than,
|
instance_type_class(Ord, f32)
|
||||||
'>=': stdtypes.u8_ord_greater_than_or_equal,
|
instance_type_class(Ord, f64)
|
||||||
})
|
|
||||||
instance_type_class(Ord, u32, methods={
|
|
||||||
'min': stdtypes.u32_ord_min,
|
|
||||||
'max': stdtypes.u32_ord_max,
|
|
||||||
}, operators={
|
|
||||||
'<': stdtypes.u32_ord_less_than,
|
|
||||||
'<=': stdtypes.u32_ord_less_than_or_equal,
|
|
||||||
'>': stdtypes.u32_ord_greater_than,
|
|
||||||
'>=': stdtypes.u32_ord_greater_than_or_equal,
|
|
||||||
})
|
|
||||||
instance_type_class(Ord, u64, methods={
|
|
||||||
'min': stdtypes.u64_ord_min,
|
|
||||||
'max': stdtypes.u64_ord_max,
|
|
||||||
}, operators={
|
|
||||||
'<': stdtypes.u64_ord_less_than,
|
|
||||||
'<=': stdtypes.u64_ord_less_than_or_equal,
|
|
||||||
'>': stdtypes.u64_ord_greater_than,
|
|
||||||
'>=': stdtypes.u64_ord_greater_than_or_equal,
|
|
||||||
})
|
|
||||||
instance_type_class(Ord, i8, methods={
|
|
||||||
'min': stdtypes.i8_ord_min,
|
|
||||||
'max': stdtypes.i8_ord_max,
|
|
||||||
}, operators={
|
|
||||||
'<': stdtypes.i8_ord_less_than,
|
|
||||||
'<=': stdtypes.i8_ord_less_than_or_equal,
|
|
||||||
'>': stdtypes.i8_ord_greater_than,
|
|
||||||
'>=': stdtypes.i8_ord_greater_than_or_equal,
|
|
||||||
})
|
|
||||||
instance_type_class(Ord, i32, methods={
|
|
||||||
'min': stdtypes.i32_ord_min,
|
|
||||||
'max': stdtypes.i32_ord_max,
|
|
||||||
}, operators={
|
|
||||||
'<': stdtypes.i32_ord_less_than,
|
|
||||||
'<=': stdtypes.i32_ord_less_than_or_equal,
|
|
||||||
'>': stdtypes.i32_ord_greater_than,
|
|
||||||
'>=': stdtypes.i32_ord_greater_than_or_equal,
|
|
||||||
})
|
|
||||||
instance_type_class(Ord, i64, methods={
|
|
||||||
'min': stdtypes.i64_ord_min,
|
|
||||||
'max': stdtypes.i64_ord_max,
|
|
||||||
}, operators={
|
|
||||||
'<': stdtypes.i64_ord_less_than,
|
|
||||||
'<=': stdtypes.i64_ord_less_than_or_equal,
|
|
||||||
'>': stdtypes.i64_ord_greater_than,
|
|
||||||
'>=': stdtypes.i64_ord_greater_than_or_equal,
|
|
||||||
})
|
|
||||||
instance_type_class(Ord, f32, methods={
|
|
||||||
'min': stdtypes.f32_ord_min,
|
|
||||||
'max': stdtypes.f32_ord_max,
|
|
||||||
}, operators={
|
|
||||||
'<': stdtypes.f32_ord_less_than,
|
|
||||||
'<=': stdtypes.f32_ord_less_than_or_equal,
|
|
||||||
'>': stdtypes.f32_ord_greater_than,
|
|
||||||
'>=': stdtypes.f32_ord_greater_than_or_equal,
|
|
||||||
})
|
|
||||||
instance_type_class(Ord, f64, methods={
|
|
||||||
'min': stdtypes.f64_ord_min,
|
|
||||||
'max': stdtypes.f64_ord_max,
|
|
||||||
}, operators={
|
|
||||||
'<': stdtypes.f64_ord_less_than,
|
|
||||||
'<=': stdtypes.f64_ord_less_than_or_equal,
|
|
||||||
'>': stdtypes.f64_ord_greater_than,
|
|
||||||
'>=': stdtypes.f64_ord_greater_than_or_equal,
|
|
||||||
})
|
|
||||||
|
|
||||||
Bits = Type3Class('Bits', (a, ), methods={
|
Bits = Type3Class('Bits', [a], methods={
|
||||||
'shl': [a, u32, a], # Logical shift left
|
'shl': [a, u32, a], # Logical shift left
|
||||||
'shr': [a, u32, a], # Logical shift right
|
'shr': [a, u32, a], # Logical shift right
|
||||||
'rotl': [a, u32, a], # Rotate bits left
|
'rotl': [a, u32, a], # Rotate bits left
|
||||||
@ -344,38 +208,11 @@ Bits = Type3Class('Bits', (a, ), methods={
|
|||||||
'^': [a, a, a], # Bit-wise xor
|
'^': [a, a, a], # Bit-wise xor
|
||||||
})
|
})
|
||||||
|
|
||||||
instance_type_class(Bits, u8, methods={
|
instance_type_class(Bits, u8)
|
||||||
'shl': stdtypes.u8_bits_logical_shift_left,
|
instance_type_class(Bits, u32)
|
||||||
'shr': stdtypes.u8_bits_logical_shift_right,
|
instance_type_class(Bits, u64)
|
||||||
'rotl': stdtypes.u8_bits_rotate_left,
|
|
||||||
'rotr': stdtypes.u8_bits_rotate_right,
|
|
||||||
}, operators={
|
|
||||||
'&': stdtypes.u8_bits_bitwise_and,
|
|
||||||
'|': stdtypes.u8_bits_bitwise_or,
|
|
||||||
'^': stdtypes.u8_bits_bitwise_xor,
|
|
||||||
})
|
|
||||||
instance_type_class(Bits, u32, methods={
|
|
||||||
'shl': stdtypes.u32_bits_logical_shift_left,
|
|
||||||
'shr': stdtypes.u32_bits_logical_shift_right,
|
|
||||||
'rotl': stdtypes.u32_bits_rotate_left,
|
|
||||||
'rotr': stdtypes.u32_bits_rotate_right,
|
|
||||||
}, operators={
|
|
||||||
'&': stdtypes.u32_bits_bitwise_and,
|
|
||||||
'|': stdtypes.u32_bits_bitwise_or,
|
|
||||||
'^': stdtypes.u32_bits_bitwise_xor,
|
|
||||||
})
|
|
||||||
instance_type_class(Bits, u64, methods={
|
|
||||||
'shl': stdtypes.u64_bits_logical_shift_left,
|
|
||||||
'shr': stdtypes.u64_bits_logical_shift_right,
|
|
||||||
'rotl': stdtypes.u64_bits_rotate_left,
|
|
||||||
'rotr': stdtypes.u64_bits_rotate_right,
|
|
||||||
}, operators={
|
|
||||||
'&': stdtypes.u64_bits_bitwise_and,
|
|
||||||
'|': stdtypes.u64_bits_bitwise_or,
|
|
||||||
'^': stdtypes.u64_bits_bitwise_xor,
|
|
||||||
})
|
|
||||||
|
|
||||||
NatNum = Type3Class('NatNum', (a, ), methods={}, operators={
|
NatNum = Type3Class('NatNum', [a], methods={}, operators={
|
||||||
'+': [a, a, a],
|
'+': [a, a, a],
|
||||||
'-': [a, a, a],
|
'-': [a, a, a],
|
||||||
'*': [a, a, a],
|
'*': [a, a, a],
|
||||||
@ -383,95 +220,35 @@ NatNum = Type3Class('NatNum', (a, ), methods={}, operators={
|
|||||||
'>>': [a, u32, a], # Arithmic shift right
|
'>>': [a, u32, a], # Arithmic shift right
|
||||||
})
|
})
|
||||||
|
|
||||||
instance_type_class(NatNum, u32, operators={
|
instance_type_class(NatNum, u32)
|
||||||
'+': stdtypes.u32_natnum_add,
|
instance_type_class(NatNum, u64)
|
||||||
'-': stdtypes.u32_natnum_sub,
|
instance_type_class(NatNum, i32)
|
||||||
'*': stdtypes.u32_natnum_mul,
|
instance_type_class(NatNum, i64)
|
||||||
'<<': stdtypes.u32_natnum_arithmic_shift_left,
|
instance_type_class(NatNum, f32)
|
||||||
'>>': stdtypes.u32_natnum_arithmic_shift_right,
|
instance_type_class(NatNum, f64)
|
||||||
})
|
|
||||||
instance_type_class(NatNum, u64, operators={
|
|
||||||
'+': stdtypes.u64_natnum_add,
|
|
||||||
'-': stdtypes.u64_natnum_sub,
|
|
||||||
'*': stdtypes.u64_natnum_mul,
|
|
||||||
'<<': stdtypes.u64_natnum_arithmic_shift_left,
|
|
||||||
'>>': stdtypes.u64_natnum_arithmic_shift_right,
|
|
||||||
})
|
|
||||||
instance_type_class(NatNum, i32, operators={
|
|
||||||
'+': stdtypes.i32_natnum_add,
|
|
||||||
'-': stdtypes.i32_natnum_sub,
|
|
||||||
'*': stdtypes.i32_natnum_mul,
|
|
||||||
'<<': stdtypes.i32_natnum_arithmic_shift_left,
|
|
||||||
'>>': stdtypes.i32_natnum_arithmic_shift_right,
|
|
||||||
})
|
|
||||||
instance_type_class(NatNum, i64, operators={
|
|
||||||
'+': stdtypes.i64_natnum_add,
|
|
||||||
'-': stdtypes.i64_natnum_sub,
|
|
||||||
'*': stdtypes.i64_natnum_mul,
|
|
||||||
'<<': stdtypes.i64_natnum_arithmic_shift_left,
|
|
||||||
'>>': stdtypes.i64_natnum_arithmic_shift_right,
|
|
||||||
})
|
|
||||||
instance_type_class(NatNum, f32, operators={
|
|
||||||
'+': stdtypes.f32_natnum_add,
|
|
||||||
'-': stdtypes.f32_natnum_sub,
|
|
||||||
'*': stdtypes.f32_natnum_mul,
|
|
||||||
'<<': stdtypes.f32_natnum_arithmic_shift_left,
|
|
||||||
'>>': stdtypes.f32_natnum_arithmic_shift_right,
|
|
||||||
})
|
|
||||||
instance_type_class(NatNum, f64, operators={
|
|
||||||
'+': stdtypes.f64_natnum_add,
|
|
||||||
'-': stdtypes.f64_natnum_sub,
|
|
||||||
'*': stdtypes.f64_natnum_mul,
|
|
||||||
'<<': stdtypes.f64_natnum_arithmic_shift_left,
|
|
||||||
'>>': stdtypes.f64_natnum_arithmic_shift_right,
|
|
||||||
})
|
|
||||||
|
|
||||||
IntNum = Type3Class('IntNum', (a, ), methods={
|
IntNum = Type3Class('IntNum', [a], methods={
|
||||||
'abs': [a, a],
|
'abs': [a, a],
|
||||||
'neg': [a, a],
|
'neg': [a, a],
|
||||||
}, operators={}, inherited_classes=[NatNum])
|
}, operators={}, inherited_classes=[NatNum])
|
||||||
|
|
||||||
instance_type_class(IntNum, i32, methods={
|
instance_type_class(IntNum, i32)
|
||||||
'abs': stdtypes.i32_intnum_abs,
|
instance_type_class(IntNum, i64)
|
||||||
'neg': stdtypes.i32_intnum_neg,
|
instance_type_class(IntNum, f32)
|
||||||
})
|
instance_type_class(IntNum, f64)
|
||||||
instance_type_class(IntNum, i64, methods={
|
|
||||||
'abs': stdtypes.i64_intnum_abs,
|
|
||||||
'neg': stdtypes.i64_intnum_neg,
|
|
||||||
})
|
|
||||||
instance_type_class(IntNum, f32, methods={
|
|
||||||
'abs': stdtypes.f32_intnum_abs,
|
|
||||||
'neg': stdtypes.f32_intnum_neg,
|
|
||||||
})
|
|
||||||
instance_type_class(IntNum, f64, methods={
|
|
||||||
'abs': stdtypes.f64_intnum_abs,
|
|
||||||
'neg': stdtypes.f64_intnum_neg,
|
|
||||||
})
|
|
||||||
|
|
||||||
Integral = Type3Class('Eq', (a, ), methods={
|
Integral = Type3Class('Eq', [a], methods={
|
||||||
}, operators={
|
}, operators={
|
||||||
'//': [a, a, a],
|
'//': [a, a, a],
|
||||||
'%': [a, a, a],
|
'%': [a, a, a],
|
||||||
}, inherited_classes=[NatNum])
|
}, inherited_classes=[NatNum])
|
||||||
|
|
||||||
instance_type_class(Integral, u32, operators={
|
instance_type_class(Integral, u32)
|
||||||
'//': stdtypes.u32_integral_div,
|
instance_type_class(Integral, u64)
|
||||||
'%': stdtypes.u32_integral_rem,
|
instance_type_class(Integral, i32)
|
||||||
})
|
instance_type_class(Integral, i64)
|
||||||
instance_type_class(Integral, u64, operators={
|
|
||||||
'//': stdtypes.u64_integral_div,
|
|
||||||
'%': stdtypes.u64_integral_rem,
|
|
||||||
})
|
|
||||||
instance_type_class(Integral, i32, operators={
|
|
||||||
'//': stdtypes.i32_integral_div,
|
|
||||||
'%': stdtypes.i32_integral_rem,
|
|
||||||
})
|
|
||||||
instance_type_class(Integral, i64, operators={
|
|
||||||
'//': stdtypes.i64_integral_div,
|
|
||||||
'%': stdtypes.i64_integral_rem,
|
|
||||||
})
|
|
||||||
|
|
||||||
Fractional = Type3Class('Fractional', (a, ), methods={
|
Fractional = Type3Class('Fractional', [a], methods={
|
||||||
'ceil': [a, a],
|
'ceil': [a, a],
|
||||||
'floor': [a, a],
|
'floor': [a, a],
|
||||||
'trunc': [a, a],
|
'trunc': [a, a],
|
||||||
@ -480,88 +257,47 @@ Fractional = Type3Class('Fractional', (a, ), methods={
|
|||||||
'/': [a, a, a],
|
'/': [a, a, a],
|
||||||
}, inherited_classes=[NatNum])
|
}, inherited_classes=[NatNum])
|
||||||
|
|
||||||
instance_type_class(Fractional, f32, methods={
|
instance_type_class(Fractional, f32)
|
||||||
'ceil': stdtypes.f32_fractional_ceil,
|
instance_type_class(Fractional, f64)
|
||||||
'floor': stdtypes.f32_fractional_floor,
|
|
||||||
'trunc': stdtypes.f32_fractional_trunc,
|
|
||||||
'nearest': stdtypes.f32_fractional_nearest,
|
|
||||||
}, operators={
|
|
||||||
'/': stdtypes.f32_fractional_div,
|
|
||||||
})
|
|
||||||
instance_type_class(Fractional, f64, methods={
|
|
||||||
'ceil': stdtypes.f64_fractional_ceil,
|
|
||||||
'floor': stdtypes.f64_fractional_floor,
|
|
||||||
'trunc': stdtypes.f64_fractional_trunc,
|
|
||||||
'nearest': stdtypes.f64_fractional_nearest,
|
|
||||||
}, operators={
|
|
||||||
'/': stdtypes.f64_fractional_div,
|
|
||||||
})
|
|
||||||
|
|
||||||
Floating = Type3Class('Floating', (a, ), methods={
|
Floating = Type3Class('Floating', [a], methods={
|
||||||
'sqrt': [a, a],
|
'sqrt': [a, a],
|
||||||
}, operators={}, inherited_classes=[Fractional])
|
}, operators={}, inherited_classes=[Fractional])
|
||||||
|
|
||||||
# FIXME: Do we want to expose copysign?
|
# FIXME: Do we want to expose copysign?
|
||||||
|
|
||||||
instance_type_class(Floating, f32, methods={
|
instance_type_class(Floating, f32)
|
||||||
'sqrt': stdtypes.f32_floating_sqrt,
|
instance_type_class(Floating, f64)
|
||||||
})
|
|
||||||
instance_type_class(Floating, f64, methods={
|
|
||||||
'sqrt': stdtypes.f64_floating_sqrt,
|
|
||||||
})
|
|
||||||
|
|
||||||
Sized_ = Type3Class('Sized', (a, ), methods={
|
Sized_ = Type3Class('Sized', [a], methods={
|
||||||
'len': [a, u32],
|
'len': [a, u32],
|
||||||
}, operators={}) # FIXME: Once we get type class families, add [] here
|
}, operators={}) # FIXME: Once we get type class families, add [] here
|
||||||
|
|
||||||
instance_type_class(Sized_, bytes_, methods={
|
instance_type_class(Sized_, bytes_)
|
||||||
'len': stdtypes.bytes_sized_len,
|
|
||||||
})
|
|
||||||
|
|
||||||
Extendable = Type3Class('Extendable', (a, b, ), methods={
|
Extendable = Type3Class('Extendable', [a, b], methods={
|
||||||
'extend': [a, b],
|
'extend': [a, b],
|
||||||
'wrap': [b, a],
|
'wrap': [b, a],
|
||||||
}, operators={})
|
}, operators={})
|
||||||
|
|
||||||
instance_type_class(Extendable, u8, u32, methods={
|
instance_type_class(Extendable, u8, u32)
|
||||||
'extend': stdtypes.u8_u32_extend,
|
instance_type_class(Extendable, u8, u64)
|
||||||
'wrap': stdtypes.u8_u32_wrap,
|
instance_type_class(Extendable, u32, u64)
|
||||||
})
|
instance_type_class(Extendable, i8, i32)
|
||||||
instance_type_class(Extendable, u8, u64, methods={
|
instance_type_class(Extendable, i8, i64)
|
||||||
'extend': stdtypes.u8_u64_extend,
|
instance_type_class(Extendable, i32, i64)
|
||||||
'wrap': stdtypes.u8_u64_wrap,
|
|
||||||
})
|
|
||||||
instance_type_class(Extendable, u32, u64, methods={
|
|
||||||
'extend': stdtypes.u32_u64_extend,
|
|
||||||
'wrap': stdtypes.u32_u64_wrap,
|
|
||||||
})
|
|
||||||
instance_type_class(Extendable, i8, i32, methods={
|
|
||||||
'extend': stdtypes.i8_i32_extend,
|
|
||||||
'wrap': stdtypes.i8_i32_wrap,
|
|
||||||
})
|
|
||||||
instance_type_class(Extendable, i8, i64, methods={
|
|
||||||
'extend': stdtypes.i8_i64_extend,
|
|
||||||
'wrap': stdtypes.i8_i64_wrap,
|
|
||||||
})
|
|
||||||
instance_type_class(Extendable, i32, i64, methods={
|
|
||||||
'extend': stdtypes.i32_i64_extend,
|
|
||||||
'wrap': stdtypes.i32_i64_wrap,
|
|
||||||
})
|
|
||||||
|
|
||||||
Promotable = Type3Class('Promotable', (a, b, ), methods={
|
Promotable = Type3Class('Promotable', [a, b], methods={
|
||||||
'promote': [a, b],
|
'promote': [a, b],
|
||||||
'demote': [b, a],
|
'demote': [b, a],
|
||||||
}, operators={})
|
}, operators={})
|
||||||
|
|
||||||
instance_type_class(Promotable, f32, f64, methods={
|
instance_type_class(Promotable, f32, f64)
|
||||||
'promote': stdtypes.f32_f64_promote,
|
|
||||||
'demote': stdtypes.f32_f64_demote,
|
|
||||||
})
|
|
||||||
|
|
||||||
Foldable = Type3Class('Foldable', (t, ), methods={
|
Foldable = Type3Class('Foldable', [t], methods={
|
||||||
'sum': [t(a), a],
|
'sum': [t(a), a],
|
||||||
}, operators={}, additional_context={
|
}, operators={}, additional_context={
|
||||||
'sum': [Constraint_TypeClassInstanceExists(NatNum, (a, ))],
|
'sum': [Constraint_TypeClassInstanceExists(NatNum, [a])],
|
||||||
})
|
})
|
||||||
|
|
||||||
instance_type_class(Foldable, static_array)
|
instance_type_class(Foldable, static_array)
|
||||||
|
|||||||
@ -1,40 +1,8 @@
|
|||||||
from . import prelude
|
from . import prelude
|
||||||
from .type3.routers import NoRouteForTypeException, TypeApplicationRouter
|
from .type3 import types as type3types
|
||||||
from .type3.types import IntType3, Type3
|
|
||||||
|
|
||||||
|
|
||||||
def calculate_alloc_size_static_array(is_member: bool, args: tuple[Type3, IntType3]) -> int:
|
def calculate_alloc_size(typ: type3types.Type3, is_member: bool = False) -> int:
|
||||||
if is_member:
|
|
||||||
return 4
|
|
||||||
|
|
||||||
sa_type, sa_len = args
|
|
||||||
|
|
||||||
return sa_len.value * calculate_alloc_size(sa_type, is_member=True)
|
|
||||||
|
|
||||||
def calculate_alloc_size_tuple(is_member: bool, args: tuple[Type3, ...]) -> int:
|
|
||||||
if is_member:
|
|
||||||
return 4
|
|
||||||
|
|
||||||
return sum(
|
|
||||||
calculate_alloc_size(x, is_member=True)
|
|
||||||
for x in args
|
|
||||||
)
|
|
||||||
|
|
||||||
def calculate_alloc_size_struct(is_member: bool, args: tuple[tuple[str, Type3], ...]) -> int:
|
|
||||||
if is_member:
|
|
||||||
return 4
|
|
||||||
|
|
||||||
return sum(
|
|
||||||
calculate_alloc_size(x, is_member=True)
|
|
||||||
for _, x in args
|
|
||||||
)
|
|
||||||
|
|
||||||
ALLOC_SIZE_ROUTER = TypeApplicationRouter[bool, int]()
|
|
||||||
ALLOC_SIZE_ROUTER.add(prelude.static_array, calculate_alloc_size_static_array)
|
|
||||||
ALLOC_SIZE_ROUTER.add(prelude.struct, calculate_alloc_size_struct)
|
|
||||||
ALLOC_SIZE_ROUTER.add(prelude.tuple_, calculate_alloc_size_tuple)
|
|
||||||
|
|
||||||
def calculate_alloc_size(typ: Type3, is_member: bool = False) -> int:
|
|
||||||
if typ in (prelude.u8, prelude.i8, ):
|
if typ in (prelude.u8, prelude.i8, ):
|
||||||
return 4 # FIXME: We allocate 4 bytes for every u8 since you load them into an i32
|
return 4 # FIXME: We allocate 4 bytes for every u8 since you load them into an i32
|
||||||
|
|
||||||
@ -44,20 +12,51 @@ def calculate_alloc_size(typ: Type3, is_member: bool = False) -> int:
|
|||||||
if typ in (prelude.u64, prelude.i64, prelude.f64, ):
|
if typ in (prelude.u64, prelude.i64, prelude.f64, ):
|
||||||
return 8
|
return 8
|
||||||
|
|
||||||
try:
|
if typ == prelude.bytes_:
|
||||||
return ALLOC_SIZE_ROUTER(is_member, typ)
|
|
||||||
except NoRouteForTypeException:
|
|
||||||
if is_member:
|
if is_member:
|
||||||
# By default, 'boxed' or 'constructed' types are
|
|
||||||
# stored as pointers when a member of a struct or tuple
|
|
||||||
return 4
|
return 4
|
||||||
|
|
||||||
raise NotImplementedError(typ)
|
raise NotImplementedError # When does this happen?
|
||||||
|
|
||||||
def calculate_member_offset(st_name: str, st_args: tuple[tuple[str, Type3], ...], needle: str) -> int:
|
st_args = prelude.struct.did_construct(typ)
|
||||||
|
if st_args is not None:
|
||||||
|
if is_member:
|
||||||
|
# Structs referred to by other structs or tuples are pointers
|
||||||
|
return 4
|
||||||
|
|
||||||
|
return sum(
|
||||||
|
calculate_alloc_size(x, is_member=True)
|
||||||
|
for x in st_args.values()
|
||||||
|
)
|
||||||
|
|
||||||
|
sa_args = prelude.static_array.did_construct(typ)
|
||||||
|
if sa_args is not None:
|
||||||
|
if is_member:
|
||||||
|
# tuples referred to by other structs or tuples are pointers
|
||||||
|
return 4
|
||||||
|
|
||||||
|
sa_type, sa_len = sa_args
|
||||||
|
|
||||||
|
return sa_len.value * calculate_alloc_size(sa_type, is_member=True)
|
||||||
|
|
||||||
|
tp_args = prelude.tuple_.did_construct(typ)
|
||||||
|
if tp_args is not None:
|
||||||
|
if is_member:
|
||||||
|
# tuples referred to by other structs or tuples are pointers
|
||||||
|
return 4
|
||||||
|
|
||||||
|
size = 0
|
||||||
|
for arg in tp_args:
|
||||||
|
size += calculate_alloc_size(arg, is_member=True)
|
||||||
|
|
||||||
|
return size
|
||||||
|
|
||||||
|
raise NotImplementedError(calculate_alloc_size, typ)
|
||||||
|
|
||||||
|
def calculate_member_offset(st_name: str, st_args: dict[str, type3types.Type3], needle: str) -> int:
|
||||||
result = 0
|
result = 0
|
||||||
|
|
||||||
for memnam, memtyp in st_args:
|
for memnam, memtyp in st_args.items():
|
||||||
if needle == memnam:
|
if needle == memnam:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,6 @@ from typing import Any, Dict, Iterable, List, Optional, Tuple, Union
|
|||||||
from .. import ourlang, prelude
|
from .. import ourlang, prelude
|
||||||
from . import placeholders, typeclasses, types
|
from . import placeholders, typeclasses, types
|
||||||
from .placeholders import PlaceholderForType
|
from .placeholders import PlaceholderForType
|
||||||
from .routers import NoRouteForTypeException, TypeApplicationRouter
|
|
||||||
|
|
||||||
|
|
||||||
class Error:
|
class Error:
|
||||||
@ -50,7 +49,7 @@ class Context:
|
|||||||
__slots__ = ('type_class_instances_existing', )
|
__slots__ = ('type_class_instances_existing', )
|
||||||
|
|
||||||
# Constraint_TypeClassInstanceExists
|
# Constraint_TypeClassInstanceExists
|
||||||
type_class_instances_existing: set[tuple[typeclasses.Type3Class, tuple[Union[types.Type3, types.TypeConstructor_Base[Any], types.TypeConstructor_Struct], ...]]]
|
type_class_instances_existing: set[tuple[typeclasses.Type3Class, tuple[Union[types.Type3, types.TypeConstructor[Any], types.TypeConstructor_Struct], ...]]]
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.type_class_instances_existing = set()
|
self.type_class_instances_existing = set()
|
||||||
@ -159,18 +158,24 @@ class SameTypeConstraint(ConstraintBase):
|
|||||||
return f'SameTypeConstraint({args}, comment={repr(self.comment)})'
|
return f'SameTypeConstraint({args}, comment={repr(self.comment)})'
|
||||||
|
|
||||||
class TupleMatchConstraint(ConstraintBase):
|
class TupleMatchConstraint(ConstraintBase):
|
||||||
__slots__ = ('exp_type', 'args', )
|
|
||||||
|
|
||||||
exp_type: placeholders.Type3OrPlaceholder
|
|
||||||
args: list[placeholders.Type3OrPlaceholder]
|
|
||||||
|
|
||||||
def __init__(self, exp_type: placeholders.Type3OrPlaceholder, args: Iterable[placeholders.Type3OrPlaceholder], comment: str):
|
def __init__(self, exp_type: placeholders.Type3OrPlaceholder, args: Iterable[placeholders.Type3OrPlaceholder], comment: str):
|
||||||
super().__init__(comment=comment)
|
super().__init__(comment=comment)
|
||||||
|
|
||||||
self.exp_type = exp_type
|
self.exp_type = exp_type
|
||||||
self.args = list(args)
|
self.args = list(args)
|
||||||
|
|
||||||
def _generate_static_array(self, sa_args: tuple[types.Type3, types.IntType3]) -> CheckResult:
|
def check(self) -> CheckResult:
|
||||||
|
exp_type = self.exp_type
|
||||||
|
if isinstance(exp_type, placeholders.PlaceholderForType):
|
||||||
|
if exp_type.resolve_as is None:
|
||||||
|
return RequireTypeSubstitutes()
|
||||||
|
|
||||||
|
exp_type = exp_type.resolve_as
|
||||||
|
|
||||||
|
assert isinstance(exp_type, types.Type3)
|
||||||
|
|
||||||
|
sa_args = prelude.static_array.did_construct(exp_type)
|
||||||
|
if sa_args is not None:
|
||||||
sa_type, sa_len = sa_args
|
sa_type, sa_len = sa_args
|
||||||
|
|
||||||
if sa_len.value != len(self.args):
|
if sa_len.value != len(self.args):
|
||||||
@ -181,7 +186,8 @@ class TupleMatchConstraint(ConstraintBase):
|
|||||||
for arg in self.args
|
for arg in self.args
|
||||||
]
|
]
|
||||||
|
|
||||||
def _generate_tuple(self, tp_args: tuple[types.Type3, ...]) -> CheckResult:
|
tp_args = prelude.tuple_.did_construct(exp_type)
|
||||||
|
if tp_args is not None:
|
||||||
if len(tp_args) != len(self.args):
|
if len(tp_args) != len(self.args):
|
||||||
return Error('Mismatch between applied types argument count', comment=self.comment)
|
return Error('Mismatch between applied types argument count', comment=self.comment)
|
||||||
|
|
||||||
@ -190,21 +196,6 @@ class TupleMatchConstraint(ConstraintBase):
|
|||||||
for arg, oth_arg in zip(self.args, tp_args, strict=True)
|
for arg, oth_arg in zip(self.args, tp_args, strict=True)
|
||||||
]
|
]
|
||||||
|
|
||||||
GENERATE_ROUTER = TypeApplicationRouter['TupleMatchConstraint', CheckResult]()
|
|
||||||
GENERATE_ROUTER.add(prelude.static_array, _generate_static_array)
|
|
||||||
GENERATE_ROUTER.add(prelude.tuple_, _generate_tuple)
|
|
||||||
|
|
||||||
def check(self) -> CheckResult:
|
|
||||||
exp_type = self.exp_type
|
|
||||||
if isinstance(exp_type, placeholders.PlaceholderForType):
|
|
||||||
if exp_type.resolve_as is None:
|
|
||||||
return RequireTypeSubstitutes()
|
|
||||||
|
|
||||||
exp_type = exp_type.resolve_as
|
|
||||||
|
|
||||||
try:
|
|
||||||
return self.__class__.GENERATE_ROUTER(self, exp_type)
|
|
||||||
except NoRouteForTypeException:
|
|
||||||
raise NotImplementedError(exp_type)
|
raise NotImplementedError(exp_type)
|
||||||
|
|
||||||
class MustImplementTypeClassConstraint(ConstraintBase):
|
class MustImplementTypeClassConstraint(ConstraintBase):
|
||||||
@ -229,7 +220,7 @@ class MustImplementTypeClassConstraint(ConstraintBase):
|
|||||||
self.types = types
|
self.types = types
|
||||||
|
|
||||||
def check(self) -> CheckResult:
|
def check(self) -> CheckResult:
|
||||||
typ_list: list[types.Type3 | types.TypeConstructor_Base[Any] | types.TypeConstructor_Struct] = []
|
typ_list = []
|
||||||
for typ in self.types:
|
for typ in self.types:
|
||||||
if isinstance(typ, placeholders.PlaceholderForType) and typ.resolve_as is not None:
|
if isinstance(typ, placeholders.PlaceholderForType) and typ.resolve_as is not None:
|
||||||
typ = typ.resolve_as
|
typ = typ.resolve_as
|
||||||
@ -237,15 +228,7 @@ class MustImplementTypeClassConstraint(ConstraintBase):
|
|||||||
if isinstance(typ, placeholders.PlaceholderForType):
|
if isinstance(typ, placeholders.PlaceholderForType):
|
||||||
return RequireTypeSubstitutes()
|
return RequireTypeSubstitutes()
|
||||||
|
|
||||||
if isinstance(typ.application, (types.TypeApplication_Nullary, types.TypeApplication_Struct, )):
|
|
||||||
typ_list.append(typ)
|
typ_list.append(typ)
|
||||||
continue
|
|
||||||
|
|
||||||
if isinstance(typ.application, (types.TypeApplication_TypeInt, types.TypeApplication_TypeStar)):
|
|
||||||
typ_list.append(typ.application.constructor)
|
|
||||||
continue
|
|
||||||
|
|
||||||
raise NotImplementedError(typ, typ.application)
|
|
||||||
|
|
||||||
assert len(typ_list) == len(self.types)
|
assert len(typ_list) == len(self.types)
|
||||||
|
|
||||||
@ -298,85 +281,6 @@ class LiteralFitsConstraint(ConstraintBase):
|
|||||||
self.type3 = type3
|
self.type3 = type3
|
||||||
self.literal = literal
|
self.literal = literal
|
||||||
|
|
||||||
def _generate_static_array(self, sa_args: tuple[types.Type3, types.IntType3]) -> CheckResult:
|
|
||||||
if not isinstance(self.literal, ourlang.ConstantTuple):
|
|
||||||
return Error('Must be tuple', comment=self.comment)
|
|
||||||
|
|
||||||
sa_type, sa_len = sa_args
|
|
||||||
|
|
||||||
if sa_len.value != len(self.literal.value):
|
|
||||||
return Error('Member count mismatch', comment=self.comment)
|
|
||||||
|
|
||||||
res: list[ConstraintBase] = []
|
|
||||||
|
|
||||||
res.extend(
|
|
||||||
LiteralFitsConstraint(sa_type, y)
|
|
||||||
for y in self.literal.value
|
|
||||||
)
|
|
||||||
|
|
||||||
# Generate placeholders so each Literal expression
|
|
||||||
# gets updated when we figure out the type of the
|
|
||||||
# expression the literal is used in
|
|
||||||
res.extend(
|
|
||||||
SameTypeConstraint(sa_type, PlaceholderForType([y]))
|
|
||||||
for y in self.literal.value
|
|
||||||
)
|
|
||||||
|
|
||||||
return res
|
|
||||||
|
|
||||||
def _generate_struct(self, st_args: tuple[tuple[str, types.Type3], ...]) -> CheckResult:
|
|
||||||
if not isinstance(self.literal, ourlang.ConstantStruct):
|
|
||||||
return Error('Must be struct')
|
|
||||||
|
|
||||||
if len(st_args) != len(self.literal.value):
|
|
||||||
return Error('Struct element count mismatch')
|
|
||||||
|
|
||||||
res: list[ConstraintBase] = []
|
|
||||||
|
|
||||||
res.extend(
|
|
||||||
LiteralFitsConstraint(x, y)
|
|
||||||
for (_, x), y in zip(st_args, self.literal.value, strict=True)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Generate placeholders so each Literal expression
|
|
||||||
# gets updated when we figure out the type of the
|
|
||||||
# expression the literal is used in
|
|
||||||
res.extend(
|
|
||||||
SameTypeConstraint(x_t, PlaceholderForType([y]), comment=f'{self.literal.struct_name}.{x_n}')
|
|
||||||
for (x_n, x_t, ), y in zip(st_args, self.literal.value, strict=True)
|
|
||||||
)
|
|
||||||
|
|
||||||
return res
|
|
||||||
|
|
||||||
def _generate_tuple(self, tp_args: tuple[types.Type3, ...]) -> CheckResult:
|
|
||||||
if not isinstance(self.literal, ourlang.ConstantTuple):
|
|
||||||
return Error('Must be tuple', comment=self.comment)
|
|
||||||
|
|
||||||
if len(tp_args) != len(self.literal.value):
|
|
||||||
return Error('Tuple element count mismatch', comment=self.comment)
|
|
||||||
|
|
||||||
res: list[ConstraintBase] = []
|
|
||||||
|
|
||||||
res.extend(
|
|
||||||
LiteralFitsConstraint(x, y)
|
|
||||||
for x, y in zip(tp_args, self.literal.value, strict=True)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Generate placeholders so each Literal expression
|
|
||||||
# gets updated when we figure out the type of the
|
|
||||||
# expression the literal is used in
|
|
||||||
res.extend(
|
|
||||||
SameTypeConstraint(x, PlaceholderForType([y]))
|
|
||||||
for x, y in zip(tp_args, self.literal.value, strict=True)
|
|
||||||
)
|
|
||||||
|
|
||||||
return res
|
|
||||||
|
|
||||||
GENERATE_ROUTER = TypeApplicationRouter['LiteralFitsConstraint', CheckResult]()
|
|
||||||
GENERATE_ROUTER.add(prelude.static_array, _generate_static_array)
|
|
||||||
GENERATE_ROUTER.add(prelude.struct, _generate_struct)
|
|
||||||
GENERATE_ROUTER.add(prelude.tuple_, _generate_tuple)
|
|
||||||
|
|
||||||
def check(self) -> CheckResult:
|
def check(self) -> CheckResult:
|
||||||
int_table: Dict[str, Tuple[int, bool]] = {
|
int_table: Dict[str, Tuple[int, bool]] = {
|
||||||
'u8': (1, False),
|
'u8': (1, False),
|
||||||
@ -427,12 +331,92 @@ class LiteralFitsConstraint(ConstraintBase):
|
|||||||
|
|
||||||
return Error('Must be bytes', comment=self.comment) # FIXME: Add line information
|
return Error('Must be bytes', comment=self.comment) # FIXME: Add line information
|
||||||
|
|
||||||
exp_type = self.type3
|
res: NewConstraintList
|
||||||
|
|
||||||
try:
|
assert isinstance(self.type3, types.Type3)
|
||||||
return self.__class__.GENERATE_ROUTER(self, exp_type)
|
|
||||||
except NoRouteForTypeException:
|
tp_args = prelude.tuple_.did_construct(self.type3)
|
||||||
raise NotImplementedError(exp_type)
|
if tp_args is not None:
|
||||||
|
if not isinstance(self.literal, ourlang.ConstantTuple):
|
||||||
|
return Error('Must be tuple', comment=self.comment)
|
||||||
|
|
||||||
|
if len(tp_args) != len(self.literal.value):
|
||||||
|
return Error('Tuple element count mismatch', comment=self.comment)
|
||||||
|
|
||||||
|
res = []
|
||||||
|
|
||||||
|
res.extend(
|
||||||
|
LiteralFitsConstraint(x, y)
|
||||||
|
for x, y in zip(tp_args, self.literal.value, strict=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Generate placeholders so each Literal expression
|
||||||
|
# gets updated when we figure out the type of the
|
||||||
|
# expression the literal is used in
|
||||||
|
res.extend(
|
||||||
|
SameTypeConstraint(x, PlaceholderForType([y]))
|
||||||
|
for x, y in zip(tp_args, self.literal.value, strict=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
sa_args = prelude.static_array.did_construct(self.type3)
|
||||||
|
if sa_args is not None:
|
||||||
|
if not isinstance(self.literal, ourlang.ConstantTuple):
|
||||||
|
return Error('Must be tuple', comment=self.comment)
|
||||||
|
|
||||||
|
sa_type, sa_len = sa_args
|
||||||
|
|
||||||
|
if sa_len.value != len(self.literal.value):
|
||||||
|
return Error('Member count mismatch', comment=self.comment)
|
||||||
|
|
||||||
|
res = []
|
||||||
|
|
||||||
|
res.extend(
|
||||||
|
LiteralFitsConstraint(sa_type, y)
|
||||||
|
for y in self.literal.value
|
||||||
|
)
|
||||||
|
|
||||||
|
# Generate placeholders so each Literal expression
|
||||||
|
# gets updated when we figure out the type of the
|
||||||
|
# expression the literal is used in
|
||||||
|
res.extend(
|
||||||
|
SameTypeConstraint(sa_type, PlaceholderForType([y]))
|
||||||
|
for y in self.literal.value
|
||||||
|
)
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
st_args = prelude.struct.did_construct(self.type3)
|
||||||
|
if st_args is not None:
|
||||||
|
if not isinstance(self.literal, ourlang.ConstantStruct):
|
||||||
|
return Error('Must be struct')
|
||||||
|
|
||||||
|
if self.literal.struct_name != self.type3.name:
|
||||||
|
return Error('Struct mismatch')
|
||||||
|
|
||||||
|
|
||||||
|
if len(st_args) != len(self.literal.value):
|
||||||
|
return Error('Struct element count mismatch')
|
||||||
|
|
||||||
|
res = []
|
||||||
|
|
||||||
|
res.extend(
|
||||||
|
LiteralFitsConstraint(x, y)
|
||||||
|
for x, y in zip(st_args.values(), self.literal.value, strict=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Generate placeholders so each Literal expression
|
||||||
|
# gets updated when we figure out the type of the
|
||||||
|
# expression the literal is used in
|
||||||
|
res.extend(
|
||||||
|
SameTypeConstraint(x_t, PlaceholderForType([y]), comment=f'{self.literal.struct_name}.{x_n}')
|
||||||
|
for (x_n, x_t, ), y in zip(st_args.items(), self.literal.value, strict=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
raise NotImplementedError(self.type3, self.literal)
|
||||||
|
|
||||||
def human_readable(self) -> HumanReadableRet:
|
def human_readable(self) -> HumanReadableRet:
|
||||||
return (
|
return (
|
||||||
@ -472,31 +456,38 @@ class CanBeSubscriptedConstraint(ConstraintBase):
|
|||||||
self.index = index
|
self.index = index
|
||||||
self.index_phft = index_phft
|
self.index_phft = index_phft
|
||||||
|
|
||||||
def _generate_bytes(self) -> CheckResult:
|
def check(self) -> CheckResult:
|
||||||
return [
|
exp_type = self.type3
|
||||||
SameTypeConstraint(prelude.u32, self.index_phft, comment='([]) :: bytes -> u32 -> u8'),
|
if isinstance(exp_type, placeholders.PlaceholderForType):
|
||||||
SameTypeConstraint(prelude.u8, self.ret_type3, comment='([]) :: bytes -> u32 -> u8'),
|
if exp_type.resolve_as is None:
|
||||||
]
|
return RequireTypeSubstitutes()
|
||||||
|
|
||||||
def _generate_static_array(self, sa_args: tuple[types.Type3, types.IntType3]) -> CheckResult:
|
exp_type = exp_type.resolve_as
|
||||||
|
|
||||||
|
assert isinstance(exp_type, types.Type3)
|
||||||
|
|
||||||
|
sa_args = prelude.static_array.did_construct(exp_type)
|
||||||
|
if sa_args is not None:
|
||||||
sa_type, sa_len = sa_args
|
sa_type, sa_len = sa_args
|
||||||
|
|
||||||
|
result: List[ConstraintBase] = [
|
||||||
|
SameTypeConstraint(prelude.u32, self.index_phft, comment='([]) :: Subscriptable a => a b -> u32 -> b'),
|
||||||
|
SameTypeConstraint(sa_type, self.ret_type3, comment='([]) :: Subscriptable a => a b -> u32 -> b'),
|
||||||
|
]
|
||||||
|
|
||||||
if isinstance(self.index, ourlang.ConstantPrimitive):
|
if isinstance(self.index, ourlang.ConstantPrimitive):
|
||||||
assert isinstance(self.index.value, int)
|
assert isinstance(self.index.value, int)
|
||||||
|
|
||||||
if self.index.value < 0 or sa_len.value <= self.index.value:
|
if self.index.value < 0 or sa_len.value <= self.index.value:
|
||||||
return Error('Tuple index out of range')
|
return Error('Tuple index out of range')
|
||||||
|
|
||||||
return [
|
return result
|
||||||
SameTypeConstraint(prelude.u32, self.index_phft, comment='([]) :: Subscriptable a => a b -> u32 -> b'),
|
|
||||||
SameTypeConstraint(sa_type, self.ret_type3, comment='([]) :: Subscriptable a => a b -> u32 -> b'),
|
|
||||||
]
|
|
||||||
|
|
||||||
def _generate_tuple(self, tp_args: tuple[types.Type3, ...]) -> CheckResult:
|
|
||||||
# We special case tuples to allow for ease of use to the programmer
|
# We special case tuples to allow for ease of use to the programmer
|
||||||
# e.g. rather than having to do `fst a` and `snd a` and only have to-sized tuples
|
# e.g. rather than having to do `fst a` and `snd a` and only have to-sized tuples
|
||||||
# we use a[0] and a[1] and allow for a[2] and on.
|
# we use a[0] and a[1] and allow for a[2] and on.
|
||||||
|
tp_args = prelude.tuple_.did_construct(exp_type)
|
||||||
|
if tp_args is not None:
|
||||||
if not isinstance(self.index, ourlang.ConstantPrimitive):
|
if not isinstance(self.index, ourlang.ConstantPrimitive):
|
||||||
return Error('Must index with literal')
|
return Error('Must index with literal')
|
||||||
|
|
||||||
@ -511,22 +502,12 @@ class CanBeSubscriptedConstraint(ConstraintBase):
|
|||||||
SameTypeConstraint(tp_args[self.index.value], self.ret_type3, comment=f'Tuple subscript index {self.index.value}'),
|
SameTypeConstraint(tp_args[self.index.value], self.ret_type3, comment=f'Tuple subscript index {self.index.value}'),
|
||||||
]
|
]
|
||||||
|
|
||||||
GENERATE_ROUTER = TypeApplicationRouter['CanBeSubscriptedConstraint', CheckResult]()
|
if exp_type is prelude.bytes_:
|
||||||
GENERATE_ROUTER.add_n(prelude.bytes_, _generate_bytes)
|
return [
|
||||||
GENERATE_ROUTER.add(prelude.static_array, _generate_static_array)
|
SameTypeConstraint(prelude.u32, self.index_phft, comment='([]) :: bytes -> u32 -> u8'),
|
||||||
GENERATE_ROUTER.add(prelude.tuple_, _generate_tuple)
|
SameTypeConstraint(prelude.u8, self.ret_type3, comment='([]) :: bytes -> u32 -> u8'),
|
||||||
|
]
|
||||||
|
|
||||||
def check(self) -> CheckResult:
|
|
||||||
exp_type = self.type3
|
|
||||||
if isinstance(exp_type, placeholders.PlaceholderForType):
|
|
||||||
if exp_type.resolve_as is None:
|
|
||||||
return RequireTypeSubstitutes()
|
|
||||||
|
|
||||||
exp_type = exp_type.resolve_as
|
|
||||||
|
|
||||||
try:
|
|
||||||
return self.__class__.GENERATE_ROUTER(self, exp_type)
|
|
||||||
except NoRouteForTypeException:
|
|
||||||
return Error(f'{exp_type.name} cannot be subscripted')
|
return Error(f'{exp_type.name} cannot be subscripted')
|
||||||
|
|
||||||
def human_readable(self) -> HumanReadableRet:
|
def human_readable(self) -> HumanReadableRet:
|
||||||
|
|||||||
@ -39,57 +39,38 @@ def constant(ctx: Context, inp: ourlang.Constant, phft: placeholders.Placeholder
|
|||||||
|
|
||||||
raise NotImplementedError(constant, inp)
|
raise NotImplementedError(constant, inp)
|
||||||
|
|
||||||
def expression_binary_op(ctx: Context, inp: ourlang.BinaryOp, phft: PlaceholderForType) -> ConstraintGenerator:
|
def expression(ctx: Context, inp: ourlang.Expression, phft: placeholders.PlaceholderForType) -> ConstraintGenerator:
|
||||||
return _expression_function_call(
|
if isinstance(inp, ourlang.Constant):
|
||||||
ctx,
|
yield from constant(ctx, inp, phft)
|
||||||
f'({inp.operator.name})',
|
return
|
||||||
inp.operator.signature,
|
|
||||||
[inp.left, inp.right],
|
|
||||||
inp,
|
|
||||||
phft,
|
|
||||||
)
|
|
||||||
|
|
||||||
def expression_function_call(ctx: Context, inp: ourlang.FunctionCall, phft: PlaceholderForType) -> ConstraintGenerator:
|
if isinstance(inp, ourlang.VariableReference):
|
||||||
return _expression_function_call(
|
yield SameTypeConstraint(inp.variable.type3, phft,
|
||||||
ctx,
|
comment=f'typeOf("{inp.variable.name}") == typeOf({inp.variable.name})')
|
||||||
inp.function.name,
|
return
|
||||||
inp.function.signature,
|
|
||||||
inp.arguments,
|
|
||||||
inp,
|
|
||||||
phft,
|
|
||||||
)
|
|
||||||
|
|
||||||
def _expression_function_call(
|
if isinstance(inp, ourlang.BinaryOp) or isinstance(inp, ourlang.FunctionCall):
|
||||||
ctx: Context,
|
func_name = f'({inp.operator.name})' if isinstance(inp, ourlang.BinaryOp) else inp.function.name
|
||||||
func_name: str,
|
|
||||||
signature: functions.FunctionSignature,
|
arguments = [inp.left, inp.right] if isinstance(inp, ourlang.BinaryOp) else inp.arguments
|
||||||
arguments: list[ourlang.Expression],
|
|
||||||
return_expr: ourlang.Expression,
|
|
||||||
return_phft: PlaceholderForType,
|
|
||||||
) -> ConstraintGenerator:
|
|
||||||
"""
|
|
||||||
Generates all type-level constraints for a function call.
|
|
||||||
|
|
||||||
A Binary operator functions pretty much the same as a function call
|
|
||||||
with two arguments - it's only a syntactic difference.
|
|
||||||
"""
|
|
||||||
arg_placeholders = {
|
arg_placeholders = {
|
||||||
arg_expr: PlaceholderForType([arg_expr])
|
arg_expr: PlaceholderForType([arg_expr])
|
||||||
for arg_expr in arguments
|
for arg_expr in arguments
|
||||||
}
|
}
|
||||||
arg_placeholders[return_expr] = return_phft
|
arg_placeholders[inp] = phft
|
||||||
|
|
||||||
for call_arg in arguments:
|
for call_arg in arguments:
|
||||||
yield from expression(ctx, call_arg, arg_placeholders[call_arg])
|
yield from expression(ctx, call_arg, arg_placeholders[call_arg])
|
||||||
|
|
||||||
|
signature = inp.operator.signature if isinstance(inp, ourlang.BinaryOp) else inp.function.signature
|
||||||
type_var_map = {
|
type_var_map = {
|
||||||
x: placeholders.PlaceholderForType([])
|
x: placeholders.PlaceholderForType([])
|
||||||
for x in signature.args
|
for x in signature.args
|
||||||
if isinstance(x, functions.TypeVariable)
|
if isinstance(x, functions.TypeVariable)
|
||||||
|
or isinstance(x, functions.TypeConstructorVariable)
|
||||||
}
|
}
|
||||||
|
|
||||||
print('type_var_map', type_var_map)
|
|
||||||
|
|
||||||
for arg_expr in arguments:
|
for arg_expr in arguments:
|
||||||
yield from expression(ctx, arg_expr, arg_placeholders[arg_expr])
|
yield from expression(ctx, arg_expr, arg_placeholders[arg_expr])
|
||||||
|
|
||||||
@ -104,7 +85,7 @@ def _expression_function_call(
|
|||||||
|
|
||||||
raise NotImplementedError(constraint)
|
raise NotImplementedError(constraint)
|
||||||
|
|
||||||
for arg_no, (sig_part, arg_expr) in enumerate(zip(signature.args, arguments + [return_expr], strict=True)):
|
for arg_no, (sig_part, arg_expr) in enumerate(zip(signature.args, arguments + [inp], strict=True)):
|
||||||
if arg_no == len(arguments):
|
if arg_no == len(arguments):
|
||||||
comment = f'The type of a function call to {func_name} is the same as the type that the function returns'
|
comment = f'The type of a function call to {func_name} is the same as the type that the function returns'
|
||||||
else:
|
else:
|
||||||
@ -121,24 +102,6 @@ def _expression_function_call(
|
|||||||
raise NotImplementedError(sig_part)
|
raise NotImplementedError(sig_part)
|
||||||
return
|
return
|
||||||
|
|
||||||
def expression(ctx: Context, inp: ourlang.Expression, phft: placeholders.PlaceholderForType) -> ConstraintGenerator:
|
|
||||||
if isinstance(inp, ourlang.Constant):
|
|
||||||
yield from constant(ctx, inp, phft)
|
|
||||||
return
|
|
||||||
|
|
||||||
if isinstance(inp, ourlang.VariableReference):
|
|
||||||
yield SameTypeConstraint(inp.variable.type3, phft,
|
|
||||||
comment=f'typeOf("{inp.variable.name}") == typeOf({inp.variable.name})')
|
|
||||||
return
|
|
||||||
|
|
||||||
if isinstance(inp, ourlang.BinaryOp):
|
|
||||||
yield from expression_binary_op(ctx, inp, phft)
|
|
||||||
return
|
|
||||||
|
|
||||||
if isinstance(inp, ourlang.FunctionCall):
|
|
||||||
yield from expression_function_call(ctx, inp, phft)
|
|
||||||
return
|
|
||||||
|
|
||||||
if isinstance(inp, ourlang.TupleInstantiation):
|
if isinstance(inp, ourlang.TupleInstantiation):
|
||||||
r_type = []
|
r_type = []
|
||||||
for arg in inp.elements:
|
for arg in inp.elements:
|
||||||
@ -165,12 +128,12 @@ def expression(ctx: Context, inp: ourlang.Expression, phft: placeholders.Placeho
|
|||||||
return
|
return
|
||||||
|
|
||||||
if isinstance(inp, ourlang.AccessStructMember):
|
if isinstance(inp, ourlang.AccessStructMember):
|
||||||
assert isinstance(inp.struct_type3.application, type3types.TypeApplication_Struct) # FIXME: See test_struct.py::test_struct_not_accessible
|
assert isinstance(inp.struct_type3, type3types.Type3) # When does this happen?
|
||||||
|
st_args = prelude.struct.did_construct(inp.struct_type3)
|
||||||
mem_typ = dict(inp.struct_type3.application.arguments)[inp.member]
|
assert st_args is not None # FIXME: See test_struct.py::test_struct_not_accessible
|
||||||
|
|
||||||
yield from expression(ctx, inp.varref, PlaceholderForType([inp.varref])) # TODO
|
yield from expression(ctx, inp.varref, PlaceholderForType([inp.varref])) # TODO
|
||||||
yield SameTypeConstraint(mem_typ, phft,
|
yield SameTypeConstraint(st_args[inp.member], phft,
|
||||||
comment=f'The type of a struct member reference is the same as the type of struct member {inp.struct_type3.name}.{inp.member}')
|
comment=f'The type of a struct member reference is the same as the type of struct member {inp.struct_type3.name}.{inp.member}')
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@ -19,18 +19,9 @@ class TypeVariable:
|
|||||||
letter: str
|
letter: str
|
||||||
|
|
||||||
def __init__(self, letter: str) -> None:
|
def __init__(self, letter: str) -> None:
|
||||||
|
assert len(letter) == 1, f'{letter} is not a valid type variable'
|
||||||
self.letter = letter
|
self.letter = letter
|
||||||
|
|
||||||
def deconstruct(self) -> 'TypeConstructorVariable | None':
|
|
||||||
letter_list = self.letter.split(' ')
|
|
||||||
if len(letter_list) == 1:
|
|
||||||
return None
|
|
||||||
|
|
||||||
if len(letter_list) == 2:
|
|
||||||
return TypeConstructorVariable(letter_list[0])
|
|
||||||
|
|
||||||
raise NotImplementedError(letter_list)
|
|
||||||
|
|
||||||
def __hash__(self) -> int:
|
def __hash__(self) -> int:
|
||||||
return hash(self.letter)
|
return hash(self.letter)
|
||||||
|
|
||||||
@ -49,31 +40,18 @@ class TypeConstructorVariable:
|
|||||||
|
|
||||||
They are a lot like TypeVariable, except that they represent a
|
They are a lot like TypeVariable, except that they represent a
|
||||||
type constructor rather than a type directly.
|
type constructor rather than a type directly.
|
||||||
|
|
||||||
For now, we only have type constructor variables for kind
|
|
||||||
* -> *.
|
|
||||||
"""
|
"""
|
||||||
__slots__ = ('letter', )
|
__slots__ = ('letter', 'args', )
|
||||||
|
|
||||||
letter: str
|
def __init__(self, letter: str, args: Iterable[TypeVariable]) -> None:
|
||||||
|
|
||||||
def __init__(self, letter: str) -> None:
|
|
||||||
self.letter = letter
|
self.letter = letter
|
||||||
|
self.args = list(args)
|
||||||
|
|
||||||
def __hash__(self) -> int:
|
def __call__(self, tvar: TypeVariable) -> 'TypeConstructorVariable':
|
||||||
return hash((self.letter, ))
|
return TypeConstructorVariable(self.letter, self.args + [tvar])
|
||||||
|
|
||||||
def __eq__(self, other: Any) -> bool:
|
|
||||||
if not isinstance(other, TypeConstructorVariable):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
return (self.letter == other.letter)
|
|
||||||
|
|
||||||
def __call__(self, tvar: TypeVariable) -> 'TypeVariable':
|
|
||||||
return TypeVariable(self.letter + ' ' + tvar.letter)
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f'TypeConstructorVariable({self.letter!r})'
|
return f'TypeConstructorVariable({self.letter!r}, {self.args!r})'
|
||||||
|
|
||||||
class ConstraintBase:
|
class ConstraintBase:
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
@ -82,9 +60,9 @@ class Constraint_TypeClassInstanceExists(ConstraintBase):
|
|||||||
__slots__ = ('type_class3', 'types', )
|
__slots__ = ('type_class3', 'types', )
|
||||||
|
|
||||||
type_class3: 'Type3Class'
|
type_class3: 'Type3Class'
|
||||||
types: list[TypeVariable]
|
types: list[Union[TypeVariable, TypeConstructorVariable]]
|
||||||
|
|
||||||
def __init__(self, type_class3: 'Type3Class', types: Iterable[TypeVariable]) -> None:
|
def __init__(self, type_class3: 'Type3Class', types: Iterable[Union[TypeVariable, TypeConstructorVariable]]) -> None:
|
||||||
self.type_class3 = type_class3
|
self.type_class3 = type_class3
|
||||||
self.types = list(types)
|
self.types = list(types)
|
||||||
|
|
||||||
@ -92,12 +70,6 @@ class Constraint_TypeClassInstanceExists(ConstraintBase):
|
|||||||
# you can only add a constraint by supplying types for all variables
|
# you can only add a constraint by supplying types for all variables
|
||||||
assert len(self.type_class3.args) == len(self.types)
|
assert len(self.type_class3.args) == len(self.types)
|
||||||
|
|
||||||
def __str__(self) -> str:
|
|
||||||
return self.type_class3.name + ' ' + ' '.join(x.letter for x in self.types)
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return f'Constraint_TypeClassInstanceExists({self.type_class3.name}, {self.types!r})'
|
|
||||||
|
|
||||||
class TypeVariableContext:
|
class TypeVariableContext:
|
||||||
__slots__ = ('constraints', )
|
__slots__ = ('constraints', )
|
||||||
|
|
||||||
@ -109,27 +81,12 @@ class TypeVariableContext:
|
|||||||
def __copy__(self) -> 'TypeVariableContext':
|
def __copy__(self) -> 'TypeVariableContext':
|
||||||
return TypeVariableContext(self.constraints)
|
return TypeVariableContext(self.constraints)
|
||||||
|
|
||||||
def __str__(self) -> str:
|
|
||||||
if not self.constraints:
|
|
||||||
return ''
|
|
||||||
|
|
||||||
return '(' + ', '.join(str(x) for x in self.constraints) + ') => '
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return f'TypeVariableContext({self.constraints!r})'
|
|
||||||
|
|
||||||
class FunctionSignature:
|
class FunctionSignature:
|
||||||
__slots__ = ('context', 'args', )
|
__slots__ = ('context', 'args', )
|
||||||
|
|
||||||
context: TypeVariableContext
|
context: TypeVariableContext
|
||||||
args: List[Union['Type3', TypeVariable]]
|
args: List[Union['Type3', TypeVariable, TypeConstructorVariable]]
|
||||||
|
|
||||||
def __init__(self, context: TypeVariableContext, args: Iterable[Union['Type3', TypeVariable]]) -> None:
|
def __init__(self, context: TypeVariableContext, args: Iterable[Union['Type3', TypeVariable, TypeConstructorVariable]]) -> None:
|
||||||
self.context = context.__copy__()
|
self.context = context.__copy__()
|
||||||
self.args = list(args)
|
self.args = list(args)
|
||||||
|
|
||||||
def __str__(self) -> str:
|
|
||||||
return str(self.context) + ' -> '.join(x.letter if isinstance(x, TypeVariable) else x.name for x in self.args)
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return f'FunctionSignature({self.context!r}, {self.args!r})'
|
|
||||||
|
|||||||
@ -1,88 +0,0 @@
|
|||||||
from typing import Any, Callable, TypeVar
|
|
||||||
|
|
||||||
from .functions import FunctionSignature, TypeVariable
|
|
||||||
from .types import Type3, TypeConstructor_Base
|
|
||||||
|
|
||||||
T = TypeVar('T')
|
|
||||||
|
|
||||||
class NoRouteForTypeException(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class TypeApplicationRouter[S, R]:
|
|
||||||
"""
|
|
||||||
Helper class to find a method based on a constructed type
|
|
||||||
"""
|
|
||||||
__slots__ = ('by_constructor', 'by_type', )
|
|
||||||
|
|
||||||
by_constructor: dict[Any, Callable[[S, Any], R]]
|
|
||||||
"""
|
|
||||||
Contains all the added routing functions for constructed types
|
|
||||||
"""
|
|
||||||
|
|
||||||
by_type: dict[Type3, Callable[[S], R]]
|
|
||||||
"""
|
|
||||||
Contains all the added routing functions for constructed types
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self) -> None:
|
|
||||||
self.by_constructor = {}
|
|
||||||
self.by_type = {}
|
|
||||||
|
|
||||||
def add_n(self, typ: Type3, helper: Callable[[S], R]) -> None:
|
|
||||||
"""
|
|
||||||
Lets you route to types that were not constructed
|
|
||||||
|
|
||||||
Also known types of kind *
|
|
||||||
"""
|
|
||||||
self.by_type[typ] = helper
|
|
||||||
|
|
||||||
def add(self, constructor: TypeConstructor_Base[T], helper: Callable[[S, T], R]) -> None:
|
|
||||||
self.by_constructor[constructor] = helper
|
|
||||||
|
|
||||||
def __call__(self, arg0: S, typ: Type3) -> R:
|
|
||||||
t_helper = self.by_type.get(typ)
|
|
||||||
if t_helper is not None:
|
|
||||||
return t_helper(arg0)
|
|
||||||
|
|
||||||
c_helper = self.by_constructor.get(typ.application.constructor)
|
|
||||||
if c_helper is not None:
|
|
||||||
return c_helper(arg0, typ.application.arguments)
|
|
||||||
|
|
||||||
raise NoRouteForTypeException(arg0, typ)
|
|
||||||
|
|
||||||
class FunctionSignatureRouter[S, R]:
|
|
||||||
"""
|
|
||||||
Helper class to find a method based on a function signature
|
|
||||||
"""
|
|
||||||
__slots__ = ('signature', 'data', )
|
|
||||||
|
|
||||||
signature: FunctionSignature
|
|
||||||
|
|
||||||
data: dict[tuple[Type3, ...], Callable[[S], R]]
|
|
||||||
|
|
||||||
def __init__(self, signature: FunctionSignature) -> None:
|
|
||||||
self.signature = signature
|
|
||||||
self.data = {}
|
|
||||||
|
|
||||||
def add(self, tv_map: dict[TypeVariable, Type3], helper: Callable[[S], R]) -> None:
|
|
||||||
key = tuple(
|
|
||||||
tv_map[x]
|
|
||||||
for x in self.signature.args
|
|
||||||
if isinstance(x, TypeVariable)
|
|
||||||
)
|
|
||||||
# assert len(key) == len(tv_map), (key, tv_map)
|
|
||||||
|
|
||||||
self.data[key] = helper
|
|
||||||
|
|
||||||
def __call__(self, arg0: S, tv_map: dict[TypeVariable, Type3]) -> R:
|
|
||||||
key = tuple(
|
|
||||||
tv_map[x]
|
|
||||||
for x in self.signature.args
|
|
||||||
if isinstance(x, TypeVariable)
|
|
||||||
)
|
|
||||||
|
|
||||||
t_helper = self.data.get(key)
|
|
||||||
if t_helper is not None:
|
|
||||||
return t_helper(arg0)
|
|
||||||
|
|
||||||
raise NoRouteForTypeException(arg0, tv_map)
|
|
||||||
@ -21,9 +21,6 @@ class Type3ClassMethod:
|
|||||||
self.name = name
|
self.name = name
|
||||||
self.signature = signature
|
self.signature = signature
|
||||||
|
|
||||||
def __str__(self) -> str:
|
|
||||||
return f'{self.name} :: {self.signature}'
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f'Type3ClassMethod({repr(self.name)}, {repr(self.signature)})'
|
return f'Type3ClassMethod({repr(self.name)}, {repr(self.signature)})'
|
||||||
|
|
||||||
@ -31,7 +28,7 @@ class Type3Class:
|
|||||||
__slots__ = ('name', 'args', 'methods', 'operators', 'inherited_classes', )
|
__slots__ = ('name', 'args', 'methods', 'operators', 'inherited_classes', )
|
||||||
|
|
||||||
name: str
|
name: str
|
||||||
args: tuple[TypeVariable] | tuple[TypeVariable, TypeVariable] | tuple[TypeConstructorVariable]
|
args: List[Union[TypeVariable, TypeConstructorVariable]]
|
||||||
methods: Dict[str, Type3ClassMethod]
|
methods: Dict[str, Type3ClassMethod]
|
||||||
operators: Dict[str, Type3ClassMethod]
|
operators: Dict[str, Type3ClassMethod]
|
||||||
inherited_classes: List['Type3Class']
|
inherited_classes: List['Type3Class']
|
||||||
@ -39,21 +36,24 @@ class Type3Class:
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
name: str,
|
name: str,
|
||||||
args: tuple[TypeVariable] | tuple[TypeVariable, TypeVariable] | tuple[TypeConstructorVariable],
|
args: Iterable[Union[TypeVariable, TypeConstructorVariable]],
|
||||||
methods: Mapping[str, Iterable[Union[Type3, TypeVariable]]],
|
methods: Mapping[str, Iterable[Union[Type3, TypeVariable, TypeConstructorVariable]]],
|
||||||
operators: Mapping[str, Iterable[Union[Type3, TypeVariable]]],
|
operators: Mapping[str, Iterable[Union[Type3, TypeVariable, TypeConstructorVariable]]],
|
||||||
inherited_classes: Optional[List['Type3Class']] = None,
|
inherited_classes: Optional[List['Type3Class']] = None,
|
||||||
additional_context: Optional[Mapping[str, Iterable[ConstraintBase]]] = None,
|
additional_context: Optional[Mapping[str, Iterable[ConstraintBase]]] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.name = name
|
self.name = name
|
||||||
self.args = args
|
self.args = list(args)
|
||||||
|
|
||||||
|
context = TypeVariableContext()
|
||||||
|
context.constraints.append(Constraint_TypeClassInstanceExists(self, args))
|
||||||
|
|
||||||
self.methods = {
|
self.methods = {
|
||||||
k: Type3ClassMethod(k, _create_signature(v, self))
|
k: Type3ClassMethod(k, FunctionSignature(context, v))
|
||||||
for k, v in methods.items()
|
for k, v in methods.items()
|
||||||
}
|
}
|
||||||
self.operators = {
|
self.operators = {
|
||||||
k: Type3ClassMethod(k, _create_signature(v, self))
|
k: Type3ClassMethod(k, FunctionSignature(context, v))
|
||||||
for k, v in operators.items()
|
for k, v in operators.items()
|
||||||
}
|
}
|
||||||
self.inherited_classes = inherited_classes or []
|
self.inherited_classes = inherited_classes or []
|
||||||
@ -67,32 +67,3 @@ class Type3Class:
|
|||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def _create_signature(
|
|
||||||
method_arg_list: Iterable[Type3 | TypeVariable],
|
|
||||||
type_class3: Type3Class,
|
|
||||||
) -> FunctionSignature:
|
|
||||||
context = TypeVariableContext()
|
|
||||||
if not isinstance(type_class3.args[0], TypeConstructorVariable):
|
|
||||||
context.constraints.append(Constraint_TypeClassInstanceExists(type_class3, type_class3.args))
|
|
||||||
|
|
||||||
signature_args: list[Type3 | TypeVariable] = []
|
|
||||||
for method_arg in method_arg_list:
|
|
||||||
if isinstance(method_arg, Type3):
|
|
||||||
signature_args.append(method_arg)
|
|
||||||
continue
|
|
||||||
|
|
||||||
if isinstance(method_arg, TypeVariable):
|
|
||||||
type_constructor = method_arg.deconstruct()
|
|
||||||
if type_constructor is None:
|
|
||||||
signature_args.append(method_arg)
|
|
||||||
continue
|
|
||||||
|
|
||||||
if (type_constructor, ) == type_class3.args:
|
|
||||||
context.constraints.append(Constraint_TypeClassInstanceExists(type_class3, [method_arg]))
|
|
||||||
signature_args.append(method_arg)
|
|
||||||
continue
|
|
||||||
|
|
||||||
raise NotImplementedError(method_arg)
|
|
||||||
|
|
||||||
return FunctionSignature(context, signature_args)
|
|
||||||
|
|||||||
@ -4,34 +4,15 @@ Contains the final types for use in Phasm, as well as construtors.
|
|||||||
from typing import (
|
from typing import (
|
||||||
Any,
|
Any,
|
||||||
Callable,
|
Callable,
|
||||||
Hashable,
|
Generic,
|
||||||
Self,
|
|
||||||
Tuple,
|
Tuple,
|
||||||
TypeVar,
|
TypeVar,
|
||||||
)
|
)
|
||||||
|
|
||||||
S = TypeVar('S')
|
|
||||||
T = TypeVar('T')
|
|
||||||
|
|
||||||
class KindArgument:
|
class KindArgument:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class TypeApplication_Base[T: Hashable, S: Hashable]:
|
|
||||||
"""
|
|
||||||
Records the constructor and arguments used to create this type.
|
|
||||||
|
|
||||||
Nullary types, or types of kind *, have both arguments set to None.
|
|
||||||
"""
|
|
||||||
constructor: T
|
|
||||||
arguments: S
|
|
||||||
|
|
||||||
def __init__(self, constructor: T, arguments: S) -> None:
|
|
||||||
self.constructor = constructor
|
|
||||||
self.arguments = arguments
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return f'{self.__class__.__name__}({self.constructor!r}, {self.arguments!r})'
|
|
||||||
|
|
||||||
class Type3(KindArgument):
|
class Type3(KindArgument):
|
||||||
"""
|
"""
|
||||||
Base class for the type3 types
|
Base class for the type3 types
|
||||||
@ -39,25 +20,18 @@ class Type3(KindArgument):
|
|||||||
(Having a separate name makes it easier to distinguish from
|
(Having a separate name makes it easier to distinguish from
|
||||||
Python's Type)
|
Python's Type)
|
||||||
"""
|
"""
|
||||||
__slots__ = ('name', 'application', )
|
__slots__ = ('name', )
|
||||||
|
|
||||||
name: str
|
name: str
|
||||||
"""
|
"""
|
||||||
The name of the string, as parsed and outputted by codestyle.
|
The name of the string, as parsed and outputted by codestyle.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
application: TypeApplication_Base[Any, Any]
|
def __init__(self, name: str) -> None:
|
||||||
"""
|
|
||||||
How the type was constructed; i.e. which constructor was used and which
|
|
||||||
type level arguments were applied to the constructor.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, name: str, application: TypeApplication_Base[Any, Any]) -> None:
|
|
||||||
self.name = name
|
self.name = name
|
||||||
self.application = application
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f'Type3({self.name!r}, {self.application!r})'
|
return f'Type3({repr(self.name)})'
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return self.name
|
return self.name
|
||||||
@ -83,9 +57,6 @@ class Type3(KindArgument):
|
|||||||
def __bool__(self) -> bool:
|
def __bool__(self) -> bool:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
class TypeApplication_Nullary(TypeApplication_Base[None, None]):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class IntType3(KindArgument):
|
class IntType3(KindArgument):
|
||||||
"""
|
"""
|
||||||
Sometimes you can have an int on the type level, e.g. when using static arrays
|
Sometimes you can have an int on the type level, e.g. when using static arrays
|
||||||
@ -122,11 +93,13 @@ class IntType3(KindArgument):
|
|||||||
def __hash__(self) -> int:
|
def __hash__(self) -> int:
|
||||||
return hash(self.value)
|
return hash(self.value)
|
||||||
|
|
||||||
class TypeConstructor_Base[T]:
|
T = TypeVar('T')
|
||||||
|
|
||||||
|
class TypeConstructor(Generic[T]):
|
||||||
"""
|
"""
|
||||||
Base class for type construtors
|
Base class for type construtors
|
||||||
"""
|
"""
|
||||||
__slots__ = ('name', 'on_create', '_cache', )
|
__slots__ = ('name', 'on_create', '_cache', '_reverse_cache')
|
||||||
|
|
||||||
name: str
|
name: str
|
||||||
"""
|
"""
|
||||||
@ -144,26 +117,31 @@ class TypeConstructor_Base[T]:
|
|||||||
it should produce the exact same result.
|
it should produce the exact same result.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
_reverse_cache: dict[Type3, T]
|
||||||
|
"""
|
||||||
|
Sometimes we need to know the key that created a type.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, name: str, on_create: Callable[[T, Type3], None]) -> None:
|
def __init__(self, name: str, on_create: Callable[[T, Type3], None]) -> None:
|
||||||
self.name = name
|
self.name = name
|
||||||
self.on_create = on_create
|
self.on_create = on_create
|
||||||
|
|
||||||
self._cache = {}
|
self._cache = {}
|
||||||
|
self._reverse_cache = {}
|
||||||
|
|
||||||
def make_name(self, key: T) -> str:
|
def make_name(self, key: T) -> str:
|
||||||
"""
|
"""
|
||||||
Renders the type's name based on the given arguments
|
Renders the type's name based on the given arguments
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError('make_name', self)
|
raise NotImplementedError
|
||||||
|
|
||||||
def make_application(self, key: T) -> TypeApplication_Base[Self, T]:
|
def did_construct(self, typ: Type3) -> T | None:
|
||||||
"""
|
"""
|
||||||
Records how the type was constructed into type.
|
Was the given type constructed by this constructor?
|
||||||
|
|
||||||
The type checker and compiler will need to know what
|
If so, which arguments where used?
|
||||||
arguments where made to construct the type.
|
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError('make_application', self)
|
return self._reverse_cache.get(typ)
|
||||||
|
|
||||||
def construct(self, key: T) -> Type3:
|
def construct(self, key: T) -> Type3:
|
||||||
"""
|
"""
|
||||||
@ -172,12 +150,22 @@ class TypeConstructor_Base[T]:
|
|||||||
"""
|
"""
|
||||||
result = self._cache.get(key, None)
|
result = self._cache.get(key, None)
|
||||||
if result is None:
|
if result is None:
|
||||||
self._cache[key] = result = Type3(self.make_name(key), self.make_application(key))
|
self._cache[key] = result = Type3(self.make_name(key))
|
||||||
|
self._reverse_cache[result] = key
|
||||||
self.on_create(key, result)
|
self.on_create(key, result)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
class TypeConstructor_TypeInt(TypeConstructor_Base[Tuple[Type3, IntType3]]):
|
class TypeConstructor_Type(TypeConstructor[Type3]):
|
||||||
|
"""
|
||||||
|
Base class type constructors of kind: * -> *
|
||||||
|
"""
|
||||||
|
__slots__ = ()
|
||||||
|
|
||||||
|
def __call__(self, arg: Type3) -> Type3:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
class TypeConstructor_TypeInt(TypeConstructor[Tuple[Type3, IntType3]]):
|
||||||
"""
|
"""
|
||||||
Base class type constructors of kind: * -> Int -> *
|
Base class type constructors of kind: * -> Int -> *
|
||||||
|
|
||||||
@ -185,34 +173,22 @@ class TypeConstructor_TypeInt(TypeConstructor_Base[Tuple[Type3, IntType3]]):
|
|||||||
"""
|
"""
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
|
||||||
def make_application(self, key: Tuple[Type3, IntType3]) -> 'TypeApplication_TypeInt':
|
|
||||||
return TypeApplication_TypeInt(self, key)
|
|
||||||
|
|
||||||
def make_name(self, key: Tuple[Type3, IntType3]) -> str:
|
def make_name(self, key: Tuple[Type3, IntType3]) -> str:
|
||||||
return f'{self.name} {key[0].name} {key[1].value}'
|
return f'{self.name} {key[0].name} {key[1].value}'
|
||||||
|
|
||||||
def __call__(self, arg0: Type3, arg1: IntType3) -> Type3:
|
def __call__(self, arg0: Type3, arg1: IntType3) -> Type3:
|
||||||
return self.construct((arg0, arg1))
|
return self.construct((arg0, arg1))
|
||||||
|
|
||||||
class TypeApplication_TypeInt(TypeApplication_Base[TypeConstructor_TypeInt, Tuple[Type3, IntType3]]):
|
class TypeConstructor_TypeStar(TypeConstructor[Tuple[Type3, ...]]):
|
||||||
pass
|
|
||||||
|
|
||||||
class TypeConstructor_TypeStar(TypeConstructor_Base[Tuple[Type3, ...]]):
|
|
||||||
"""
|
"""
|
||||||
Base class type constructors of variadic kind
|
Base class type constructors of variadic kind
|
||||||
|
|
||||||
Notably, tuple.
|
Notably, tuple.
|
||||||
"""
|
"""
|
||||||
def make_application(self, key: Tuple[Type3, ...]) -> 'TypeApplication_TypeStar':
|
|
||||||
return TypeApplication_TypeStar(self, key)
|
|
||||||
|
|
||||||
def __call__(self, *args: Type3) -> Type3:
|
def __call__(self, *args: Type3) -> Type3:
|
||||||
key: Tuple[Type3, ...] = tuple(args)
|
key: Tuple[Type3, ...] = tuple(args)
|
||||||
return self.construct(key)
|
return self.construct(key)
|
||||||
|
|
||||||
class TypeApplication_TypeStar(TypeApplication_Base[TypeConstructor_TypeStar, Tuple[Type3, ...]]):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class TypeConstructor_StaticArray(TypeConstructor_TypeInt):
|
class TypeConstructor_StaticArray(TypeConstructor_TypeInt):
|
||||||
def make_name(self, key: Tuple[Type3, IntType3]) -> str:
|
def make_name(self, key: Tuple[Type3, IntType3]) -> str:
|
||||||
return f'{key[0].name}[{key[1].value}]'
|
return f'{key[0].name}[{key[1].value}]'
|
||||||
@ -221,30 +197,52 @@ class TypeConstructor_Tuple(TypeConstructor_TypeStar):
|
|||||||
def make_name(self, key: Tuple[Type3, ...]) -> str:
|
def make_name(self, key: Tuple[Type3, ...]) -> str:
|
||||||
return '(' + ', '.join(x.name for x in key) + ', )'
|
return '(' + ', '.join(x.name for x in key) + ', )'
|
||||||
|
|
||||||
class TypeConstructor_Struct(TypeConstructor_Base[tuple[tuple[str, Type3], ...]]):
|
class TypeConstructor_Struct:
|
||||||
"""
|
"""
|
||||||
Constructs struct types
|
Base class for type construtors
|
||||||
"""
|
"""
|
||||||
def make_application(self, key: tuple[tuple[str, Type3], ...]) -> 'TypeApplication_Struct':
|
__slots__ = ('name', 'on_create', '_cache', '_reverse_cache')
|
||||||
return TypeApplication_Struct(self, key)
|
|
||||||
|
|
||||||
def make_name(self, key: tuple[tuple[str, Type3], ...]) -> str:
|
name: str
|
||||||
return f'{self.name}(' + ', '.join(
|
|
||||||
f'{n}: {t.name}'
|
|
||||||
for n, t in key
|
|
||||||
) + ')'
|
|
||||||
|
|
||||||
def construct(self, key: T) -> Type3:
|
|
||||||
"""
|
"""
|
||||||
Constructs the type by applying the given arguments to this
|
The name of the type constructor
|
||||||
constructor.
|
|
||||||
"""
|
"""
|
||||||
raise Exception('This does not work with the caching system')
|
|
||||||
|
|
||||||
def __call__(self, name: str, args: tuple[tuple[str, Type3], ...]) -> Type3:
|
on_create: Callable[[Type3], None]
|
||||||
result = Type3(name, self.make_application(args))
|
"""
|
||||||
self.on_create(args, result)
|
Who to let know if a type is created
|
||||||
|
"""
|
||||||
|
|
||||||
|
_cache: dict[str, Type3]
|
||||||
|
"""
|
||||||
|
When constructing a type with the same arguments,
|
||||||
|
it should produce the exact same result.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_reverse_cache: dict[Type3, dict[str, Type3]]
|
||||||
|
"""
|
||||||
|
After construction you may need to look up the arguments
|
||||||
|
used for making the type
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, name: str, on_create: Callable[[Type3], None]) -> None:
|
||||||
|
self.name = name
|
||||||
|
self.on_create = on_create
|
||||||
|
|
||||||
|
self._cache = {}
|
||||||
|
self._reverse_cache = {}
|
||||||
|
|
||||||
|
def did_construct(self, typ: Type3) -> dict[str, Type3] | None:
|
||||||
|
"""
|
||||||
|
Was the given type constructed by this constructor?
|
||||||
|
|
||||||
|
If so, which arguments where used?
|
||||||
|
"""
|
||||||
|
return self._reverse_cache.get(typ)
|
||||||
|
|
||||||
|
def __call__(self, name: str, args: dict[str, Type3]) -> Type3:
|
||||||
|
result = Type3(name)
|
||||||
|
self._reverse_cache[result] = args
|
||||||
|
self.on_create(result)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
class TypeApplication_Struct(TypeApplication_Base[TypeConstructor_Struct, tuple[tuple[str, Type3], ...]]):
|
|
||||||
pass
|
|
||||||
|
|||||||
@ -4,14 +4,8 @@ from typing import Any, Generator, Iterable, List, TextIO, Union
|
|||||||
|
|
||||||
from phasm import compiler, prelude
|
from phasm import compiler, prelude
|
||||||
from phasm.codestyle import phasm_render
|
from phasm.codestyle import phasm_render
|
||||||
from phasm.runtime import (
|
from phasm.runtime import calculate_alloc_size
|
||||||
calculate_alloc_size,
|
|
||||||
calculate_alloc_size_static_array,
|
|
||||||
calculate_alloc_size_struct,
|
|
||||||
calculate_alloc_size_tuple,
|
|
||||||
)
|
|
||||||
from phasm.type3 import types as type3types
|
from phasm.type3 import types as type3types
|
||||||
from phasm.type3.routers import NoRouteForTypeException, TypeApplicationRouter
|
|
||||||
|
|
||||||
from . import runners
|
from . import runners
|
||||||
|
|
||||||
@ -85,10 +79,29 @@ class Suite:
|
|||||||
wasm_args.append(arg)
|
wasm_args.append(arg)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
try:
|
if arg_typ is prelude.bytes_:
|
||||||
adr = ALLOCATE_MEMORY_STORED_ROUTER((runner, arg), arg_typ)
|
adr = _allocate_memory_stored_value(runner, arg_typ, arg)
|
||||||
wasm_args.append(adr)
|
wasm_args.append(adr)
|
||||||
except NoRouteForTypeException:
|
continue
|
||||||
|
|
||||||
|
sa_args = prelude.static_array.did_construct(arg_typ)
|
||||||
|
if sa_args is not None:
|
||||||
|
adr = _allocate_memory_stored_value(runner, arg_typ, arg)
|
||||||
|
wasm_args.append(adr)
|
||||||
|
continue
|
||||||
|
|
||||||
|
tp_args = prelude.tuple_.did_construct(arg_typ)
|
||||||
|
if tp_args is not None:
|
||||||
|
adr = _allocate_memory_stored_value(runner, arg_typ, arg)
|
||||||
|
wasm_args.append(adr)
|
||||||
|
continue
|
||||||
|
|
||||||
|
st_args = prelude.struct.did_construct(arg_typ)
|
||||||
|
if st_args is not None:
|
||||||
|
adr = _allocate_memory_stored_value(runner, arg_typ, arg)
|
||||||
|
wasm_args.append(adr)
|
||||||
|
continue
|
||||||
|
|
||||||
raise NotImplementedError(arg_typ, arg)
|
raise NotImplementedError(arg_typ, arg)
|
||||||
|
|
||||||
write_header(sys.stderr, 'Memory (pre run)')
|
write_header(sys.stderr, 'Memory (pre run)')
|
||||||
@ -128,18 +141,39 @@ def _write_memory_stored_value(
|
|||||||
val_typ: type3types.Type3,
|
val_typ: type3types.Type3,
|
||||||
val: Any,
|
val: Any,
|
||||||
) -> int:
|
) -> int:
|
||||||
try:
|
if val_typ is prelude.bytes_:
|
||||||
adr2 = ALLOCATE_MEMORY_STORED_ROUTER((runner, val), val_typ)
|
adr2 = _allocate_memory_stored_value(runner, val_typ, val)
|
||||||
runner.interpreter_write_memory(adr, compiler.module_data_u32(adr2))
|
runner.interpreter_write_memory(adr, compiler.module_data_u32(adr2))
|
||||||
return 4
|
return 4
|
||||||
except NoRouteForTypeException:
|
|
||||||
|
st_args = prelude.struct.did_construct(val_typ)
|
||||||
|
if st_args is not None:
|
||||||
|
adr2 = _allocate_memory_stored_value(runner, val_typ, val)
|
||||||
|
runner.interpreter_write_memory(adr, compiler.module_data_u32(adr2))
|
||||||
|
return 4
|
||||||
|
|
||||||
|
sa_args = prelude.static_array.did_construct(val_typ)
|
||||||
|
if sa_args is not None:
|
||||||
|
adr2 = _allocate_memory_stored_value(runner, val_typ, val)
|
||||||
|
runner.interpreter_write_memory(adr, compiler.module_data_u32(adr2))
|
||||||
|
return 4
|
||||||
|
|
||||||
|
tp_args = prelude.tuple_.did_construct(val_typ)
|
||||||
|
if tp_args is not None:
|
||||||
|
adr2 = _allocate_memory_stored_value(runner, val_typ, val)
|
||||||
|
runner.interpreter_write_memory(adr, compiler.module_data_u32(adr2))
|
||||||
|
return 4
|
||||||
|
|
||||||
to_write = WRITE_LOOKUP_MAP[val_typ.name](val)
|
to_write = WRITE_LOOKUP_MAP[val_typ.name](val)
|
||||||
runner.interpreter_write_memory(adr, to_write)
|
runner.interpreter_write_memory(adr, to_write)
|
||||||
return len(to_write)
|
return len(to_write)
|
||||||
|
|
||||||
def _allocate_memory_stored_bytes(attrs: tuple[runners.RunnerBase, bytes]) -> int:
|
def _allocate_memory_stored_value(
|
||||||
runner, val = attrs
|
runner: runners.RunnerBase,
|
||||||
|
val_typ: type3types.Type3,
|
||||||
|
val: Any
|
||||||
|
) -> int:
|
||||||
|
if val_typ is prelude.bytes_:
|
||||||
assert isinstance(val, bytes)
|
assert isinstance(val, bytes)
|
||||||
|
|
||||||
adr = runner.call('stdlib.types.__alloc_bytes__', len(val))
|
adr = runner.call('stdlib.types.__alloc_bytes__', len(val))
|
||||||
@ -149,14 +183,13 @@ def _allocate_memory_stored_bytes(attrs: tuple[runners.RunnerBase, bytes]) -> in
|
|||||||
runner.interpreter_write_memory(adr + 4, val)
|
runner.interpreter_write_memory(adr + 4, val)
|
||||||
return adr
|
return adr
|
||||||
|
|
||||||
def _allocate_memory_stored_static_array(attrs: tuple[runners.RunnerBase, Any], sa_args: tuple[type3types.Type3, type3types.IntType3]) -> int:
|
sa_args = prelude.static_array.did_construct(val_typ)
|
||||||
runner, val = attrs
|
if sa_args is not None:
|
||||||
|
|
||||||
assert isinstance(val, tuple)
|
assert isinstance(val, tuple)
|
||||||
|
|
||||||
sa_type, sa_len = sa_args
|
sa_type, sa_len = sa_args
|
||||||
|
|
||||||
alloc_size = calculate_alloc_size_static_array(False, sa_args)
|
alloc_size = calculate_alloc_size(val_typ)
|
||||||
adr = runner.call('stdlib.alloc.__alloc__', alloc_size)
|
adr = runner.call('stdlib.alloc.__alloc__', alloc_size)
|
||||||
assert isinstance(adr, int)
|
assert isinstance(adr, int)
|
||||||
sys.stderr.write(f'Allocation 0x{adr:08x} {repr(val)}\n')
|
sys.stderr.write(f'Allocation 0x{adr:08x} {repr(val)}\n')
|
||||||
@ -169,32 +202,13 @@ def _allocate_memory_stored_static_array(attrs: tuple[runners.RunnerBase, Any],
|
|||||||
offset += _write_memory_stored_value(runner, offset, sa_type, val_el_val)
|
offset += _write_memory_stored_value(runner, offset, sa_type, val_el_val)
|
||||||
return adr
|
return adr
|
||||||
|
|
||||||
def _allocate_memory_stored_struct(attrs: tuple[runners.RunnerBase, Any], st_args: tuple[tuple[str, type3types.Type3], ...]) -> int:
|
val_el_typ: type3types.Type3
|
||||||
runner, val = attrs
|
|
||||||
|
|
||||||
assert isinstance(val, dict)
|
|
||||||
|
|
||||||
alloc_size = calculate_alloc_size_struct(False, st_args)
|
|
||||||
adr = runner.call('stdlib.alloc.__alloc__', alloc_size)
|
|
||||||
assert isinstance(adr, int)
|
|
||||||
sys.stderr.write(f'Allocation 0x{adr:08x} {repr(val)}\n')
|
|
||||||
|
|
||||||
offset = adr
|
|
||||||
for val_el_name, val_el_typ in st_args:
|
|
||||||
assert val_el_name in val, f'Missing key value {val_el_name}'
|
|
||||||
val_el_val = val.pop(val_el_name)
|
|
||||||
offset += _write_memory_stored_value(runner, offset, val_el_typ, val_el_val)
|
|
||||||
|
|
||||||
assert not val, f'Additional values: {list(val)!r}'
|
|
||||||
|
|
||||||
return adr
|
|
||||||
|
|
||||||
def _allocate_memory_stored_tuple(attrs: tuple[runners.RunnerBase, Any], tp_args: tuple[type3types.Type3, ...]) -> int:
|
|
||||||
runner, val = attrs
|
|
||||||
|
|
||||||
|
tp_args = prelude.tuple_.did_construct(val_typ)
|
||||||
|
if tp_args is not None:
|
||||||
assert isinstance(val, tuple)
|
assert isinstance(val, tuple)
|
||||||
|
|
||||||
alloc_size = calculate_alloc_size_tuple(False, tp_args)
|
alloc_size = calculate_alloc_size(val_typ)
|
||||||
adr = runner.call('stdlib.alloc.__alloc__', alloc_size)
|
adr = runner.call('stdlib.alloc.__alloc__', alloc_size)
|
||||||
assert isinstance(adr, int)
|
assert isinstance(adr, int)
|
||||||
sys.stderr.write(f'Allocation 0x{adr:08x} {repr(val)}\n')
|
sys.stderr.write(f'Allocation 0x{adr:08x} {repr(val)}\n')
|
||||||
@ -206,11 +220,24 @@ def _allocate_memory_stored_tuple(attrs: tuple[runners.RunnerBase, Any], tp_args
|
|||||||
offset += _write_memory_stored_value(runner, offset, val_el_typ, val_el_val)
|
offset += _write_memory_stored_value(runner, offset, val_el_typ, val_el_val)
|
||||||
return adr
|
return adr
|
||||||
|
|
||||||
ALLOCATE_MEMORY_STORED_ROUTER = TypeApplicationRouter[tuple[runners.RunnerBase, Any], Any]()
|
st_args = prelude.struct.did_construct(val_typ)
|
||||||
ALLOCATE_MEMORY_STORED_ROUTER.add_n(prelude.bytes_, _allocate_memory_stored_bytes)
|
if st_args is not None:
|
||||||
ALLOCATE_MEMORY_STORED_ROUTER.add(prelude.static_array, _allocate_memory_stored_static_array)
|
assert isinstance(val, dict)
|
||||||
ALLOCATE_MEMORY_STORED_ROUTER.add(prelude.struct, _allocate_memory_stored_struct)
|
|
||||||
ALLOCATE_MEMORY_STORED_ROUTER.add(prelude.tuple_, _allocate_memory_stored_tuple)
|
alloc_size = calculate_alloc_size(val_typ)
|
||||||
|
adr = runner.call('stdlib.alloc.__alloc__', alloc_size)
|
||||||
|
assert isinstance(adr, int)
|
||||||
|
sys.stderr.write(f'Allocation 0x{adr:08x} {repr(val)}\n')
|
||||||
|
|
||||||
|
assert list(val.keys()) == list(st_args)
|
||||||
|
|
||||||
|
offset = adr
|
||||||
|
for val_el_name, val_el_typ in st_args.items():
|
||||||
|
val_el_val = val[val_el_name]
|
||||||
|
offset += _write_memory_stored_value(runner, offset, val_el_typ, val_el_val)
|
||||||
|
return adr
|
||||||
|
|
||||||
|
raise NotImplementedError(val_typ, val)
|
||||||
|
|
||||||
def _load_memory_stored_returned_value(
|
def _load_memory_stored_returned_value(
|
||||||
runner: runners.RunnerBase,
|
runner: runners.RunnerBase,
|
||||||
@ -258,9 +285,28 @@ def _load_memory_stored_returned_value(
|
|||||||
assert isinstance(wasm_value, float), wasm_value
|
assert isinstance(wasm_value, float), wasm_value
|
||||||
return wasm_value
|
return wasm_value
|
||||||
|
|
||||||
|
if ret_type3 is prelude.bytes_:
|
||||||
assert isinstance(wasm_value, int), wasm_value
|
assert isinstance(wasm_value, int), wasm_value
|
||||||
|
|
||||||
return LOAD_FROM_ADDRESS_ROUTER((runner, wasm_value), ret_type3)
|
return _load_bytes_from_address(runner, ret_type3, wasm_value)
|
||||||
|
|
||||||
|
sa_args = prelude.static_array.did_construct(ret_type3)
|
||||||
|
if sa_args is not None:
|
||||||
|
assert isinstance(wasm_value, int), wasm_value
|
||||||
|
|
||||||
|
return _load_static_array_from_address(runner, sa_args[0], sa_args[1], wasm_value)
|
||||||
|
|
||||||
|
tp_args = prelude.tuple_.did_construct(ret_type3)
|
||||||
|
if tp_args is not None:
|
||||||
|
assert isinstance(wasm_value, int), wasm_value
|
||||||
|
|
||||||
|
return _load_tuple_from_address(runner, tp_args, wasm_value)
|
||||||
|
|
||||||
|
st_args = prelude.struct.did_construct(ret_type3)
|
||||||
|
if st_args is not None:
|
||||||
|
return _load_struct_from_address(runner, st_args, wasm_value)
|
||||||
|
|
||||||
|
raise NotImplementedError(ret_type3, wasm_value)
|
||||||
|
|
||||||
def _unpack(runner: runners.RunnerBase, typ: type3types.Type3, inp: bytes) -> Any:
|
def _unpack(runner: runners.RunnerBase, typ: type3types.Type3, inp: bytes) -> Any:
|
||||||
if typ is prelude.u8:
|
if typ is prelude.u8:
|
||||||
@ -297,19 +343,39 @@ def _unpack(runner: runners.RunnerBase, typ: type3types.Type3, inp: bytes) -> An
|
|||||||
assert len(inp) == 8
|
assert len(inp) == 8
|
||||||
return struct.unpack('<d', inp)[0]
|
return struct.unpack('<d', inp)[0]
|
||||||
|
|
||||||
|
if typ is prelude.bytes_:
|
||||||
|
# Note: For bytes, inp should contain a 4 byte pointer
|
||||||
|
assert len(inp) == 4
|
||||||
|
adr = struct.unpack('<I', inp)[0]
|
||||||
|
|
||||||
|
return _load_bytes_from_address(runner, typ, adr)
|
||||||
|
|
||||||
if (prelude.InternalPassAsPointer, (typ, )) in prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING:
|
if (prelude.InternalPassAsPointer, (typ, )) in prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING:
|
||||||
# Note: For applied types, inp should contain a 4 byte pointer
|
# Note: For applied types, inp should contain a 4 byte pointer
|
||||||
assert len(inp) == 4
|
assert len(inp) == 4
|
||||||
adr = struct.unpack('<I', inp)[0]
|
adr = struct.unpack('<I', inp)[0]
|
||||||
|
|
||||||
return LOAD_FROM_ADDRESS_ROUTER((runner, adr), typ)
|
sa_args = prelude.static_array.did_construct(typ)
|
||||||
|
if sa_args is not None:
|
||||||
|
sa_type, sa_len = sa_args
|
||||||
|
return _load_static_array_from_address(runner, sa_type, sa_len, adr)
|
||||||
|
|
||||||
|
tp_args = prelude.tuple_.did_construct(typ)
|
||||||
|
if tp_args is not None:
|
||||||
|
return _load_tuple_from_address(runner, tp_args, adr)
|
||||||
|
|
||||||
|
st_args = prelude.struct.did_construct(typ)
|
||||||
|
if st_args is not None:
|
||||||
|
# Note: For structs, inp should contain a 4 byte pointer
|
||||||
|
assert len(inp) == 4
|
||||||
|
adr = struct.unpack('<I', inp)[0]
|
||||||
|
|
||||||
|
return _load_struct_from_address(runner, st_args, adr)
|
||||||
|
|
||||||
raise NotImplementedError(typ, inp)
|
raise NotImplementedError(typ, inp)
|
||||||
|
|
||||||
def _load_bytes_from_address(attrs: tuple[runners.RunnerBase, int]) -> bytes:
|
def _load_bytes_from_address(runner: runners.RunnerBase, typ: type3types.Type3, adr: int) -> bytes:
|
||||||
runner, adr = attrs
|
sys.stderr.write(f'Reading 0x{adr:08x} {typ:s}\n')
|
||||||
|
|
||||||
sys.stderr.write(f'Reading 0x{adr:08x} bytes\n')
|
|
||||||
read_bytes = runner.interpreter_read_memory(adr, 4)
|
read_bytes = runner.interpreter_read_memory(adr, 4)
|
||||||
bytes_len, = struct.unpack('<I', read_bytes)
|
bytes_len, = struct.unpack('<I', read_bytes)
|
||||||
|
|
||||||
@ -322,10 +388,7 @@ def _split_read_bytes(all_bytes: bytes, split_sizes: Iterable[int]) -> Generator
|
|||||||
yield all_bytes[offset:offset + size]
|
yield all_bytes[offset:offset + size]
|
||||||
offset += size
|
offset += size
|
||||||
|
|
||||||
def _load_static_array_from_address(attrs: tuple[runners.RunnerBase, int], sa_args: tuple[type3types.Type3, type3types.IntType3]) -> Any:
|
def _load_static_array_from_address(runner: runners.RunnerBase, sub_typ: type3types.Type3, len_typ: type3types.IntType3, adr: int) -> Any:
|
||||||
runner, adr = attrs
|
|
||||||
sub_typ, len_typ = sa_args
|
|
||||||
|
|
||||||
sys.stderr.write(f'Reading 0x{adr:08x} {sub_typ:s} {len_typ:s}\n')
|
sys.stderr.write(f'Reading 0x{adr:08x} {sub_typ:s} {len_typ:s}\n')
|
||||||
|
|
||||||
sa_len = len_typ.value
|
sa_len = len_typ.value
|
||||||
@ -340,42 +403,37 @@ def _load_static_array_from_address(attrs: tuple[runners.RunnerBase, int], sa_ar
|
|||||||
for arg_bytes in _split_read_bytes(read_bytes, arg_sizes)
|
for arg_bytes in _split_read_bytes(read_bytes, arg_sizes)
|
||||||
)
|
)
|
||||||
|
|
||||||
def _load_struct_from_address(attrs: tuple[runners.RunnerBase, int], st_args: tuple[tuple[str, type3types.Type3], ...]) -> dict[str, Any]:
|
def _load_tuple_from_address(runner: runners.RunnerBase, typ_args: tuple[type3types.Type3, ...], adr: int) -> Any:
|
||||||
runner, adr = attrs
|
sys.stderr.write(f'Reading 0x{adr:08x} tuple {len(typ_args)}\n')
|
||||||
|
|
||||||
sys.stderr.write(f'Reading 0x{adr:08x} struct {list(st_args)}\n')
|
|
||||||
|
|
||||||
arg_sizes = [
|
arg_sizes = [
|
||||||
calculate_alloc_size(x, is_member=True)
|
calculate_alloc_size(x, is_member=True)
|
||||||
for _, x in st_args
|
for x in typ_args
|
||||||
]
|
|
||||||
|
|
||||||
read_bytes = runner.interpreter_read_memory(adr, sum(arg_sizes))
|
|
||||||
|
|
||||||
return {
|
|
||||||
arg_name: _unpack(runner, arg_typ, arg_bytes)
|
|
||||||
for (arg_name, arg_typ, ), arg_bytes in zip(st_args, _split_read_bytes(read_bytes, arg_sizes), strict=True)
|
|
||||||
}
|
|
||||||
|
|
||||||
def _load_tuple_from_address(attrs: tuple[runners.RunnerBase, int], tp_args: tuple[type3types.Type3, ...]) -> Any:
|
|
||||||
runner, adr = attrs
|
|
||||||
|
|
||||||
sys.stderr.write(f'Reading 0x{adr:08x} tuple {len(tp_args)}\n')
|
|
||||||
|
|
||||||
arg_sizes = [
|
|
||||||
calculate_alloc_size(x, is_member=True)
|
|
||||||
for x in tp_args
|
|
||||||
]
|
]
|
||||||
|
|
||||||
read_bytes = runner.interpreter_read_memory(adr, sum(arg_sizes))
|
read_bytes = runner.interpreter_read_memory(adr, sum(arg_sizes))
|
||||||
|
|
||||||
return tuple(
|
return tuple(
|
||||||
_unpack(runner, arg_typ, arg_bytes)
|
_unpack(runner, arg_typ, arg_bytes)
|
||||||
for arg_typ, arg_bytes in zip(tp_args, _split_read_bytes(read_bytes, arg_sizes), strict=True)
|
for arg_typ, arg_bytes in zip(typ_args, _split_read_bytes(read_bytes, arg_sizes), strict=True)
|
||||||
)
|
)
|
||||||
|
|
||||||
LOAD_FROM_ADDRESS_ROUTER = TypeApplicationRouter[tuple[runners.RunnerBase, int], Any]()
|
def _load_struct_from_address(runner: runners.RunnerBase, st_args: dict[str, type3types.Type3], adr: int) -> Any:
|
||||||
LOAD_FROM_ADDRESS_ROUTER.add_n(prelude.bytes_, _load_bytes_from_address)
|
sys.stderr.write(f'Reading 0x{adr:08x} struct {list(st_args)}\n')
|
||||||
LOAD_FROM_ADDRESS_ROUTER.add(prelude.static_array, _load_static_array_from_address)
|
|
||||||
LOAD_FROM_ADDRESS_ROUTER.add(prelude.struct, _load_struct_from_address)
|
name_list = list(st_args)
|
||||||
LOAD_FROM_ADDRESS_ROUTER.add(prelude.tuple_, _load_tuple_from_address)
|
|
||||||
|
typ_list = list(st_args.values())
|
||||||
|
assert len(typ_list) == len(st_args)
|
||||||
|
|
||||||
|
arg_sizes = [
|
||||||
|
calculate_alloc_size(x, is_member=True)
|
||||||
|
for x in typ_list
|
||||||
|
]
|
||||||
|
|
||||||
|
read_bytes = runner.interpreter_read_memory(adr, sum(arg_sizes))
|
||||||
|
|
||||||
|
return {
|
||||||
|
arg_name: _unpack(runner, arg_typ, arg_bytes)
|
||||||
|
for arg_name, arg_typ, arg_bytes in zip(name_list, typ_list, _split_read_bytes(read_bytes, arg_sizes), strict=True)
|
||||||
|
}
|
||||||
|
|||||||
@ -1,30 +0,0 @@
|
|||||||
import pytest
|
|
||||||
|
|
||||||
from ..helpers import Suite
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
|
||||||
def test_bytes_export_constant():
|
|
||||||
code_py = """
|
|
||||||
CONSTANT: bytes = b'Hello'
|
|
||||||
|
|
||||||
@exported
|
|
||||||
def testEntry() -> bytes:
|
|
||||||
return CONSTANT
|
|
||||||
"""
|
|
||||||
|
|
||||||
result = Suite(code_py).run_code()
|
|
||||||
|
|
||||||
assert b"Hello" == result.returned_value
|
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
|
||||||
def test_bytes_export_instantiation():
|
|
||||||
code_py = """
|
|
||||||
@exported
|
|
||||||
def testEntry() -> bytes:
|
|
||||||
return b'Hello'
|
|
||||||
"""
|
|
||||||
|
|
||||||
result = Suite(code_py).run_code()
|
|
||||||
|
|
||||||
assert b"Hello" == result.returned_value
|
|
||||||
@ -30,29 +30,3 @@ def testEntry() -> i32:
|
|||||||
|
|
||||||
with pytest.raises(Type3Exception, match='Member count mismatch'):
|
with pytest.raises(Type3Exception, match='Member count mismatch'):
|
||||||
Suite(code_py).run_code()
|
Suite(code_py).run_code()
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
|
||||||
def test_static_array_export_constant():
|
|
||||||
code_py = """
|
|
||||||
CONSTANT: u8[3] = (1, 2, 3, )
|
|
||||||
|
|
||||||
@exported
|
|
||||||
def testEntry() -> u8[3]:
|
|
||||||
return CONSTANT
|
|
||||||
"""
|
|
||||||
|
|
||||||
result = Suite(code_py).run_code()
|
|
||||||
|
|
||||||
assert (1, 2, 3) == result.returned_value
|
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
|
||||||
def test_static_array_export_instantiation():
|
|
||||||
code_py = """
|
|
||||||
@exported
|
|
||||||
def testEntry() -> u8[3]:
|
|
||||||
return (1, 2, 3, )
|
|
||||||
"""
|
|
||||||
|
|
||||||
result = Suite(code_py).run_code()
|
|
||||||
|
|
||||||
assert (1, 2, 3) == result.returned_value
|
|
||||||
|
|||||||
@ -112,35 +112,3 @@ def testEntry(x: u8) -> u8:
|
|||||||
|
|
||||||
with pytest.raises(Type3Exception, match='u8 is not struct'):
|
with pytest.raises(Type3Exception, match='u8 is not struct'):
|
||||||
Suite(code_py).run_code()
|
Suite(code_py).run_code()
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
|
||||||
def test_struct_export_constant():
|
|
||||||
code_py = """
|
|
||||||
class CheckedValue:
|
|
||||||
value: i32
|
|
||||||
|
|
||||||
CONSTANT: CheckedValue = CheckedValue(32)
|
|
||||||
|
|
||||||
@exported
|
|
||||||
def testEntry() -> CheckedValue:
|
|
||||||
return CONSTANT
|
|
||||||
"""
|
|
||||||
|
|
||||||
result = Suite(code_py).run_code()
|
|
||||||
|
|
||||||
assert {"value": 32} == result.returned_value
|
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
|
||||||
def test_struct_export_instantiation():
|
|
||||||
code_py = """
|
|
||||||
class CheckedValue:
|
|
||||||
value: i32
|
|
||||||
|
|
||||||
@exported
|
|
||||||
def testEntry() -> CheckedValue:
|
|
||||||
return CheckedValue(32)
|
|
||||||
"""
|
|
||||||
|
|
||||||
result = Suite(code_py).run_code()
|
|
||||||
|
|
||||||
assert {"value": 32} == result.returned_value
|
|
||||||
|
|||||||
@ -23,23 +23,6 @@ def testEntry(f: {type_}) -> u8:
|
|||||||
|
|
||||||
assert exp_result == result.returned_value
|
assert exp_result == result.returned_value
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
|
||||||
@pytest.mark.parametrize('type_, in_put, exp_result', [
|
|
||||||
('(u8, u8, u8, )', (45, 46, 47), 47, ),
|
|
||||||
('u8[5]', (45, 46, 47, 48, 49), 47, ),
|
|
||||||
('bytes', b'This is a test', 105)
|
|
||||||
])
|
|
||||||
def test_subscript_2(type_, in_put, exp_result):
|
|
||||||
code_py = f"""
|
|
||||||
@exported
|
|
||||||
def testEntry(f: {type_}) -> u8:
|
|
||||||
return f[2]
|
|
||||||
"""
|
|
||||||
|
|
||||||
result = Suite(code_py).run_code(in_put)
|
|
||||||
|
|
||||||
assert exp_result == result.returned_value
|
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
@pytest.mark.integration_test
|
||||||
@pytest.mark.parametrize('type_, in_put, exp_result', [
|
@pytest.mark.parametrize('type_, in_put, exp_result', [
|
||||||
('(u8, u8, )', (45, 46), 45, ),
|
('(u8, u8, )', (45, 46), 45, ),
|
||||||
|
|||||||
@ -70,29 +70,3 @@ CONSTANT: (u32, u8, u8, ) = (24, 4000, 1, )
|
|||||||
|
|
||||||
with pytest.raises(Type3Exception, match=r'Must fit in 1 byte\(s\)'):
|
with pytest.raises(Type3Exception, match=r'Must fit in 1 byte\(s\)'):
|
||||||
Suite(code_py).run_code()
|
Suite(code_py).run_code()
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
|
||||||
def test_tuple_export_constant():
|
|
||||||
code_py = """
|
|
||||||
CONSTANT: (u32, u8, u8, ) = (4000, 20, 20, )
|
|
||||||
|
|
||||||
@exported
|
|
||||||
def testEntry() -> (u32, u8, u8, ):
|
|
||||||
return CONSTANT
|
|
||||||
"""
|
|
||||||
|
|
||||||
result = Suite(code_py).run_code()
|
|
||||||
|
|
||||||
assert (4000, 20, 20, ) == result.returned_value
|
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
|
||||||
def test_tuple_export_instantiation():
|
|
||||||
code_py = """
|
|
||||||
@exported
|
|
||||||
def testEntry() -> (u32, u8, u8, ):
|
|
||||||
return (4000, 20, 20, )
|
|
||||||
"""
|
|
||||||
|
|
||||||
result = Suite(code_py).run_code()
|
|
||||||
|
|
||||||
assert (4000, 20, 20, ) == result.returned_value
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user