Various cleanup to type system

- Make struct into a type constuctor
- Rework placeholders
- Got rid of 'PrimitiveType' as a concept
- Moved out the prelude to its own folder

Still in idea form [skip-ci]
This commit is contained in:
Johan B.W. de Vries 2025-04-21 12:32:47 +02:00
parent d6b483581b
commit 3d491b7a36
13 changed files with 756 additions and 667 deletions

View File

@ -5,9 +5,8 @@ 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 from . import ourlang, prelude
from .type3 import types as type3types from .type3 import placeholders as type3placeholders
from .type3.types import TYPE3_ASSERTION_ERROR, Type3, Type3OrPlaceholder
def phasm_render(inp: ourlang.Module) -> str: def phasm_render(inp: ourlang.Module) -> str:
@ -18,13 +17,14 @@ def phasm_render(inp: ourlang.Module) -> str:
Statements = Generator[str, None, None] Statements = Generator[str, None, None]
def type3(inp: Type3OrPlaceholder) -> str: def type3(inp: type3placeholders.Type3OrPlaceholder) -> str:
""" """
Render: type's name Render: type's name
""" """
assert isinstance(inp, Type3), TYPE3_ASSERTION_ERROR if isinstance(inp, type3placeholders.PlaceholderForType):
raise NotImplementedError
if inp is type3types.none: if inp is prelude.none:
return 'None' return 'None'
return inp.name return inp.name
@ -33,8 +33,11 @@ def struct_definition(inp: ourlang.StructDefinition) -> str:
""" """
Render: TypeStruct's definition Render: TypeStruct's definition
""" """
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.members.items(): for mem, typ in st_args.items():
result += f' {mem}: {type3(typ)}\n' result += f' {mem}: {type3(typ)}\n'
return result return result

View File

@ -4,10 +4,11 @@ This module contains the code to convert parsed Ourlang into WebAssembly code
import struct import struct
from typing import Dict, List, Optional from typing import Dict, List, Optional
from . import codestyle, ourlang, wasm from . import codestyle, ourlang, prelude, wasm
from .runtime import calculate_alloc_size, calculate_member_offset from .runtime import calculate_alloc_size, calculate_member_offset
from .stdlib import alloc as stdlib_alloc from .stdlib import alloc as stdlib_alloc
from .stdlib import types as stdlib_types from .stdlib import types as stdlib_types
from .type3 import placeholders as type3placeholders
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 .wasmgenerator import Generator as WasmGenerator from .wasmgenerator import Generator as WasmGenerator
@ -29,7 +30,7 @@ LOAD_STORE_TYPE_MAP = {
# For now this is nice & clean, but this will get messy quick # For now this is nice & clean, but this will get messy quick
# Especially once we get functions with polymorphying applied types # Especially once we get functions with polymorphying applied types
INSTANCES = { INSTANCES = {
type3classes.Eq.operators['==']: { prelude.Eq.operators['==']: {
'a=u8': stdlib_types.u8_eq_equals, 'a=u8': stdlib_types.u8_eq_equals,
'a=u32': stdlib_types.u32_eq_equals, 'a=u32': stdlib_types.u32_eq_equals,
'a=u64': stdlib_types.u64_eq_equals, 'a=u64': stdlib_types.u64_eq_equals,
@ -39,7 +40,7 @@ INSTANCES = {
'a=f32': stdlib_types.f32_eq_equals, 'a=f32': stdlib_types.f32_eq_equals,
'a=f64': stdlib_types.f64_eq_equals, 'a=f64': stdlib_types.f64_eq_equals,
}, },
type3classes.Eq.operators['!=']: { prelude.Eq.operators['!=']: {
'a=u8': stdlib_types.u8_eq_not_equals, 'a=u8': stdlib_types.u8_eq_not_equals,
'a=u32': stdlib_types.u32_eq_not_equals, 'a=u32': stdlib_types.u32_eq_not_equals,
'a=u64': stdlib_types.u64_eq_not_equals, 'a=u64': stdlib_types.u64_eq_not_equals,
@ -49,7 +50,7 @@ INSTANCES = {
'a=f32': stdlib_types.f32_eq_not_equals, 'a=f32': stdlib_types.f32_eq_not_equals,
'a=f64': stdlib_types.f64_eq_not_equals, 'a=f64': stdlib_types.f64_eq_not_equals,
}, },
type3classes.Ord.methods['min']: { prelude.Ord.methods['min']: {
'a=u8': stdlib_types.u8_ord_min, 'a=u8': stdlib_types.u8_ord_min,
'a=u32': stdlib_types.u32_ord_min, 'a=u32': stdlib_types.u32_ord_min,
'a=u64': stdlib_types.u64_ord_min, 'a=u64': stdlib_types.u64_ord_min,
@ -59,7 +60,7 @@ INSTANCES = {
'a=f32': stdlib_types.f32_ord_min, 'a=f32': stdlib_types.f32_ord_min,
'a=f64': stdlib_types.f64_ord_min, 'a=f64': stdlib_types.f64_ord_min,
}, },
type3classes.Ord.methods['max']: { prelude.Ord.methods['max']: {
'a=u8': stdlib_types.u8_ord_max, 'a=u8': stdlib_types.u8_ord_max,
'a=u32': stdlib_types.u32_ord_max, 'a=u32': stdlib_types.u32_ord_max,
'a=u64': stdlib_types.u64_ord_max, 'a=u64': stdlib_types.u64_ord_max,
@ -69,7 +70,7 @@ INSTANCES = {
'a=f32': stdlib_types.f32_ord_max, 'a=f32': stdlib_types.f32_ord_max,
'a=f64': stdlib_types.f64_ord_max, 'a=f64': stdlib_types.f64_ord_max,
}, },
type3classes.Ord.operators['<']: { prelude.Ord.operators['<']: {
'a=u8': stdlib_types.u8_ord_less_than, 'a=u8': stdlib_types.u8_ord_less_than,
'a=u32': stdlib_types.u32_ord_less_than, 'a=u32': stdlib_types.u32_ord_less_than,
'a=u64': stdlib_types.u64_ord_less_than, 'a=u64': stdlib_types.u64_ord_less_than,
@ -79,7 +80,7 @@ INSTANCES = {
'a=f32': stdlib_types.f32_ord_less_than, 'a=f32': stdlib_types.f32_ord_less_than,
'a=f64': stdlib_types.f64_ord_less_than, 'a=f64': stdlib_types.f64_ord_less_than,
}, },
type3classes.Ord.operators['<=']: { prelude.Ord.operators['<=']: {
'a=u8': stdlib_types.u8_ord_less_than_or_equal, 'a=u8': stdlib_types.u8_ord_less_than_or_equal,
'a=u32': stdlib_types.u32_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=u64': stdlib_types.u64_ord_less_than_or_equal,
@ -89,7 +90,7 @@ INSTANCES = {
'a=f32': stdlib_types.f32_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, 'a=f64': stdlib_types.f64_ord_less_than_or_equal,
}, },
type3classes.Ord.operators['>']: { prelude.Ord.operators['>']: {
'a=u8': stdlib_types.u8_ord_greater_than, 'a=u8': stdlib_types.u8_ord_greater_than,
'a=u32': stdlib_types.u32_ord_greater_than, 'a=u32': stdlib_types.u32_ord_greater_than,
'a=u64': stdlib_types.u64_ord_greater_than, 'a=u64': stdlib_types.u64_ord_greater_than,
@ -99,7 +100,7 @@ INSTANCES = {
'a=f32': stdlib_types.f32_ord_greater_than, 'a=f32': stdlib_types.f32_ord_greater_than,
'a=f64': stdlib_types.f64_ord_greater_than, 'a=f64': stdlib_types.f64_ord_greater_than,
}, },
type3classes.Ord.operators['>=']: { prelude.Ord.operators['>=']: {
'a=u8': stdlib_types.u8_ord_greater_than_or_equal, 'a=u8': stdlib_types.u8_ord_greater_than_or_equal,
'a=u32': stdlib_types.u32_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=u64': stdlib_types.u64_ord_greater_than_or_equal,
@ -109,90 +110,90 @@ INSTANCES = {
'a=f32': stdlib_types.f32_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, 'a=f64': stdlib_types.f64_ord_greater_than_or_equal,
}, },
type3classes.Bits.methods['shl']: { prelude.Bits.methods['shl']: {
'a=u8': stdlib_types.u8_bits_logical_shift_left, 'a=u8': stdlib_types.u8_bits_logical_shift_left,
'a=u32': stdlib_types.u32_bits_logical_shift_left, 'a=u32': stdlib_types.u32_bits_logical_shift_left,
'a=u64': stdlib_types.u64_bits_logical_shift_left, 'a=u64': stdlib_types.u64_bits_logical_shift_left,
}, },
type3classes.Bits.methods['shr']: { prelude.Bits.methods['shr']: {
'a=u8': stdlib_types.u8_bits_logical_shift_right, 'a=u8': stdlib_types.u8_bits_logical_shift_right,
'a=u32': stdlib_types.u32_bits_logical_shift_right, 'a=u32': stdlib_types.u32_bits_logical_shift_right,
'a=u64': stdlib_types.u64_bits_logical_shift_right, 'a=u64': stdlib_types.u64_bits_logical_shift_right,
}, },
type3classes.Bits.methods['rotl']: { prelude.Bits.methods['rotl']: {
'a=u8': stdlib_types.u8_bits_rotate_left, 'a=u8': stdlib_types.u8_bits_rotate_left,
'a=u32': stdlib_types.u32_bits_rotate_left, 'a=u32': stdlib_types.u32_bits_rotate_left,
'a=u64': stdlib_types.u64_bits_rotate_left, 'a=u64': stdlib_types.u64_bits_rotate_left,
}, },
type3classes.Bits.methods['rotr']: { prelude.Bits.methods['rotr']: {
'a=u8': stdlib_types.u8_bits_rotate_right, 'a=u8': stdlib_types.u8_bits_rotate_right,
'a=u32': stdlib_types.u32_bits_rotate_right, 'a=u32': stdlib_types.u32_bits_rotate_right,
'a=u64': stdlib_types.u64_bits_rotate_right, 'a=u64': stdlib_types.u64_bits_rotate_right,
}, },
type3classes.Bits.operators['&']: { prelude.Bits.operators['&']: {
'a=u8': stdlib_types.u8_bits_bitwise_and, 'a=u8': stdlib_types.u8_bits_bitwise_and,
'a=u32': stdlib_types.u32_bits_bitwise_and, 'a=u32': stdlib_types.u32_bits_bitwise_and,
'a=u64': stdlib_types.u64_bits_bitwise_and, 'a=u64': stdlib_types.u64_bits_bitwise_and,
}, },
type3classes.Bits.operators['|']: { prelude.Bits.operators['|']: {
'a=u8': stdlib_types.u8_bits_bitwise_or, 'a=u8': stdlib_types.u8_bits_bitwise_or,
'a=u32': stdlib_types.u32_bits_bitwise_or, 'a=u32': stdlib_types.u32_bits_bitwise_or,
'a=u64': stdlib_types.u64_bits_bitwise_or, 'a=u64': stdlib_types.u64_bits_bitwise_or,
}, },
type3classes.Bits.operators['^']: { prelude.Bits.operators['^']: {
'a=u8': stdlib_types.u8_bits_bitwise_xor, 'a=u8': stdlib_types.u8_bits_bitwise_xor,
'a=u32': stdlib_types.u32_bits_bitwise_xor, 'a=u32': stdlib_types.u32_bits_bitwise_xor,
'a=u64': stdlib_types.u64_bits_bitwise_xor, 'a=u64': stdlib_types.u64_bits_bitwise_xor,
}, },
type3classes.Floating.methods['sqrt']: { prelude.Floating.methods['sqrt']: {
'a=f32': stdlib_types.f32_floating_sqrt, 'a=f32': stdlib_types.f32_floating_sqrt,
'a=f64': stdlib_types.f64_floating_sqrt, 'a=f64': stdlib_types.f64_floating_sqrt,
}, },
type3classes.Fractional.methods['ceil']: { prelude.Fractional.methods['ceil']: {
'a=f32': stdlib_types.f32_fractional_ceil, 'a=f32': stdlib_types.f32_fractional_ceil,
'a=f64': stdlib_types.f64_fractional_ceil, 'a=f64': stdlib_types.f64_fractional_ceil,
}, },
type3classes.Fractional.methods['floor']: { prelude.Fractional.methods['floor']: {
'a=f32': stdlib_types.f32_fractional_floor, 'a=f32': stdlib_types.f32_fractional_floor,
'a=f64': stdlib_types.f64_fractional_floor, 'a=f64': stdlib_types.f64_fractional_floor,
}, },
type3classes.Fractional.methods['trunc']: { prelude.Fractional.methods['trunc']: {
'a=f32': stdlib_types.f32_fractional_trunc, 'a=f32': stdlib_types.f32_fractional_trunc,
'a=f64': stdlib_types.f64_fractional_trunc, 'a=f64': stdlib_types.f64_fractional_trunc,
}, },
type3classes.Fractional.methods['nearest']: { prelude.Fractional.methods['nearest']: {
'a=f32': stdlib_types.f32_fractional_nearest, 'a=f32': stdlib_types.f32_fractional_nearest,
'a=f64': stdlib_types.f64_fractional_nearest, 'a=f64': stdlib_types.f64_fractional_nearest,
}, },
type3classes.Fractional.operators['/']: { prelude.Fractional.operators['/']: {
'a=f32': stdlib_types.f32_fractional_div, 'a=f32': stdlib_types.f32_fractional_div,
'a=f64': stdlib_types.f64_fractional_div, 'a=f64': stdlib_types.f64_fractional_div,
}, },
type3classes.Integral.operators['//']: { prelude.Integral.operators['//']: {
'a=u32': stdlib_types.u32_integral_div, 'a=u32': stdlib_types.u32_integral_div,
'a=u64': stdlib_types.u64_integral_div, 'a=u64': stdlib_types.u64_integral_div,
'a=i32': stdlib_types.i32_integral_div, 'a=i32': stdlib_types.i32_integral_div,
'a=i64': stdlib_types.i64_integral_div, 'a=i64': stdlib_types.i64_integral_div,
}, },
type3classes.Integral.operators['%']: { prelude.Integral.operators['%']: {
'a=u32': stdlib_types.u32_integral_rem, 'a=u32': stdlib_types.u32_integral_rem,
'a=u64': stdlib_types.u64_integral_rem, 'a=u64': stdlib_types.u64_integral_rem,
'a=i32': stdlib_types.i32_integral_rem, 'a=i32': stdlib_types.i32_integral_rem,
'a=i64': stdlib_types.i64_integral_rem, 'a=i64': stdlib_types.i64_integral_rem,
}, },
type3classes.IntNum.methods['abs']: { prelude.IntNum.methods['abs']: {
'a=i32': stdlib_types.i32_intnum_abs, 'a=i32': stdlib_types.i32_intnum_abs,
'a=i64': stdlib_types.i64_intnum_abs, 'a=i64': stdlib_types.i64_intnum_abs,
'a=f32': stdlib_types.f32_intnum_abs, 'a=f32': stdlib_types.f32_intnum_abs,
'a=f64': stdlib_types.f64_intnum_abs, 'a=f64': stdlib_types.f64_intnum_abs,
}, },
type3classes.IntNum.methods['neg']: { prelude.IntNum.methods['neg']: {
'a=i32': stdlib_types.i32_intnum_neg, 'a=i32': stdlib_types.i32_intnum_neg,
'a=i64': stdlib_types.i64_intnum_neg, 'a=i64': stdlib_types.i64_intnum_neg,
'a=f32': stdlib_types.f32_intnum_neg, 'a=f32': stdlib_types.f32_intnum_neg,
'a=f64': stdlib_types.f64_intnum_neg, 'a=f64': stdlib_types.f64_intnum_neg,
}, },
type3classes.NatNum.operators['+']: { prelude.NatNum.operators['+']: {
'a=u32': stdlib_types.u32_natnum_add, 'a=u32': stdlib_types.u32_natnum_add,
'a=u64': stdlib_types.u64_natnum_add, 'a=u64': stdlib_types.u64_natnum_add,
'a=i32': stdlib_types.i32_natnum_add, 'a=i32': stdlib_types.i32_natnum_add,
@ -200,7 +201,7 @@ INSTANCES = {
'a=f32': stdlib_types.f32_natnum_add, 'a=f32': stdlib_types.f32_natnum_add,
'a=f64': stdlib_types.f64_natnum_add, 'a=f64': stdlib_types.f64_natnum_add,
}, },
type3classes.NatNum.operators['-']: { prelude.NatNum.operators['-']: {
'a=u32': stdlib_types.u32_natnum_sub, 'a=u32': stdlib_types.u32_natnum_sub,
'a=u64': stdlib_types.u64_natnum_sub, 'a=u64': stdlib_types.u64_natnum_sub,
'a=i32': stdlib_types.i32_natnum_sub, 'a=i32': stdlib_types.i32_natnum_sub,
@ -208,7 +209,7 @@ INSTANCES = {
'a=f32': stdlib_types.f32_natnum_sub, 'a=f32': stdlib_types.f32_natnum_sub,
'a=f64': stdlib_types.f64_natnum_sub, 'a=f64': stdlib_types.f64_natnum_sub,
}, },
type3classes.NatNum.operators['*']: { prelude.NatNum.operators['*']: {
'a=u32': stdlib_types.u32_natnum_mul, 'a=u32': stdlib_types.u32_natnum_mul,
'a=u64': stdlib_types.u64_natnum_mul, 'a=u64': stdlib_types.u64_natnum_mul,
'a=i32': stdlib_types.i32_natnum_mul, 'a=i32': stdlib_types.i32_natnum_mul,
@ -216,7 +217,7 @@ INSTANCES = {
'a=f32': stdlib_types.f32_natnum_mul, 'a=f32': stdlib_types.f32_natnum_mul,
'a=f64': stdlib_types.f64_natnum_mul, 'a=f64': stdlib_types.f64_natnum_mul,
}, },
type3classes.NatNum.operators['<<']: { prelude.NatNum.operators['<<']: {
'a=u32': stdlib_types.u32_natnum_arithmic_shift_left, 'a=u32': stdlib_types.u32_natnum_arithmic_shift_left,
'a=u64': stdlib_types.u64_natnum_arithmic_shift_left, 'a=u64': stdlib_types.u64_natnum_arithmic_shift_left,
'a=i32': stdlib_types.i32_natnum_arithmic_shift_left, 'a=i32': stdlib_types.i32_natnum_arithmic_shift_left,
@ -224,7 +225,7 @@ INSTANCES = {
'a=f32': stdlib_types.f32_natnum_arithmic_shift_left, 'a=f32': stdlib_types.f32_natnum_arithmic_shift_left,
'a=f64': stdlib_types.f64_natnum_arithmic_shift_left, 'a=f64': stdlib_types.f64_natnum_arithmic_shift_left,
}, },
type3classes.NatNum.operators['>>']: { prelude.NatNum.operators['>>']: {
'a=u32': stdlib_types.u32_natnum_arithmic_shift_right, 'a=u32': stdlib_types.u32_natnum_arithmic_shift_right,
'a=u64': stdlib_types.u64_natnum_arithmic_shift_right, 'a=u64': stdlib_types.u64_natnum_arithmic_shift_right,
'a=i32': stdlib_types.i32_natnum_arithmic_shift_right, 'a=i32': stdlib_types.i32_natnum_arithmic_shift_right,
@ -241,61 +242,57 @@ def phasm_compile(inp: ourlang.Module) -> wasm.Module:
""" """
return module(inp) return module(inp)
def type3(inp: type3types.Type3OrPlaceholder) -> wasm.WasmType: def type3(inp: type3placeholders.Type3OrPlaceholder) -> wasm.WasmType:
""" """
Compile: type Compile: type
Types are used for example in WebAssembly function parameters Types are used for example in WebAssembly function parameters
and return types. and return types.
""" """
assert isinstance(inp, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR assert isinstance(inp, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
if inp == type3types.none: if inp == prelude.none:
return wasm.WasmTypeNone() return wasm.WasmTypeNone()
if inp == type3types.bool_: if inp == prelude.bool_:
# WebAssembly stores booleans as i32 # WebAssembly stores booleans as i32
# See e.g. f32.eq, which is [f32 f32] -> [i32] # See e.g. f32.eq, which is [f32 f32] -> [i32]
return wasm.WasmTypeInt32() return wasm.WasmTypeInt32()
if inp == type3types.u8: if inp == prelude.u8:
# WebAssembly has only support for 32 and 64 bits # WebAssembly has only support for 32 and 64 bits
# So we need to store more memory per byte # So we need to store more memory per byte
return wasm.WasmTypeInt32() return wasm.WasmTypeInt32()
if inp == type3types.u32: if inp == prelude.u32:
return wasm.WasmTypeInt32() return wasm.WasmTypeInt32()
if inp == type3types.u64: if inp == prelude.u64:
return wasm.WasmTypeInt64() return wasm.WasmTypeInt64()
if inp == type3types.i8: if inp == prelude.i8:
# WebAssembly has only support for 32 and 64 bits # WebAssembly has only support for 32 and 64 bits
# So we need to store more memory per byte # So we need to store more memory per byte
return wasm.WasmTypeInt32() return wasm.WasmTypeInt32()
if inp == type3types.i32: if inp == prelude.i32:
return wasm.WasmTypeInt32() return wasm.WasmTypeInt32()
if inp == type3types.i64: if inp == prelude.i64:
return wasm.WasmTypeInt64() return wasm.WasmTypeInt64()
if inp == type3types.f32: if inp == prelude.f32:
return wasm.WasmTypeFloat32() return wasm.WasmTypeFloat32()
if inp == type3types.f64: if inp == prelude.f64:
return wasm.WasmTypeFloat64() return wasm.WasmTypeFloat64()
if inp == type3types.bytes_: if inp == prelude.bytes_:
# bytes are passed as pointer # bytes are passed as pointer
# And pointers are i32 # And pointers are i32
return wasm.WasmTypeInt32() return wasm.WasmTypeInt32()
if isinstance(inp, type3types.StructType3): if prelude.InternalPassAsPointer in inp.classes:
# Structs are passed as pointer, which are i32
return wasm.WasmTypeInt32()
if type3classes.InternalPassAsPointer in inp.classes:
return wasm.WasmTypeInt32() return wasm.WasmTypeInt32()
raise NotImplementedError(type3, inp) raise NotImplementedError(type3, inp)
@ -304,17 +301,17 @@ def tuple_instantiation(wgn: WasmGenerator, inp: ourlang.TupleInstantiation) ->
""" """
Compile: Instantiation (allocation) of a tuple Compile: Instantiation (allocation) of a tuple
""" """
assert isinstance(inp.type3, type3types.PrimitiveType3) assert isinstance(inp.type3, type3types.Type3)
args: list[type3types.PrimitiveType3] = [] args: list[type3types.Type3] = []
sa_args = type3types.static_array.did_construct(inp.type3) sa_args = prelude.static_array.did_construct(inp.type3)
if sa_args is not None: if sa_args is not None:
sa_type, sa_len = sa_args sa_type, sa_len = sa_args
args = [sa_type for _ in range(sa_len.value)] args = [sa_type for _ in range(sa_len.value)]
if not args: if not args:
tp_args = type3types.tuple_.did_construct(inp.type3) tp_args = prelude.tuple_.did_construct(inp.type3)
if tp_args is None: if tp_args is None:
raise NotImplementedError raise NotImplementedError
@ -322,7 +319,7 @@ def tuple_instantiation(wgn: WasmGenerator, inp: ourlang.TupleInstantiation) ->
comment_elements = '' comment_elements = ''
for element in inp.elements: for element in inp.elements:
assert isinstance(element.type3, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR assert isinstance(element.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
comment_elements += f'{element.type3.name}, ' comment_elements += f'{element.type3.name}, '
tmp_var = wgn.temp_var_i32('tuple_adr') tmp_var = wgn.temp_var_i32('tuple_adr')
@ -336,19 +333,17 @@ def tuple_instantiation(wgn: WasmGenerator, inp: ourlang.TupleInstantiation) ->
# Store each element individually # Store each element individually
offset = 0 offset = 0
for element, exp_type3 in zip(inp.elements, args): for element, exp_type3 in zip(inp.elements, args):
if isinstance(exp_type3, type3types.PlaceholderForType): if isinstance(exp_type3, type3placeholders.PlaceholderForType):
assert exp_type3.resolve_as is not None assert exp_type3.resolve_as is not None
assert isinstance(exp_type3.resolve_as, type3types.PrimitiveType3) assert isinstance(exp_type3.resolve_as, type3types.Type3)
exp_type3 = exp_type3.resolve_as exp_type3 = exp_type3.resolve_as
assert element.type3 == exp_type3 assert element.type3 == exp_type3
if type3classes.InternalPassAsPointer in exp_type3.classes: if prelude.InternalPassAsPointer in exp_type3.classes:
mtyp = 'i32'
elif isinstance(exp_type3, type3types.StructType3):
mtyp = 'i32' mtyp = 'i32'
else: else:
assert isinstance(exp_type3, type3types.PrimitiveType3), NotImplementedError('Tuple of applied types / structs') assert isinstance(exp_type3, type3types.Type3), NotImplementedError('Tuple of applied types / structs')
mtyp = LOAD_STORE_TYPE_MAP[exp_type3.name] mtyp = LOAD_STORE_TYPE_MAP[exp_type3.name]
wgn.add_statement('nop', comment='PRE') wgn.add_statement('nop', comment='PRE')
@ -371,30 +366,30 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
raise Exception raise Exception
if isinstance(inp, ourlang.ConstantPrimitive): if isinstance(inp, ourlang.ConstantPrimitive):
assert isinstance(inp.type3, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR assert isinstance(inp.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
if inp.type3 in (type3types.i8, type3types.u8, ): if inp.type3 in (prelude.i8, prelude.u8, ):
# No native u8 type - treat as i32, with caution # No native u8 type - treat as i32, with caution
assert isinstance(inp.value, int) assert isinstance(inp.value, int)
wgn.i32.const(inp.value) wgn.i32.const(inp.value)
return return
if inp.type3 in (type3types.i32, type3types.u32, ): if inp.type3 in (prelude.i32, prelude.u32, ):
assert isinstance(inp.value, int) assert isinstance(inp.value, int)
wgn.i32.const(inp.value) wgn.i32.const(inp.value)
return return
if inp.type3 in (type3types.i64, type3types.u64, ): if inp.type3 in (prelude.i64, prelude.u64, ):
assert isinstance(inp.value, int) assert isinstance(inp.value, int)
wgn.i64.const(inp.value) wgn.i64.const(inp.value)
return return
if inp.type3 == type3types.f32: if inp.type3 == prelude.f32:
assert isinstance(inp.value, float) assert isinstance(inp.value, float)
wgn.f32.const(inp.value) wgn.f32.const(inp.value)
return return
if inp.type3 == type3types.f64: if inp.type3 == prelude.f64:
assert isinstance(inp.value, float) assert isinstance(inp.value, float)
wgn.f64.const(inp.value) wgn.f64.const(inp.value)
return return
@ -412,34 +407,18 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
return return
if isinstance(inp.variable, ourlang.ModuleConstantDef): if isinstance(inp.variable, ourlang.ModuleConstantDef):
assert isinstance(inp.type3, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR assert isinstance(inp.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
if type3classes.InternalPassAsPointer in inp.type3.classes: if prelude.InternalPassAsPointer in inp.type3.classes:
# FIXME: Artifact from older version # FIXME: Artifact from older version
assert isinstance(inp.variable.constant, ourlang.ConstantTuple) assert isinstance(inp.variable.constant, (ourlang.ConstantTuple, ourlang.ConstantStruct, ))
address = inp.variable.constant.data_block.address address = inp.variable.constant.data_block.address
assert address is not None, 'Value not allocated' assert address is not None, 'Value not allocated'
wgn.i32.const(address) wgn.i32.const(address)
return return
if inp.type3 is type3types.bytes_: if isinstance(inp.type3, type3types.Type3):
assert isinstance(inp.variable.constant, ourlang.ConstantBytes)
address = inp.variable.constant.data_block.address
assert address is not None, 'Value not allocated'
wgn.i32.const(address)
return
if isinstance(inp.type3, type3types.StructType3):
assert isinstance(inp.variable.constant, ourlang.ConstantStruct)
address = inp.variable.constant.data_block.address
assert address is not None, 'Value not allocated'
wgn.i32.const(address)
return
if isinstance(inp.type3, type3types.PrimitiveType3):
expression(wgn, inp.variable.constant) expression(wgn, inp.variable.constant)
return return
@ -451,7 +430,7 @@ 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 isinstance(inp.type3, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR assert isinstance(inp.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
type_var_map: Dict[type3classes.TypeVariable, type3types.Type3] = {} type_var_map: Dict[type3classes.TypeVariable, type3types.Type3] = {}
@ -460,7 +439,7 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
# Fixed type, not part of the lookup requirements # Fixed type, not part of the lookup requirements
continue continue
assert isinstance(arg_expr.type3, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR assert isinstance(arg_expr.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
type_var_map[type_var] = arg_expr.type3 type_var_map[type_var] = arg_expr.type3
instance_key = ','.join( instance_key = ','.join(
@ -478,16 +457,16 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
if isinstance(inp, ourlang.UnaryOp): if isinstance(inp, ourlang.UnaryOp):
expression(wgn, inp.right) expression(wgn, inp.right)
assert isinstance(inp.type3, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR assert isinstance(inp.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
if inp.type3 == type3types.u32: if inp.type3 == prelude.u32:
if inp.operator == 'len': if inp.operator == 'len':
if inp.right.type3 == type3types.bytes_: if inp.right.type3 == prelude.bytes_:
wgn.i32.load() wgn.i32.load()
return return
if inp.operator == 'cast': if inp.operator == 'cast':
if inp.type3 == type3types.u32 and inp.right.type3 == type3types.u8: if inp.type3 == prelude.u32 and inp.right.type3 == prelude.u8:
# Nothing to do, you can use an u8 value as a u32 no problem # Nothing to do, you can use an u8 value as a u32 no problem
return return
@ -506,7 +485,7 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
# Fixed type, not part of the lookup requirements # Fixed type, not part of the lookup requirements
continue continue
assert isinstance(arg_expr.type3, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR assert isinstance(arg_expr.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
type_var_map[type_var] = arg_expr.type3 type_var_map[type_var] = arg_expr.type3
instance_key = ','.join( instance_key = ','.join(
@ -529,17 +508,17 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
return return
if isinstance(inp, ourlang.Subscript): if isinstance(inp, ourlang.Subscript):
assert isinstance(inp.varref.type3, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR assert isinstance(inp.varref.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
if inp.varref.type3 is type3types.bytes_: if inp.varref.type3 is prelude.bytes_:
expression(wgn, inp.varref) expression(wgn, inp.varref)
expression(wgn, inp.index) expression(wgn, inp.index)
wgn.call(stdlib_types.__subscript_bytes__) wgn.call(stdlib_types.__subscript_bytes__)
return return
assert isinstance(inp.varref.type3, type3types.PrimitiveType3) assert isinstance(inp.varref.type3, type3types.Type3)
sa_args = type3types.static_array.did_construct(inp.varref.type3) sa_args = prelude.static_array.did_construct(inp.varref.type3)
if sa_args is not None: if sa_args is not None:
el_type, el_len = sa_args el_type, el_len = sa_args
@ -563,33 +542,31 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
wgn.i32.mul() wgn.i32.mul()
wgn.i32.add() wgn.i32.add()
assert isinstance(el_type, type3types.PrimitiveType3), NotImplementedError('Tuple of applied types / structs') assert isinstance(el_type, type3types.Type3), NotImplementedError('Tuple of applied types / structs')
mtyp = LOAD_STORE_TYPE_MAP[el_type.name] mtyp = LOAD_STORE_TYPE_MAP[el_type.name]
wgn.add_statement(f'{mtyp}.load') wgn.add_statement(f'{mtyp}.load')
return return
tp_args = type3types.tuple_.did_construct(inp.varref.type3) tp_args = prelude.tuple_.did_construct(inp.varref.type3)
if tp_args is not None: if tp_args is not None:
assert isinstance(inp.index, ourlang.ConstantPrimitive) assert isinstance(inp.index, ourlang.ConstantPrimitive)
assert isinstance(inp.index.value, int) assert isinstance(inp.index.value, int)
offset = 0 offset = 0
for el_type in tp_args[0:inp.index.value]: for el_type in tp_args[0:inp.index.value]:
assert isinstance(el_type, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR assert isinstance(el_type, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
offset += calculate_alloc_size(el_type) offset += calculate_alloc_size(el_type)
el_type = tp_args[inp.index.value] el_type = tp_args[inp.index.value]
assert isinstance(el_type, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR assert isinstance(el_type, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
expression(wgn, inp.varref) expression(wgn, inp.varref)
if isinstance(el_type, type3types.StructType3): if prelude.InternalPassAsPointer in el_type.classes:
mtyp = 'i32'
elif type3classes.InternalPassAsPointer in el_type.classes:
mtyp = 'i32' mtyp = 'i32'
else: else:
assert isinstance(el_type, type3types.PrimitiveType3), NotImplementedError('Tuple of applied types / structs') assert isinstance(el_type, type3types.Type3), NotImplementedError('Tuple of applied types / structs')
mtyp = LOAD_STORE_TYPE_MAP[el_type.name] mtyp = LOAD_STORE_TYPE_MAP[el_type.name]
wgn.add_statement(f'{mtyp}.load', f'offset={offset}') wgn.add_statement(f'{mtyp}.load', f'offset={offset}')
@ -598,12 +575,19 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
raise NotImplementedError(expression, inp, inp.varref.type3) raise NotImplementedError(expression, inp, inp.varref.type3)
if isinstance(inp, ourlang.AccessStructMember): if isinstance(inp, ourlang.AccessStructMember):
assert isinstance(inp.struct_type3.members[inp.member], type3types.PrimitiveType3), NotImplementedError('Tuple of applied types / structs') assert isinstance(inp.struct_type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
mtyp = LOAD_STORE_TYPE_MAP[inp.struct_type3.members[inp.member].name]
st_args = prelude.struct.did_construct(inp.struct_type3)
assert st_args is not None
member_type = st_args[inp.member]
assert isinstance(member_type, type3types.Type3), NotImplementedError('Tuple of applied types / structs')
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, inp.member inp.struct_type3.name, st_args, inp.member
))) )))
return return
@ -617,9 +601,9 @@ def expression_fold(wgn: WasmGenerator, inp: ourlang.Fold) -> None:
""" """
Compile: Fold expression Compile: Fold expression
""" """
assert isinstance(inp.type3, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR assert isinstance(inp.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
if inp.iter.type3 is not type3types.bytes_: if inp.iter.type3 is not prelude.bytes_:
raise NotImplementedError(expression_fold, inp, inp.iter.type3) raise NotImplementedError(expression_fold, inp, inp.iter.type3)
wgn.add_statement('nop', comment='acu :: u8') wgn.add_statement('nop', comment='acu :: u8')
@ -848,7 +832,7 @@ def module_data(inp: ourlang.ModuleData) -> bytes:
data_list: List[bytes] = [] data_list: List[bytes] = []
for constant in block.data: for constant in block.data:
assert isinstance(constant.type3, type3types.Type3), (id(constant), type3types.TYPE3_ASSERTION_ERROR) assert isinstance(constant.type3, type3types.Type3), (id(constant), type3placeholders.TYPE3_ASSERTION_ERROR)
if isinstance(constant, ourlang.ConstantMemoryStored) and block is not constant.data_block: if isinstance(constant, ourlang.ConstantMemoryStored) and block is not constant.data_block:
# It's stored in a different block # It's stored in a different block
@ -859,55 +843,55 @@ def module_data(inp: ourlang.ModuleData) -> bytes:
data_list.append(module_data_u32(constant.data_block.address)) data_list.append(module_data_u32(constant.data_block.address))
continue continue
if constant.type3 == type3types.u8: if constant.type3 == prelude.u8:
assert isinstance(constant, ourlang.ConstantPrimitive) assert isinstance(constant, ourlang.ConstantPrimitive)
assert isinstance(constant.value, int) assert isinstance(constant.value, int)
data_list.append(module_data_u8(constant.value)) data_list.append(module_data_u8(constant.value))
continue continue
if constant.type3 == type3types.u32: if constant.type3 == prelude.u32:
assert isinstance(constant, ourlang.ConstantPrimitive) assert isinstance(constant, ourlang.ConstantPrimitive)
assert isinstance(constant.value, int) assert isinstance(constant.value, int)
data_list.append(module_data_u32(constant.value)) data_list.append(module_data_u32(constant.value))
continue continue
if constant.type3 == type3types.u64: if constant.type3 == prelude.u64:
assert isinstance(constant, ourlang.ConstantPrimitive) assert isinstance(constant, ourlang.ConstantPrimitive)
assert isinstance(constant.value, int) assert isinstance(constant.value, int)
data_list.append(module_data_u64(constant.value)) data_list.append(module_data_u64(constant.value))
continue continue
if constant.type3 == type3types.i8: if constant.type3 == prelude.i8:
assert isinstance(constant, ourlang.ConstantPrimitive) assert isinstance(constant, ourlang.ConstantPrimitive)
assert isinstance(constant.value, int) assert isinstance(constant.value, int)
data_list.append(module_data_i8(constant.value)) data_list.append(module_data_i8(constant.value))
continue continue
if constant.type3 == type3types.i32: if constant.type3 == prelude.i32:
assert isinstance(constant, ourlang.ConstantPrimitive) assert isinstance(constant, ourlang.ConstantPrimitive)
assert isinstance(constant.value, int) assert isinstance(constant.value, int)
data_list.append(module_data_i32(constant.value)) data_list.append(module_data_i32(constant.value))
continue continue
if constant.type3 == type3types.i64: if constant.type3 == prelude.i64:
assert isinstance(constant, ourlang.ConstantPrimitive) assert isinstance(constant, ourlang.ConstantPrimitive)
assert isinstance(constant.value, int) assert isinstance(constant.value, int)
data_list.append(module_data_i64(constant.value)) data_list.append(module_data_i64(constant.value))
continue continue
if constant.type3 == type3types.f32: if constant.type3 == prelude.f32:
assert isinstance(constant, ourlang.ConstantPrimitive) assert isinstance(constant, ourlang.ConstantPrimitive)
assert isinstance(constant.value, float) assert isinstance(constant.value, float)
data_list.append(module_data_f32(constant.value)) data_list.append(module_data_f32(constant.value))
continue continue
if constant.type3 == type3types.f64: if constant.type3 == prelude.f64:
assert isinstance(constant, ourlang.ConstantPrimitive) assert isinstance(constant, ourlang.ConstantPrimitive)
assert isinstance(constant.value, float) assert isinstance(constant.value, float)
data_list.append(module_data_f64(constant.value)) data_list.append(module_data_f64(constant.value))
continue continue
if constant.type3 == type3types.bytes_: if constant.type3 == prelude.bytes_:
assert isinstance(constant, ourlang.ConstantBytes) assert isinstance(constant, ourlang.ConstantBytes)
assert isinstance(constant.value, bytes) assert isinstance(constant.value, bytes)
data_list.append(module_data_u32(len(constant.value))) data_list.append(module_data_u32(len(constant.value)))
@ -978,6 +962,9 @@ 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:
st_args = prelude.struct.did_construct(inp.struct_type3)
assert st_args is not None
tmp_var = wgn.temp_var_i32('struct_adr') tmp_var = wgn.temp_var_i32('struct_adr')
# Allocated the required amounts of bytes in memory # Allocated the required amounts of bytes in memory
@ -986,11 +973,9 @@ 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 inp.struct_type3.members.items(): for memname, mtyp3 in st_args.items():
mtyp: Optional[str] mtyp: Optional[str]
if type3classes.InternalPassAsPointer in mtyp3.classes: if prelude.InternalPassAsPointer in mtyp3.classes:
mtyp = 'i32'
elif isinstance(mtyp3, type3types.StructType3):
mtyp = 'i32' mtyp = 'i32'
else: else:
mtyp = LOAD_STORE_TYPE_MAP.get(mtyp3.name) mtyp = LOAD_STORE_TYPE_MAP.get(mtyp3.name)
@ -1001,7 +986,7 @@ def _generate_struct_constructor(wgn: WasmGenerator, inp: ourlang.StructConstruc
wgn.local.get(tmp_var) wgn.local.get(tmp_var)
wgn.add_statement('local.get', f'${memname}') wgn.add_statement('local.get', f'${memname}')
wgn.add_statement(f'{mtyp}.store', 'offset=' + str(calculate_member_offset( wgn.add_statement(f'{mtyp}.store', 'offset=' + str(calculate_member_offset(
inp.struct_type3, memname inp.struct_type3.name, st_args, memname
))) )))
# Return the allocated address # Return the allocated address

View File

@ -6,9 +6,10 @@ from typing import Dict, Iterable, List, Optional, Union
from typing_extensions import Final from typing_extensions import Final
from . import prelude
from .type3 import typeclasses as type3typeclasses from .type3 import typeclasses as type3typeclasses
from .type3 import types as type3types from .type3.placeholders import PlaceholderForType, Type3OrPlaceholder
from .type3.types import PlaceholderForType, StructType3, Type3, Type3OrPlaceholder from .type3.types import Type3
WEBASSEMBLY_BUILTIN_BYTES_OPS: Final = ('len', ) WEBASSEMBLY_BUILTIN_BYTES_OPS: Final = ('len', )
@ -214,10 +215,10 @@ class AccessStructMember(Expression):
__slots__ = ('varref', 'struct_type3', 'member', ) __slots__ = ('varref', 'struct_type3', 'member', )
varref: VariableReference varref: VariableReference
struct_type3: StructType3 struct_type3: Type3OrPlaceholder
member: str member: str
def __init__(self, varref: VariableReference, struct_type3: StructType3, member: str) -> None: def __init__(self, varref: VariableReference, struct_type3: Type3OrPlaceholder, member: str) -> None:
super().__init__() super().__init__()
self.varref = varref self.varref = varref
@ -326,7 +327,7 @@ class Function:
self.exported = False self.exported = False
self.imported = None self.imported = None
self.statements = [] self.statements = []
self.returns_type3 = type3types.none # FIXME: This could be a placeholder self.returns_type3 = prelude.none # FIXME: This could be a placeholder
self.posonlyargs = [] self.posonlyargs = []
class StructDefinition: class StructDefinition:
@ -335,10 +336,10 @@ class StructDefinition:
""" """
__slots__ = ('struct_type3', 'lineno', ) __slots__ = ('struct_type3', 'lineno', )
struct_type3: StructType3 struct_type3: Type3
lineno: int lineno: int
def __init__(self, struct_type3: StructType3, lineno: int) -> None: def __init__(self, struct_type3: Type3, lineno: int) -> None:
self.struct_type3 = struct_type3 self.struct_type3 = struct_type3
self.lineno = lineno self.lineno = lineno
@ -351,14 +352,16 @@ class StructConstructor(Function):
""" """
__slots__ = ('struct_type3', ) __slots__ = ('struct_type3', )
struct_type3: StructType3 struct_type3: Type3
def __init__(self, struct_type3: StructType3) -> None: def __init__(self, struct_type3: Type3) -> None:
super().__init__(f'@{struct_type3.name}@__init___@', -1) super().__init__(f'@{struct_type3.name}@__init___@', -1)
self.returns_type3 = struct_type3 self.returns_type3 = struct_type3
for mem, typ in struct_type3.members.items(): st_args = prelude.struct.did_construct(struct_type3)
assert st_args is not None
for mem, typ in st_args.items():
self.posonlyargs.append(FunctionParam(mem, typ, )) self.posonlyargs.append(FunctionParam(mem, typ, ))
self.struct_type3 = struct_type3 self.struct_type3 = struct_type3

View File

@ -4,6 +4,7 @@ Parses the source code from the plain text into a syntax tree
import ast import ast
from typing import Any, Dict, NoReturn, Union from typing import Any, Dict, NoReturn, Union
from . import prelude
from .exceptions import StaticError from .exceptions import StaticError
from .ourlang import ( from .ourlang import (
AccessStructMember, AccessStructMember,
@ -31,29 +32,10 @@ from .ourlang import (
UnaryOp, UnaryOp,
VariableReference, VariableReference,
) )
from .prelude import PRELUDE_METHODS, PRELUDE_OPERATORS, PRELUDE_TYPES
from .type3 import typeclasses as type3typeclasses from .type3 import typeclasses as type3typeclasses
from .type3 import types as type3types from .type3 import types as type3types
PRELUDE_OPERATORS = {
**type3typeclasses.Bits.operators,
**type3typeclasses.Eq.operators,
**type3typeclasses.Ord.operators,
**type3typeclasses.Fractional.operators,
**type3typeclasses.Integral.operators,
**type3typeclasses.IntNum.operators,
**type3typeclasses.NatNum.operators,
}
PRELUDE_METHODS = {
**type3typeclasses.Bits.methods,
**type3typeclasses.Eq.methods,
**type3typeclasses.Ord.methods,
**type3typeclasses.Floating.methods,
**type3typeclasses.Fractional.methods,
**type3typeclasses.Integral.methods,
**type3typeclasses.IntNum.methods,
**type3typeclasses.NatNum.methods,
}
def phasm_parse(source: str) -> Module: def phasm_parse(source: str) -> Module:
""" """
@ -252,7 +234,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(type3types.StructType3(node.name, members), node.lineno) return StructDefinition(prelude.struct(node.name, members, set()), 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):
@ -509,7 +491,7 @@ class OurVisitor:
'cast', 'cast',
self.visit_Module_FunctionDef_expr(module, function, our_locals, node.args[0]), self.visit_Module_FunctionDef_expr(module, function, our_locals, node.args[0]),
) )
unary_op.type3 = type3types.u32 unary_op.type3 = prelude.u32
return unary_op return unary_op
elif node.func.id == 'len': elif node.func.id == 'len':
if 1 != len(node.args): if 1 != len(node.args):
@ -570,9 +552,6 @@ class OurVisitor:
if not isinstance(varref, VariableReference): if not isinstance(varref, VariableReference):
_raise_static_error(node.value, 'Must refer to variable') _raise_static_error(node.value, 'Must refer to variable')
if not isinstance(varref.variable.type3, type3types.StructType3):
_raise_static_error(node.value, 'Must refer to struct')
return AccessStructMember( return AccessStructMember(
varref, varref,
varref.variable.type3, varref.variable.type3,
@ -664,10 +643,10 @@ class OurVisitor:
raise NotImplementedError(f'{node.value} as constant') raise NotImplementedError(f'{node.value} as constant')
def visit_type(self, module: Module, node: ast.expr) -> type3types.PrimitiveType3: def visit_type(self, module: Module, node: ast.expr) -> type3types.Type3:
if isinstance(node, ast.Constant): if isinstance(node, ast.Constant):
if node.value is None: if node.value is None:
return type3types.none return prelude.none
_raise_static_error(node, f'Unrecognized type {node.value}') _raise_static_error(node, f'Unrecognized type {node.value}')
@ -675,8 +654,8 @@ class OurVisitor:
if not isinstance(node.ctx, ast.Load): if not isinstance(node.ctx, ast.Load):
_raise_static_error(node, 'Must be load context') _raise_static_error(node, 'Must be load context')
if node.id in type3types.LOOKUP_TABLE: if node.id in PRELUDE_TYPES:
return type3types.LOOKUP_TABLE[node.id] return PRELUDE_TYPES[node.id]
if node.id in module.struct_definitions: if node.id in module.struct_definitions:
return module.struct_definitions[node.id].struct_type3 return module.struct_definitions[node.id].struct_type3
@ -693,7 +672,7 @@ class OurVisitor:
if not isinstance(node.ctx, ast.Load): if not isinstance(node.ctx, ast.Load):
_raise_static_error(node, 'Must be load context') _raise_static_error(node, 'Must be load context')
return type3types.static_array( return prelude.static_array(
self.visit_type(module, node.value), self.visit_type(module, node.value),
type3types.IntType3(node.slice.value), type3types.IntType3(node.slice.value),
) )
@ -702,7 +681,7 @@ class OurVisitor:
if not isinstance(node.ctx, ast.Load): if not isinstance(node.ctx, ast.Load):
_raise_static_error(node, 'Must be load context') _raise_static_error(node, 'Must be load context')
return type3types.tuple_( return prelude.tuple_(
*(self.visit_type(module, elt) for elt in node.elts) *(self.visit_type(module, elt) for elt in node.elts)
) )

274
phasm/prelude/__init__.py Normal file
View File

@ -0,0 +1,274 @@
"""
The prelude are all the builtin types, type classes and methods
"""
from ..type3.typeclasses import Type3Class, TypeVariable, instance_type_class
from ..type3.types import (
Type3,
TypeConstructor_StaticArray,
TypeConstructor_Struct,
TypeConstructor_Tuple,
)
none = Type3('none', [])
"""
The none type, for when functions simply don't return anything. e.g., IO().
"""
bool_ = Type3('bool', [])
"""
The bool type, either True or False
Suffixes with an underscores, as it's a Python builtin
"""
u8 = Type3('u8', [])
"""
The unsigned 8-bit integer type.
Operations on variables employ modular arithmetic, with modulus 2^8.
"""
u32 = Type3('u32', [])
"""
The unsigned 32-bit integer type.
Operations on variables employ modular arithmetic, with modulus 2^32.
"""
u64 = Type3('u64', [])
"""
The unsigned 64-bit integer type.
Operations on variables employ modular arithmetic, with modulus 2^64.
"""
i8 = Type3('i8', [])
"""
The signed 8-bit integer type.
Operations on variables employ modular arithmetic, with modulus 2^8, but
with the middel point being 0.
"""
i32 = Type3('i32', [])
"""
The unsigned 32-bit integer type.
Operations on variables employ modular arithmetic, with modulus 2^32, but
with the middel point being 0.
"""
i64 = Type3('i64', [])
"""
The unsigned 64-bit integer type.
Operations on variables employ modular arithmetic, with modulus 2^64, but
with the middel point being 0.
"""
f32 = Type3('f32', [])
"""
A 32-bits IEEE 754 float, of 32 bits width.
"""
f64 = Type3('f64', [])
"""
A 32-bits IEEE 754 float, of 64 bits width.
"""
bytes_ = Type3('bytes', [])
"""
This is a runtime-determined length piece of memory that can be indexed at runtime.
"""
static_array = TypeConstructor_StaticArray('static_array', [], [])
"""
A type constructor.
Any static array is a fixed length piece of memory that can be indexed at runtime.
It should be applied with one argument. It has a runtime-dynamic length
of the same type repeated.
"""
tuple_ = TypeConstructor_Tuple('tuple', [], [])
"""
This is a fixed length piece of memory.
It should be applied with zero or more arguments. It has a compile time
determined length, and each argument can be different.
"""
struct = TypeConstructor_Struct('struct', [], [])
"""
This is like a tuple, but each argument is named, so that developers
can get and set fields by name.
"""
PRELUDE_TYPES: dict[str, Type3] = {
'none': none,
'bool': bool_,
'u8': u8,
'u32': u32,
'u64': u64,
'i8': i8,
'i32': i32,
'i64': i64,
'f32': f32,
'f64': f64,
'bytes': bytes_,
}
a = TypeVariable('a')
InternalPassAsPointer = Type3Class('InternalPassAsPointer', [a], methods={}, operators={})
"""
Internal type class to keep track which types we pass arounds as a pointer.
"""
instance_type_class(InternalPassAsPointer, bytes_)
instance_type_class(InternalPassAsPointer, static_array)
instance_type_class(InternalPassAsPointer, tuple_)
instance_type_class(InternalPassAsPointer, struct)
Eq = Type3Class('Eq', [a], methods={}, operators={
'==': [a, a, bool_],
'!=': [a, a, bool_],
# FIXME: Do we want to expose 'eqz'? Or is that a compiler optimization?
})
instance_type_class(Eq, u8)
instance_type_class(Eq, u32)
instance_type_class(Eq, u64)
instance_type_class(Eq, i8)
instance_type_class(Eq, i32)
instance_type_class(Eq, i64)
instance_type_class(Eq, f32)
instance_type_class(Eq, f64)
instance_type_class(Eq, static_array)
Ord = Type3Class('Ord', [a], methods={
'min': [a, a, a],
'max': [a, a, a],
}, operators={
'<': [a, a, bool_],
'<=': [a, a, bool_],
'>': [a, a, bool_],
'>=': [a, a, bool_],
}, inherited_classes=[Eq])
instance_type_class(Ord, u8)
instance_type_class(Ord, u32)
instance_type_class(Ord, u64)
instance_type_class(Ord, i8)
instance_type_class(Ord, i32)
instance_type_class(Ord, i64)
instance_type_class(Ord, f32)
instance_type_class(Ord, f64)
Bits = Type3Class('Bits', [a], methods={
'shl': [a, u32, a], # Logical shift left
'shr': [a, u32, a], # Logical shift right
'rotl': [a, u32, a], # Rotate bits left
'rotr': [a, u32, a], # Rotate bits right
# FIXME: Do we want to expose clz, ctz, popcnt?
}, operators={
'&': [a, a, a], # Bit-wise and
'|': [a, a, a], # Bit-wise or
'^': [a, a, a], # Bit-wise xor
})
instance_type_class(Bits, u8)
instance_type_class(Bits, u32)
instance_type_class(Bits, u64)
NatNum = Type3Class('NatNum', [a], methods={}, operators={
'+': [a, a, a],
'-': [a, a, a],
'*': [a, a, a],
'<<': [a, u32, a], # Arithmic shift left
'>>': [a, u32, a], # Arithmic shift right
})
instance_type_class(NatNum, u32)
instance_type_class(NatNum, u64)
instance_type_class(NatNum, i32)
instance_type_class(NatNum, i64)
instance_type_class(NatNum, f32)
instance_type_class(NatNum, f64)
IntNum = Type3Class('IntNum', [a], methods={
'abs': [a, a],
'neg': [a, a],
}, operators={}, inherited_classes=[NatNum])
instance_type_class(IntNum, i32)
instance_type_class(IntNum, i64)
instance_type_class(IntNum, f32)
instance_type_class(IntNum, f64)
Integral = Type3Class('Eq', [a], methods={
}, operators={
'//': [a, a, a],
'%': [a, a, a],
}, inherited_classes=[NatNum])
instance_type_class(Integral, u32)
instance_type_class(Integral, u64)
instance_type_class(Integral, i32)
instance_type_class(Integral, i64)
Fractional = Type3Class('Fractional', [a], methods={
'ceil': [a, a],
'floor': [a, a],
'trunc': [a, a],
'nearest': [a, a],
}, operators={
'/': [a, a, a],
}, inherited_classes=[NatNum])
instance_type_class(Fractional, f32)
instance_type_class(Fractional, f64)
Floating = Type3Class('Floating', [a], methods={
'sqrt': [a, a],
}, operators={}, inherited_classes=[Fractional])
# FIXME: Do we want to expose copysign?
instance_type_class(Floating, f32)
instance_type_class(Floating, f64)
PRELUDE_TYPE_CLASSES = {
'Eq': Eq,
'Ord': Ord,
'Bits': Bits,
'NatNum': NatNum,
'IntNum': IntNum,
'Integral': Integral,
'Fractional': Fractional,
'Floating': Floating,
}
PRELUDE_OPERATORS = {
**Bits.operators,
**Eq.operators,
**Ord.operators,
**Fractional.operators,
**Integral.operators,
**IntNum.operators,
**NatNum.operators,
}
PRELUDE_METHODS = {
**Bits.methods,
**Eq.methods,
**Ord.methods,
**Floating.methods,
**Fractional.methods,
**Integral.methods,
**IntNum.methods,
**NatNum.methods,
}

View File

@ -1,35 +1,35 @@
from . import prelude
from .type3 import types as type3types from .type3 import types as type3types
def calculate_alloc_size(typ: type3types.Type3, is_member: bool = False) -> int: def calculate_alloc_size(typ: type3types.Type3, is_member: bool = False) -> int:
if typ in (type3types.u8, type3types.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
if typ in (type3types.u32, type3types.i32, type3types.f32, ): if typ in (prelude.u32, prelude.i32, prelude.f32, ):
return 4 return 4
if typ in (type3types.u64, type3types.i64, type3types.f64, ): if typ in (prelude.u64, prelude.i64, prelude.f64, ):
return 8 return 8
if typ == type3types.bytes_: if typ == prelude.bytes_:
if is_member: if is_member:
return 4 return 4
raise NotImplementedError # When does this happen? raise NotImplementedError # When does this happen?
if isinstance(typ, type3types.StructType3): st_args = prelude.struct.did_construct(typ)
if st_args is not None:
if is_member: if is_member:
# Structs referred to by other structs or tuples are pointers # Structs referred to by other structs or tuples are pointers
return 4 return 4
return sum( return sum(
calculate_alloc_size(x, is_member=True) calculate_alloc_size(x, is_member=True)
for x in typ.members.values() for x in st_args.values()
) )
assert isinstance(typ, type3types.PrimitiveType3) sa_args = prelude.static_array.did_construct(typ)
sa_args = type3types.static_array.did_construct(typ)
if sa_args is not None: if sa_args is not None:
if is_member: if is_member:
# tuples referred to by other structs or tuples are pointers # tuples referred to by other structs or tuples are pointers
@ -39,7 +39,7 @@ def calculate_alloc_size(typ: type3types.Type3, is_member: bool = False) -> int:
return sa_len.value * calculate_alloc_size(sa_type, is_member=True) return sa_len.value * calculate_alloc_size(sa_type, is_member=True)
tp_args = type3types.tuple_.did_construct(typ) tp_args = prelude.tuple_.did_construct(typ)
if tp_args is not None: if tp_args is not None:
if is_member: if is_member:
# tuples referred to by other structs or tuples are pointers # tuples referred to by other structs or tuples are pointers
@ -47,25 +47,19 @@ def calculate_alloc_size(typ: type3types.Type3, is_member: bool = False) -> int:
size = 0 size = 0
for arg in tp_args: for arg in tp_args:
assert not isinstance(arg, type3types.IntType3)
if isinstance(arg, type3types.PlaceholderForType):
assert isinstance(arg.resolve_as, type3types.PrimitiveType3)
arg = arg.resolve_as
size += calculate_alloc_size(arg, is_member=True) size += calculate_alloc_size(arg, is_member=True)
return size return size
raise NotImplementedError(calculate_alloc_size, typ) raise NotImplementedError(calculate_alloc_size, typ)
def calculate_member_offset(struct_type3: type3types.StructType3, member: str) -> int: def calculate_member_offset(st_name: str, st_args: dict[str, type3types.Type3], needle: str) -> int:
result = 0 result = 0
for mem, memtyp in struct_type3.members.items(): for memnam, memtyp in st_args.items():
if member == mem: if needle == memnam:
return result return result
result += calculate_alloc_size(memtyp, is_member=True) result += calculate_alloc_size(memtyp, is_member=True)
raise Exception(f'{member} not in {struct_type3}') raise Exception(f'{needle} not in {st_name}')

View File

@ -5,8 +5,8 @@ These need to be resolved before the program can be compiled.
""" """
from typing import Dict, List, Optional, Tuple, Union from typing import Dict, List, Optional, Tuple, Union
from .. import ourlang from .. import ourlang, prelude
from . import typeclasses, types from . import placeholders, typeclasses, types
class Error: class Error:
@ -32,13 +32,13 @@ class RequireTypeSubstitutes:
typing of the program, so this constraint can be updated. typing of the program, so this constraint can be updated.
""" """
SubstitutionMap = Dict[types.PlaceholderForType, types.Type3] SubstitutionMap = Dict[placeholders.PlaceholderForType, types.Type3]
NewConstraintList = List['ConstraintBase'] NewConstraintList = List['ConstraintBase']
CheckResult = Union[None, SubstitutionMap, Error, NewConstraintList, RequireTypeSubstitutes] CheckResult = Union[None, SubstitutionMap, Error, NewConstraintList, RequireTypeSubstitutes]
HumanReadableRet = Tuple[str, Dict[str, Union[str, ourlang.Expression, types.Type3, types.PlaceholderForType]]] HumanReadableRet = Tuple[str, Dict[str, Union[str, ourlang.Expression, types.Type3, placeholders.PlaceholderForType]]]
class Context: class Context:
""" """
@ -92,9 +92,9 @@ class SameTypeConstraint(ConstraintBase):
""" """
__slots__ = ('type_list', ) __slots__ = ('type_list', )
type_list: List[types.Type3OrPlaceholder] type_list: List[placeholders.Type3OrPlaceholder]
def __init__(self, *type_list: types.Type3OrPlaceholder, comment: Optional[str] = None) -> None: def __init__(self, *type_list: placeholders.Type3OrPlaceholder, comment: Optional[str] = None) -> None:
super().__init__(comment=comment) super().__init__(comment=comment)
assert len(type_list) > 1 assert len(type_list) > 1
@ -102,21 +102,17 @@ class SameTypeConstraint(ConstraintBase):
def check(self) -> CheckResult: def check(self) -> CheckResult:
known_types: List[types.Type3] = [] known_types: List[types.Type3] = []
placeholders = [] phft_list = []
for typ in self.type_list: for typ in self.type_list:
if isinstance(typ, types.IntType3): if isinstance(typ, types.Type3):
known_types.append(typ) known_types.append(typ)
continue continue
if isinstance(typ, (types.PrimitiveType3, types.StructType3, )): if isinstance(typ, placeholders.PlaceholderForType):
known_types.append(typ)
continue
if isinstance(typ, types.PlaceholderForType):
if typ.resolve_as is not None: if typ.resolve_as is not None:
known_types.append(typ.resolve_as) known_types.append(typ.resolve_as)
else: else:
placeholders.append(typ) phft_list.append(typ)
continue continue
raise NotImplementedError(typ) raise NotImplementedError(typ)
@ -124,28 +120,20 @@ class SameTypeConstraint(ConstraintBase):
if not known_types: if not known_types:
return RequireTypeSubstitutes() return RequireTypeSubstitutes()
new_constraint_list: List[ConstraintBase] = []
first_type = known_types[0] first_type = known_types[0]
for typ in known_types[1:]: for ktyp in known_types[1:]:
if typ != first_type: if ktyp != first_type:
return Error(f'{typ:s} must be {first_type:s} instead', comment=self.comment) return Error(f'{typ:s} must be {first_type:s} instead', comment=self.comment)
if new_constraint_list:
# If this happens, make CheckResult a class that can have both
assert not placeholders, 'Cannot (yet) return both new placeholders and new constraints'
return new_constraint_list
if not placeholders: if not placeholders:
return None return None
for typ in placeholders: for phft in phft_list:
typ.resolve_as = first_type phft.resolve_as = first_type
return { return {
typ: first_type typ: first_type
for typ in placeholders for typ in phft_list
} }
def human_readable(self) -> HumanReadableRet: def human_readable(self) -> HumanReadableRet:
@ -163,7 +151,7 @@ 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):
def __init__(self, exp_type: types.Type3OrPlaceholder, args: List[types.Type3OrPlaceholder], comment: str): def __init__(self, exp_type: placeholders.Type3OrPlaceholder, args: List[placeholders.Type3OrPlaceholder], comment: str):
super().__init__(comment=comment) super().__init__(comment=comment)
self.exp_type = exp_type self.exp_type = exp_type
@ -171,15 +159,15 @@ class TupleMatchConstraint(ConstraintBase):
def check(self) -> CheckResult: def check(self) -> CheckResult:
exp_type = self.exp_type exp_type = self.exp_type
if isinstance(exp_type, types.PlaceholderForType): if isinstance(exp_type, placeholders.PlaceholderForType):
if exp_type.resolve_as is None: if exp_type.resolve_as is None:
return RequireTypeSubstitutes() return RequireTypeSubstitutes()
exp_type = exp_type.resolve_as exp_type = exp_type.resolve_as
assert isinstance(exp_type, types.PrimitiveType3) assert isinstance(exp_type, types.Type3)
sa_args = types.static_array.did_construct(exp_type) sa_args = prelude.static_array.did_construct(exp_type)
if sa_args is not None: if sa_args is not None:
sa_type, sa_len = sa_args sa_type, sa_len = sa_args
@ -191,7 +179,7 @@ class TupleMatchConstraint(ConstraintBase):
for arg in self.args for arg in self.args
] ]
tp_args = types.tuple_.did_construct(exp_type) tp_args = prelude.tuple_.did_construct(exp_type)
if tp_args is not None: 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)
@ -209,10 +197,10 @@ class CastableConstraint(ConstraintBase):
""" """
__slots__ = ('from_type3', 'to_type3', ) __slots__ = ('from_type3', 'to_type3', )
from_type3: types.Type3OrPlaceholder from_type3: placeholders.Type3OrPlaceholder
to_type3: types.Type3OrPlaceholder to_type3: placeholders.Type3OrPlaceholder
def __init__(self, from_type3: types.Type3OrPlaceholder, to_type3: types.Type3OrPlaceholder, comment: Optional[str] = None) -> None: def __init__(self, from_type3: placeholders.Type3OrPlaceholder, to_type3: placeholders.Type3OrPlaceholder, comment: Optional[str] = None) -> None:
super().__init__(comment=comment) super().__init__(comment=comment)
self.from_type3 = from_type3 self.from_type3 = from_type3
@ -220,17 +208,17 @@ class CastableConstraint(ConstraintBase):
def check(self) -> CheckResult: def check(self) -> CheckResult:
ftyp = self.from_type3 ftyp = self.from_type3
if isinstance(ftyp, types.PlaceholderForType) and ftyp.resolve_as is not None: if isinstance(ftyp, placeholders.PlaceholderForType) and ftyp.resolve_as is not None:
ftyp = ftyp.resolve_as ftyp = ftyp.resolve_as
ttyp = self.to_type3 ttyp = self.to_type3
if isinstance(ttyp, types.PlaceholderForType) and ttyp.resolve_as is not None: if isinstance(ttyp, placeholders.PlaceholderForType) and ttyp.resolve_as is not None:
ttyp = ttyp.resolve_as ttyp = ttyp.resolve_as
if isinstance(ftyp, types.PlaceholderForType) or isinstance(ttyp, types.PlaceholderForType): if isinstance(ftyp, placeholders.PlaceholderForType) or isinstance(ttyp, placeholders.PlaceholderForType):
return RequireTypeSubstitutes() return RequireTypeSubstitutes()
if ftyp is types.u8 and ttyp is types.u32: if ftyp is prelude.u8 and ttyp is prelude.u32:
return None return None
return Error(f'Cannot cast {ftyp.name} to {ttyp.name}') return Error(f'Cannot cast {ftyp.name} to {ttyp.name}')
@ -254,13 +242,13 @@ class MustImplementTypeClassConstraint(ConstraintBase):
__slots__ = ('type_class3', 'type3', ) __slots__ = ('type_class3', 'type3', )
type_class3: Union[str, typeclasses.Type3Class] type_class3: Union[str, typeclasses.Type3Class]
type3: types.Type3OrPlaceholder type3: placeholders.Type3OrPlaceholder
DATA = { DATA = {
'bytes': {'Foldable', 'Sized'}, 'bytes': {'Foldable', 'Sized'},
} }
def __init__(self, type_class3: Union[str, typeclasses.Type3Class], type3: types.Type3OrPlaceholder, comment: Optional[str] = None) -> None: def __init__(self, type_class3: Union[str, typeclasses.Type3Class], type3: placeholders.Type3OrPlaceholder, comment: Optional[str] = None) -> None:
super().__init__(comment=comment) super().__init__(comment=comment)
self.type_class3 = type_class3 self.type_class3 = type_class3
@ -268,10 +256,10 @@ class MustImplementTypeClassConstraint(ConstraintBase):
def check(self) -> CheckResult: def check(self) -> CheckResult:
typ = self.type3 typ = self.type3
if isinstance(typ, types.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
if isinstance(typ, types.PlaceholderForType): if isinstance(typ, placeholders.PlaceholderForType):
return RequireTypeSubstitutes() return RequireTypeSubstitutes()
if isinstance(self.type_class3, typeclasses.Type3Class): if isinstance(self.type_class3, typeclasses.Type3Class):
@ -301,12 +289,12 @@ class LiteralFitsConstraint(ConstraintBase):
""" """
__slots__ = ('type3', 'literal', ) __slots__ = ('type3', 'literal', )
type3: types.Type3OrPlaceholder type3: placeholders.Type3OrPlaceholder
literal: Union[ourlang.ConstantPrimitive, ourlang.ConstantBytes, ourlang.ConstantTuple, ourlang.ConstantStruct] literal: Union[ourlang.ConstantPrimitive, ourlang.ConstantBytes, ourlang.ConstantTuple, ourlang.ConstantStruct]
def __init__( def __init__(
self, self,
type3: types.Type3OrPlaceholder, type3: placeholders.Type3OrPlaceholder,
literal: Union[ourlang.ConstantPrimitive, ourlang.ConstantBytes, ourlang.ConstantTuple, ourlang.ConstantStruct], literal: Union[ourlang.ConstantPrimitive, ourlang.ConstantBytes, ourlang.ConstantTuple, ourlang.ConstantStruct],
comment: Optional[str] = None, comment: Optional[str] = None,
) -> None: ) -> None:
@ -330,7 +318,7 @@ class LiteralFitsConstraint(ConstraintBase):
'f64': None, 'f64': None,
} }
if isinstance(self.type3, types.PlaceholderForType): if isinstance(self.type3, placeholders.PlaceholderForType):
if self.type3.resolve_as is None: if self.type3.resolve_as is None:
return RequireTypeSubstitutes() return RequireTypeSubstitutes()
@ -359,7 +347,7 @@ class LiteralFitsConstraint(ConstraintBase):
return Error('Must be real', comment=self.comment) # FIXME: Add line information return Error('Must be real', comment=self.comment) # FIXME: Add line information
if self.type3 is types.bytes_: if self.type3 is prelude.bytes_:
if isinstance(self.literal.value, bytes): if isinstance(self.literal.value, bytes):
return None return None
@ -367,9 +355,9 @@ class LiteralFitsConstraint(ConstraintBase):
res: NewConstraintList res: NewConstraintList
assert isinstance(self.type3, types.PrimitiveType3) assert isinstance(self.type3, types.Type3)
tp_args = types.tuple_.did_construct(self.type3) tp_args = prelude.tuple_.did_construct(self.type3)
if tp_args is not None: if tp_args is not None:
if not isinstance(self.literal, ourlang.ConstantTuple): if not isinstance(self.literal, ourlang.ConstantTuple):
return Error('Must be tuple', comment=self.comment) return Error('Must be tuple', comment=self.comment)
@ -390,7 +378,7 @@ class LiteralFitsConstraint(ConstraintBase):
return res return res
sa_args = types.static_array.did_construct(self.type3) sa_args = prelude.static_array.did_construct(self.type3)
if sa_args is not None: if sa_args is not None:
if not isinstance(self.literal, ourlang.ConstantTuple): if not isinstance(self.literal, ourlang.ConstantTuple):
return Error('Must be tuple', comment=self.comment) return Error('Must be tuple', comment=self.comment)
@ -413,7 +401,8 @@ class LiteralFitsConstraint(ConstraintBase):
return res return res
if isinstance(self.type3, types.StructType3): st_args = prelude.struct.did_construct(self.type3)
if st_args is not None:
if not isinstance(self.literal, ourlang.ConstantStruct): if not isinstance(self.literal, ourlang.ConstantStruct):
return Error('Must be struct') return Error('Must be struct')
@ -421,18 +410,18 @@ class LiteralFitsConstraint(ConstraintBase):
return Error('Struct mismatch') return Error('Struct mismatch')
if len(self.type3.members) != len(self.literal.value): if len(st_args) != len(self.literal.value):
return Error('Struct element count mismatch') return Error('Struct element count mismatch')
res = [] res = []
res.extend( res.extend(
LiteralFitsConstraint(x, y) LiteralFitsConstraint(x, y)
for x, y in zip(self.type3.members.values(), self.literal.value) for x, y in zip(st_args.values(), self.literal.value)
) )
res.extend( res.extend(
SameTypeConstraint(x_t, y.type3, comment=f'{self.literal.struct_name}.{x_n}') SameTypeConstraint(x_t, y.type3, comment=f'{self.literal.struct_name}.{x_n}')
for (x_n, x_t, ), y in zip(self.type3.members.items(), self.literal.value) for (x_n, x_t, ), y in zip(st_args.items(), self.literal.value)
) )
return res return res
@ -457,12 +446,12 @@ class CanBeSubscriptedConstraint(ConstraintBase):
""" """
__slots__ = ('ret_type3', 'type3', 'index', 'index_type3', ) __slots__ = ('ret_type3', 'type3', 'index', 'index_type3', )
ret_type3: types.Type3OrPlaceholder ret_type3: placeholders.Type3OrPlaceholder
type3: types.Type3OrPlaceholder type3: placeholders.Type3OrPlaceholder
index: ourlang.Expression index: ourlang.Expression
index_type3: types.Type3OrPlaceholder index_type3: placeholders.Type3OrPlaceholder
def __init__(self, ret_type3: types.Type3OrPlaceholder, type3: types.Type3OrPlaceholder, index: ourlang.Expression, comment: Optional[str] = None) -> None: def __init__(self, ret_type3: placeholders.Type3OrPlaceholder, type3: placeholders.Type3OrPlaceholder, index: ourlang.Expression, comment: Optional[str] = None) -> None:
super().__init__(comment=comment) super().__init__(comment=comment)
self.ret_type3 = ret_type3 self.ret_type3 = ret_type3
@ -472,20 +461,20 @@ class CanBeSubscriptedConstraint(ConstraintBase):
def check(self) -> CheckResult: def check(self) -> CheckResult:
exp_type = self.type3 exp_type = self.type3
if isinstance(exp_type, types.PlaceholderForType): if isinstance(exp_type, placeholders.PlaceholderForType):
if exp_type.resolve_as is None: if exp_type.resolve_as is None:
return RequireTypeSubstitutes() return RequireTypeSubstitutes()
exp_type = exp_type.resolve_as exp_type = exp_type.resolve_as
assert isinstance(exp_type, types.PrimitiveType3) assert isinstance(exp_type, types.Type3)
sa_args = types.static_array.did_construct(exp_type) sa_args = prelude.static_array.did_construct(exp_type)
if sa_args is not None: if sa_args is not None:
sa_type, sa_len = sa_args sa_type, sa_len = sa_args
result: List[ConstraintBase] = [ result: List[ConstraintBase] = [
SameTypeConstraint(types.u32, self.index_type3, comment='([]) :: Subscriptable a => a b -> u32 -> b'), SameTypeConstraint(prelude.u32, self.index_type3, comment='([]) :: Subscriptable a => a b -> u32 -> b'),
SameTypeConstraint(sa_type, self.ret_type3, comment='([]) :: Subscriptable a => a b -> u32 -> b'), SameTypeConstraint(sa_type, self.ret_type3, comment='([]) :: Subscriptable a => a b -> u32 -> b'),
] ]
@ -500,7 +489,7 @@ class CanBeSubscriptedConstraint(ConstraintBase):
# 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 = types.tuple_.did_construct(exp_type) tp_args = prelude.tuple_.did_construct(exp_type)
if tp_args is not None: 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')
@ -512,14 +501,14 @@ class CanBeSubscriptedConstraint(ConstraintBase):
return Error('Tuple index out of range') return Error('Tuple index out of range')
return [ return [
SameTypeConstraint(types.u32, self.index_type3, comment=f'Tuple subscript index {self.index.value}'), SameTypeConstraint(prelude.u32, self.index_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}'), SameTypeConstraint(tp_args[self.index.value], self.ret_type3, comment=f'Tuple subscript index {self.index.value}'),
] ]
if exp_type is types.bytes_: if exp_type is prelude.bytes_:
return [ return [
SameTypeConstraint(types.u32, self.index_type3, comment='([]) :: bytes -> u32 -> u8'), SameTypeConstraint(prelude.u32, self.index_type3, comment='([]) :: bytes -> u32 -> u8'),
SameTypeConstraint(types.u8, self.ret_type3, comment='([]) :: bytes -> u32 -> u8'), SameTypeConstraint(prelude.u8, self.ret_type3, comment='([]) :: bytes -> u32 -> u8'),
] ]
return Error(f'{exp_type.name} cannot be subscripted') return Error(f'{exp_type.name} cannot be subscripted')

View File

@ -5,7 +5,8 @@ The constraints solver can then try to resolve all constraints.
""" """
from typing import Generator, List from typing import Generator, List
from .. import ourlang from .. import ourlang, prelude
from . import placeholders as placeholders
from . import typeclasses as type3typeclasses from . import typeclasses as type3typeclasses
from . import types as type3types from . import types as type3types
from .constraints import ( from .constraints import (
@ -50,7 +51,7 @@ def expression(ctx: Context, inp: ourlang.Expression) -> ConstraintGenerator:
if 'len' == inp.operator: if 'len' == inp.operator:
yield from expression(ctx, inp.right) yield from expression(ctx, inp.right)
yield MustImplementTypeClassConstraint('Sized', inp.right.type3) yield MustImplementTypeClassConstraint('Sized', inp.right.type3)
yield SameTypeConstraint(type3types.u32, inp.type3, comment='len :: Sized a => a -> u32') yield SameTypeConstraint(prelude.u32, inp.type3, comment='len :: Sized a => a -> u32')
return return
if 'cast' == inp.operator: if 'cast' == inp.operator:
@ -62,7 +63,7 @@ def expression(ctx: Context, inp: ourlang.Expression) -> ConstraintGenerator:
if isinstance(inp, ourlang.BinaryOp): if isinstance(inp, ourlang.BinaryOp):
type_var_map = { type_var_map = {
x: type3types.PlaceholderForType([]) x: placeholders.PlaceholderForType([])
for x in inp.operator.signature for x in inp.operator.signature
if isinstance(x, type3typeclasses.TypeVariable) if isinstance(x, type3typeclasses.TypeVariable)
} }
@ -83,12 +84,11 @@ def expression(ctx: Context, inp: ourlang.Expression) -> ConstraintGenerator:
yield SameTypeConstraint(type_var_map[sig_part], arg_expr.type3) yield SameTypeConstraint(type_var_map[sig_part], arg_expr.type3)
continue continue
if isinstance(sig_part, type3typeclasses.TypeReference): if isinstance(sig_part, type3types.Type3):
# On key error: We probably have to a lot of work to do refactoring yield SameTypeConstraint(sig_part, arg_expr.type3)
# the type lookups
exp_type = type3types.LOOKUP_TABLE[sig_part.name]
yield SameTypeConstraint(exp_type, arg_expr.type3)
continue continue
raise NotImplementedError(sig_part)
return return
if isinstance(inp, ourlang.FunctionCall): if isinstance(inp, ourlang.FunctionCall):
@ -96,7 +96,7 @@ def expression(ctx: Context, inp: ourlang.Expression) -> ConstraintGenerator:
# FIXME: Duplicate code with BinaryOp # FIXME: Duplicate code with BinaryOp
type_var_map = { type_var_map = {
x: type3types.PlaceholderForType([]) x: placeholders.PlaceholderForType([])
for x in inp.function.signature for x in inp.function.signature
if isinstance(x, type3typeclasses.TypeVariable) if isinstance(x, type3typeclasses.TypeVariable)
} }
@ -117,12 +117,11 @@ def expression(ctx: Context, inp: ourlang.Expression) -> ConstraintGenerator:
yield SameTypeConstraint(type_var_map[sig_part], arg_expr.type3) yield SameTypeConstraint(type_var_map[sig_part], arg_expr.type3)
continue continue
if isinstance(sig_part, type3typeclasses.TypeReference): if isinstance(sig_part, type3types.Type3):
# On key error: We probably have to a lot of work to do refactoring yield SameTypeConstraint(sig_part, arg_expr.type3)
# the type lookups
exp_type = type3types.LOOKUP_TABLE[sig_part.name]
yield SameTypeConstraint(exp_type, arg_expr.type3)
continue continue
raise NotImplementedError(sig_part)
return return
yield SameTypeConstraint(inp.function.returns_type3, inp.type3, yield SameTypeConstraint(inp.function.returns_type3, inp.type3,
@ -159,8 +158,12 @@ def expression(ctx: Context, inp: ourlang.Expression) -> ConstraintGenerator:
return return
if isinstance(inp, ourlang.AccessStructMember): if isinstance(inp, ourlang.AccessStructMember):
assert isinstance(inp.struct_type3, type3types.Type3) # When does this happen?
st_args = prelude.struct.did_construct(inp.struct_type3)
assert st_args is not None # When does this happen?
yield from expression(ctx, inp.varref) yield from expression(ctx, inp.varref)
yield SameTypeConstraint(inp.struct_type3.members[inp.member], inp.type3, yield SameTypeConstraint(st_args[inp.member], inp.type3,
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
@ -185,7 +188,7 @@ def statement_return(ctx: Context, fun: ourlang.Function, inp: ourlang.Statement
def statement_if(ctx: Context, fun: ourlang.Function, inp: ourlang.StatementIf) -> ConstraintGenerator: def statement_if(ctx: Context, fun: ourlang.Function, inp: ourlang.StatementIf) -> ConstraintGenerator:
yield from expression(ctx, inp.test) yield from expression(ctx, inp.test)
yield SameTypeConstraint(inp.test.type3, type3types.bool_, yield SameTypeConstraint(inp.test.type3, prelude.bool_,
comment='Must pass a boolean expression to if') comment='Must pass a boolean expression to if')
for stmt in inp.statements: for stmt in inp.statements:

View File

@ -12,14 +12,11 @@ from .constraints import (
SubstitutionMap, SubstitutionMap,
) )
from .constraintsgenerator import phasm_type3_generate_constraints from .constraintsgenerator import phasm_type3_generate_constraints
from .types import ( from .placeholders import (
IntType3,
PlaceholderForType, PlaceholderForType,
PrimitiveType3,
StructType3,
Type3,
Type3OrPlaceholder, Type3OrPlaceholder,
) )
from .types import Type3
MAX_RESTACK_COUNT = 100 MAX_RESTACK_COUNT = 100
@ -143,7 +140,7 @@ def print_constraint(placeholder_id_map: Dict[int, str], constraint: ConstraintB
print('- ' + txt.format(**act_fmt)) print('- ' + txt.format(**act_fmt))
def get_printable_type_name(inp: Type3OrPlaceholder, placeholder_id_map: Dict[int, str]) -> str: def get_printable_type_name(inp: Type3OrPlaceholder, placeholder_id_map: Dict[int, str]) -> str:
if isinstance(inp, (PrimitiveType3, StructType3, IntType3, )): if isinstance(inp, Type3):
return inp.name return inp.name
if isinstance(inp, PlaceholderForType): if isinstance(inp, PlaceholderForType):

View File

@ -0,0 +1,67 @@
"""
Contains the placeholder for types for use in Phasm.
These are temporary while the compiler is calculating all the types and validating them.
"""
from typing import Any, Iterable, List, Optional, Protocol, Union
from .types import Type3
TYPE3_ASSERTION_ERROR = 'You must call phasm_type3 after calling phasm_parse before you can call any other method'
class ExpressionProtocol(Protocol):
"""
A protocol for classes that should be updated on substitution
"""
type3: 'Type3OrPlaceholder'
"""
The type to update
"""
class PlaceholderForType:
"""
A placeholder type, for when we don't know the final type yet
"""
__slots__ = ('update_on_substitution', 'resolve_as', )
update_on_substitution: List[ExpressionProtocol]
resolve_as: Optional[Type3]
def __init__(self, update_on_substitution: Iterable[ExpressionProtocol]) -> None:
self.update_on_substitution = [*update_on_substitution]
self.resolve_as = None
def __repr__(self) -> str:
uos = ', '.join(repr(x) for x in self.update_on_substitution)
return f'PlaceholderForType({id(self)}, [{uos}])'
def __str__(self) -> str:
return f'PhFT_{id(self)}'
def __format__(self, format_spec: str) -> str:
if format_spec != 's':
raise TypeError('unsupported format string passed to Type3.__format__')
return str(self)
def __eq__(self, other: Any) -> bool:
if isinstance(other, Type3):
return False
if not isinstance(other, PlaceholderForType):
raise NotImplementedError
return self is other
def __ne__(self, other: Any) -> bool:
return not self.__eq__(other)
def __hash__(self) -> int:
return 0 # Valid but performs badly
def __bool__(self) -> bool:
raise NotImplementedError
Type3OrPlaceholder = Union[Type3, PlaceholderForType]

View File

@ -1,5 +1,7 @@
from typing import Any, Dict, Iterable, List, Mapping, Optional, Union from typing import Any, Dict, Iterable, List, Mapping, Optional, Union
from .types import Type3, TypeConstructor, TypeConstructor_Struct
class TypeVariable: class TypeVariable:
__slots__ = ('letter', ) __slots__ = ('letter', )
@ -48,15 +50,12 @@ class Type3ClassMethod:
type3_class: 'Type3Class' type3_class: 'Type3Class'
name: str name: str
signature: List[Union[TypeReference, TypeVariable]] signature: List[Union[Type3, TypeVariable]]
def __init__(self, type3_class: 'Type3Class', name: str, signature: str) -> None: def __init__(self, type3_class: 'Type3Class', name: str, signature: Iterable[Union[Type3, TypeVariable]]) -> None:
self.type3_class = type3_class self.type3_class = type3_class
self.name = name self.name = name
self.signature = [ self.signature = list(signature)
TypeVariable(x) if len(x) == 1 else TypeReference(x)
for x in signature.split(' -> ')
]
def __repr__(self) -> str: def __repr__(self) -> str:
return f'Type3ClassMethod({repr(self.type3_class)}, {repr(self.name)}, {repr(self.signature)})' return f'Type3ClassMethod({repr(self.type3_class)}, {repr(self.name)}, {repr(self.signature)})'
@ -73,13 +72,13 @@ class Type3Class:
def __init__( def __init__(
self, self,
name: str, name: str,
args: Iterable[str], args: Iterable[TypeVariable],
methods: Mapping[str, str], methods: Mapping[str, Iterable[Union[Type3, TypeVariable]]],
operators: Mapping[str, str], operators: Mapping[str, Iterable[Union[Type3, TypeVariable]]],
inherited_classes: Optional[List['Type3Class']] = None, inherited_classes: Optional[List['Type3Class']] = None,
) -> None: ) -> None:
self.name = name self.name = name
self.args = [TypeVariable(x) for x in args] self.args = list(args)
self.methods = { self.methods = {
k: Type3ClassMethod(self, k, v) k: Type3ClassMethod(self, k, v)
for k, v in methods.items() for k, v in methods.items()
@ -93,66 +92,8 @@ class Type3Class:
def __repr__(self) -> str: def __repr__(self) -> str:
return self.name return self.name
InternalPassAsPointer = Type3Class('InternalPassAsPointer', ['a'], methods={}, operators={}) def instance_type_class(cls: Type3Class, typ: Type3 | TypeConstructor[Any] | TypeConstructor_Struct) -> None:
if isinstance(typ, Type3):
Eq = Type3Class('Eq', ['a'], methods={}, operators={ typ.classes.add(cls)
'==': 'a -> a -> bool', else:
'!=': 'a -> a -> bool', typ.type_classes.add(cls)
# FIXME: Do we want to expose 'eqz'? Or is that a compiler optimization?
})
Ord = Type3Class('Ord', ['a'], methods={
'min': 'a -> a -> a',
'max': 'a -> a -> a',
}, operators={
'<': 'a -> a -> bool',
'<=': 'a -> a -> bool',
'>': 'a -> a -> bool',
'>=': 'a -> a -> bool',
}, inherited_classes=[Eq])
Bits = Type3Class('Bits', ['a'], methods={
'shl': 'a -> u32 -> a', # Logical shift left
'shr': 'a -> u32 -> a', # Logical shift right
'rotl': 'a -> u32 -> a', # Rotate bits left
'rotr': 'a -> u32 -> a', # Rotate bits right
# FIXME: Do we want to expose clz, ctz, popcnt?
}, operators={
'&': 'a -> a -> a', # Bit-wise and
'|': 'a -> a -> a', # Bit-wise or
'^': 'a -> a -> a', # Bit-wise xor
})
NatNum = Type3Class('NatNum', ['a'], methods={}, operators={
'+': 'a -> a -> a',
'-': 'a -> a -> a',
'*': 'a -> a -> a',
'<<': 'a -> u32 -> a', # Arithmic shift left
'>>': 'a -> u32 -> a', # Arithmic shift right
})
IntNum = Type3Class('IntNum', ['a'], methods={
'abs': 'a -> a',
'neg': 'a -> a',
}, operators={}, inherited_classes=[NatNum])
Integral = Type3Class('Eq', ['a'], methods={
}, operators={
'//': 'a -> a -> a',
'%': 'a -> a -> a',
}, inherited_classes=[NatNum])
Fractional = Type3Class('Fractional', ['a'], methods={
'ceil': 'a -> a',
'floor': 'a -> a',
'trunc': 'a -> a',
'nearest': 'a -> a',
}, operators={
'/': 'a -> a -> a',
}, inherited_classes=[NatNum])
Floating = Type3Class('Floating', ['a'], methods={
'sqrt': 'a -> a',
}, operators={}, inherited_classes=[Fractional])
# FIXME: Do we want to expose copysign?

View File

@ -1,51 +1,29 @@
""" """
Contains the final types for use in Phasm Contains the final types for use in Phasm, as well as construtors.
These are actual, instantiated types; not the abstract types that the
constraint generator works with.
""" """
from typing import ( from typing import (
TYPE_CHECKING,
Any, Any,
Dict,
Generic, Generic,
Iterable, Iterable,
List,
Optional,
Protocol,
Set, Set,
Tuple, Tuple,
TypeVar, TypeVar,
Union,
) )
from .typeclasses import ( if TYPE_CHECKING:
Bits, from .typeclasses import Type3Class
Eq,
Floating,
Fractional,
Integral,
InternalPassAsPointer,
IntNum,
NatNum,
Ord,
Type3Class,
)
TYPE3_ASSERTION_ERROR = 'You must call phasm_type3 after calling phasm_parse before you can call any other method'
class ExpressionProtocol(Protocol): class KindArgument:
""" pass
A protocol for classes that should be updated on substitution
"""
type3: 'Type3OrPlaceholder' class Type3(KindArgument):
"""
The type to update
"""
class Type3:
""" """
Base class for the type3 types Base class for the type3 types
(Having a separate name makes it easier to distinguish from
Python's Type)
""" """
__slots__ = ('name', 'classes', ) __slots__ = ('name', 'classes', )
@ -54,12 +32,12 @@ class Type3:
The name of the string, as parsed and outputted by codestyle. The name of the string, as parsed and outputted by codestyle.
""" """
classes: Set[Type3Class] classes: Set['Type3Class']
""" """
The type classes that this type implements The type classes that this type implements
""" """
def __init__(self, name: str, classes: Iterable[Type3Class]) -> None: def __init__(self, name: str, classes: Iterable['Type3Class']) -> None:
self.name = name self.name = name
self.classes = set(classes) self.classes = set(classes)
@ -84,9 +62,6 @@ class Type3:
return str(self) return str(self)
def __eq__(self, other: Any) -> bool: def __eq__(self, other: Any) -> bool:
if isinstance(other, PlaceholderForType):
return False
if not isinstance(other, Type3): if not isinstance(other, Type3):
raise NotImplementedError raise NotImplementedError
@ -101,19 +76,15 @@ class Type3:
def __bool__(self) -> bool: def __bool__(self) -> bool:
raise NotImplementedError raise NotImplementedError
class PrimitiveType3(Type3): class IntType3(KindArgument):
""" """
Intermediate class to tell primitive types from others Sometimes you can have an int on the type level, e.g. when using static arrays
"""
__slots__ = ()
class IntType3(Type3):
"""
Sometimes you can have an int as type, e.g. when using static arrays
This is not the same as an int on the language level. This is not the same as an int on the language level.
[1.0, 1.2] :: Float[2] :: * -> Int -> * [1.0, 1.2] :: f32[2] :: * -> Int -> *
That is to say, you can create a static array of size two with each element
a f32 using f32[2].
""" """
__slots__ = ('value', ) __slots__ = ('value', )
@ -121,15 +92,13 @@ class IntType3(Type3):
value: int value: int
def __init__(self, value: int) -> None: def __init__(self, value: int) -> None:
super().__init__(str(value), [])
self.value = value self.value = value
def __eq__(self, other: Any) -> bool: def __eq__(self, other: Any) -> bool:
if isinstance(other, IntType3): if isinstance(other, IntType3):
return self.value == other.value return self.value == other.value
if isinstance(other, Type3): if isinstance(other, KindArgument):
return False return False
raise NotImplementedError raise NotImplementedError
@ -137,53 +106,6 @@ class IntType3(Type3):
def __hash__(self) -> int: def __hash__(self) -> int:
return hash(self.value) return hash(self.value)
class PlaceholderForType:
"""
A placeholder type, for when we don't know the final type yet
"""
__slots__ = ('update_on_substitution', 'resolve_as', )
update_on_substitution: List[ExpressionProtocol]
resolve_as: Optional[Type3]
def __init__(self, update_on_substitution: Iterable[ExpressionProtocol]) -> None:
self.update_on_substitution = [*update_on_substitution]
self.resolve_as = None
def __repr__(self) -> str:
uos = ', '.join(repr(x) for x in self.update_on_substitution)
return f'PlaceholderForType({id(self)}, [{uos}])'
def __str__(self) -> str:
return f'PhFT_{id(self)}'
def __format__(self, format_spec: str) -> str:
if format_spec != 's':
raise TypeError('unsupported format string passed to Type3.__format__')
return str(self)
def __eq__(self, other: Any) -> bool:
if isinstance(other, Type3):
return False
if not isinstance(other, PlaceholderForType):
raise NotImplementedError
return self is other
def __ne__(self, other: Any) -> bool:
return not self.__eq__(other)
def __hash__(self) -> int:
return 0 # Valid but performs badly
def __bool__(self) -> bool:
raise NotImplementedError
Type3OrPlaceholder = Union[Type3, PlaceholderForType]
T = TypeVar('T') T = TypeVar('T')
class TypeConstructor(Generic[T]): class TypeConstructor(Generic[T]):
@ -197,28 +119,28 @@ class TypeConstructor(Generic[T]):
The name of the type constructor The name of the type constructor
""" """
classes: Set[Type3Class] classes: Set['Type3Class']
""" """
The type classes that this constructor implements The type classes that this constructor implements
""" """
type_classes: Set[Type3Class] type_classes: Set['Type3Class']
""" """
The type classes that the constructed types implement The type classes that the constructed types implement
""" """
_cache: dict[T, PrimitiveType3] _cache: dict[T, Type3]
""" """
When constructing a type with the same arguments, When constructing a type with the same arguments,
it should produce the exact same result. it should produce the exact same result.
""" """
_reverse_cache: dict[PrimitiveType3, T] _reverse_cache: dict[Type3, T]
""" """
Sometimes we need to know the key that created a type. Sometimes we need to know the key that created a type.
""" """
def __init__(self, name: str, classes: Iterable[Type3Class], type_classes: Iterable[Type3Class]) -> None: def __init__(self, name: str, classes: Iterable['Type3Class'], type_classes: Iterable['Type3Class']) -> None:
self.name = name self.name = name
self.classes = set(classes) self.classes = set(classes)
self.type_classes = set(type_classes) self.type_classes = set(type_classes)
@ -227,186 +149,123 @@ class TypeConstructor(Generic[T]):
self._reverse_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
"""
raise NotImplementedError raise NotImplementedError
def did_construct(self, typ: PrimitiveType3) -> T | None: def did_construct(self, typ: Type3) -> T | None:
"""
Was the given type constructed by this constructor?
If so, which arguments where used?
"""
return self._reverse_cache.get(typ) return self._reverse_cache.get(typ)
def construct(self, key: T) -> PrimitiveType3: def construct(self, key: T) -> Type3:
"""
Constructs the type by applying the given arguments to this
constructor.
"""
result = self._cache.get(key, None) result = self._cache.get(key, None)
if result is None: if result is None:
self._cache[key] = result = PrimitiveType3(self.make_name(key), self.type_classes) self._cache[key] = result = Type3(self.make_name(key), self.type_classes)
self._reverse_cache[result] = key self._reverse_cache[result] = key
return result return result
class TypeConstructor_Type(TypeConstructor[PrimitiveType3]): class TypeConstructor_Type(TypeConstructor[Type3]):
""" """
Base class type constructors of kind: * -> * Base class type constructors of kind: * -> *
""" """
__slots__ = () __slots__ = ()
def __call__(self, arg: PrimitiveType3) -> PrimitiveType3: def __call__(self, arg: Type3) -> Type3:
raise NotImplementedError raise NotImplementedError
class TypeConstructor_TypeInt(TypeConstructor[Tuple[PrimitiveType3, IntType3]]): class TypeConstructor_TypeInt(TypeConstructor[Tuple[Type3, IntType3]]):
""" """
Base class type constructors of kind: * -> Int -> * Base class type constructors of kind: * -> Int -> *
Notably, static array.
""" """
__slots__ = () __slots__ = ()
def make_name(self, key: Tuple[PrimitiveType3, 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: PrimitiveType3, arg1: IntType3) -> PrimitiveType3: def __call__(self, arg0: Type3, arg1: IntType3) -> Type3:
return self.construct((arg0, arg1)) return self.construct((arg0, arg1))
class TypeConstructor_TypeStar(TypeConstructor[Tuple[PrimitiveType3, ...]]): class TypeConstructor_TypeStar(TypeConstructor[Tuple[Type3, ...]]):
""" """
Base class type constructors of variadic kind Base class type constructors of variadic kind
Notably, tuple.
""" """
def __call__(self, *args: PrimitiveType3) -> PrimitiveType3: def __call__(self, *args: Type3) -> Type3:
key: Tuple[PrimitiveType3, ...] = tuple(args) key: Tuple[Type3, ...] = tuple(args)
return self.construct(key) return self.construct(key)
class TypeConstructor_StaticArray(TypeConstructor_TypeInt): class TypeConstructor_StaticArray(TypeConstructor_TypeInt):
def make_name(self, key: Tuple[PrimitiveType3, 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}]'
class TypeConstructor_Tuple(TypeConstructor_TypeStar): class TypeConstructor_Tuple(TypeConstructor_TypeStar):
def make_name(self, key: Tuple[PrimitiveType3, ...]) -> 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 StructType3(PrimitiveType3): class TypeConstructor_Struct:
""" """
A Type3 struct with named members Base class for type construtors
""" """
__slots__ = ('name', 'members', ) __slots__ = ('name', 'classes', 'type_classes', '_cache', '_reverse_cache')
name: str name: str
""" """
The structs fully qualified name The name of the type constructor
""" """
members: Dict[str, Type3] classes: Set['Type3Class']
""" """
The struct's field definitions The type classes that this constructor implements
""" """
def __init__(self, name: str, members: Dict[str, Type3]) -> None: type_classes: Set['Type3Class']
super().__init__(name, []) """
The type classes that the constructed types implement
"""
_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, classes: Iterable['Type3Class'], type_classes: Iterable['Type3Class']) -> None:
self.name = name self.name = name
self.members = dict(members) self.classes = set(classes)
self.type_classes = set(type_classes)
def __repr__(self) -> str: self._cache = {}
return f'StructType3(repr({self.name}), repr({self.members}))' self._reverse_cache = {}
none = PrimitiveType3('none', []) def did_construct(self, typ: Type3) -> dict[str, Type3] | None:
""" """
The none type, for when functions simply don't return anything. e.g., IO(). Was the given type constructed by this constructor?
"""
bool_ = PrimitiveType3('bool', []) If so, which arguments where used?
""" """
The bool type, either True or False return self._reverse_cache.get(typ)
Suffixes with an underscores, as it's a Python builtin def __call__(self, name: str, args: dict[str, Type3], classes: Set['Type3Class']) -> Type3:
""" result = Type3(name, classes | self.type_classes)
self._reverse_cache[result] = args
u8 = PrimitiveType3('u8', [Bits, Eq, Ord]) return result
"""
The unsigned 8-bit integer type.
Operations on variables employ modular arithmetic, with modulus 2^8.
"""
u32 = PrimitiveType3('u32', [Bits, Eq, Integral, NatNum, Ord])
"""
The unsigned 32-bit integer type.
Operations on variables employ modular arithmetic, with modulus 2^32.
"""
u64 = PrimitiveType3('u64', [Bits, Eq, Integral, NatNum, Ord])
"""
The unsigned 64-bit integer type.
Operations on variables employ modular arithmetic, with modulus 2^64.
"""
i8 = PrimitiveType3('i8', [Eq, Ord])
"""
The signed 8-bit integer type.
Operations on variables employ modular arithmetic, with modulus 2^8, but
with the middel point being 0.
"""
i32 = PrimitiveType3('i32', [Eq, Integral, IntNum, NatNum, Ord])
"""
The unsigned 32-bit integer type.
Operations on variables employ modular arithmetic, with modulus 2^32, but
with the middel point being 0.
"""
i64 = PrimitiveType3('i64', [Eq, Integral, IntNum, NatNum, Ord])
"""
The unsigned 64-bit integer type.
Operations on variables employ modular arithmetic, with modulus 2^64, but
with the middel point being 0.
"""
f32 = PrimitiveType3('f32', [Eq, Floating, Fractional, IntNum, NatNum, Ord])
"""
A 32-bits IEEE 754 float, of 32 bits width.
"""
f64 = PrimitiveType3('f64', [Eq, Floating, Fractional, IntNum, NatNum, Ord])
"""
A 32-bits IEEE 754 float, of 64 bits width.
"""
bytes_ = PrimitiveType3('bytes', [])
"""
This is a runtime-determined length piece of memory that can be indexed at runtime.
"""
static_array = TypeConstructor_StaticArray('static_array', [], [
Eq,
InternalPassAsPointer,
])
"""
A type constructor.
Any static array is a fixed length piece of memory that can be indexed at runtime.
It should be applied with one argument. It has a runtime-dynamic length
of the same type repeated.
"""
tuple_ = TypeConstructor_Tuple('tuple', [], [
InternalPassAsPointer,
])
"""
This is a fixed length piece of memory.
It should be applied with zero or more arguments. It has a compile time
determined length, and each argument can be different.
"""
LOOKUP_TABLE: Dict[str, PrimitiveType3] = {
'none': none,
'bool': bool_,
'u8': u8,
'u32': u32,
'u64': u64,
'i8': i8,
'i32': i32,
'i64': i64,
'f32': f32,
'f64': f64,
'bytes': bytes_,
}

View File

@ -2,10 +2,10 @@ import struct
import sys import sys
from typing import Any, Generator, Iterable, List, TextIO, Union from typing import Any, Generator, Iterable, List, TextIO, Union
from phasm import compiler from phasm import compiler, prelude
from phasm.codestyle import phasm_render from phasm.codestyle import phasm_render
from phasm.runtime import calculate_alloc_size from phasm.runtime import calculate_alloc_size
from phasm.type3 import typeclasses as type3classes from phasm.type3 import placeholders as type3placeholders
from phasm.type3 import types as type3types from phasm.type3 import types as type3types
from . import runners from . import runners
@ -65,43 +65,44 @@ class Suite:
runner.interpreter_dump_memory(sys.stderr) runner.interpreter_dump_memory(sys.stderr)
for arg, arg_typ in zip(args, func_args): for arg, arg_typ in zip(args, func_args):
assert not isinstance(arg_typ, type3types.PlaceholderForType), \ assert not isinstance(arg_typ, type3placeholders.PlaceholderForType), \
'Cannot call polymorphic function from outside' 'Cannot call polymorphic function from outside'
if arg_typ in (type3types.u8, type3types.u32, type3types.u64, ): if arg_typ in (prelude.u8, prelude.u32, prelude.u64, ):
assert isinstance(arg, int) assert isinstance(arg, int)
wasm_args.append(arg) wasm_args.append(arg)
continue continue
if arg_typ in (type3types.i8, type3types.i32, type3types.i64, ): if arg_typ in (prelude.i8, prelude.i32, prelude.i64, ):
assert isinstance(arg, int) assert isinstance(arg, int)
wasm_args.append(arg) wasm_args.append(arg)
continue continue
if arg_typ in (type3types.f32, type3types.f64, ): if arg_typ in (prelude.f32, prelude.f64, ):
assert isinstance(arg, float) assert isinstance(arg, float)
wasm_args.append(arg) wasm_args.append(arg)
continue continue
if arg_typ is type3types.bytes_: if arg_typ is prelude.bytes_:
adr = _allocate_memory_stored_value(runner, arg_typ, arg) adr = _allocate_memory_stored_value(runner, arg_typ, arg)
wasm_args.append(adr) wasm_args.append(adr)
continue continue
assert isinstance(arg_typ, type3types.PrimitiveType3) assert isinstance(arg_typ, type3types.Type3)
sa_args = type3types.static_array.did_construct(arg_typ) sa_args = prelude.static_array.did_construct(arg_typ)
if sa_args is not None: if sa_args is not None:
adr = _allocate_memory_stored_value(runner, arg_typ, arg) adr = _allocate_memory_stored_value(runner, arg_typ, arg)
wasm_args.append(adr) wasm_args.append(adr)
continue continue
tp_args = type3types.tuple_.did_construct(arg_typ) tp_args = prelude.tuple_.did_construct(arg_typ)
if tp_args is not None: if tp_args is not None:
adr = _allocate_memory_stored_value(runner, arg_typ, arg) adr = _allocate_memory_stored_value(runner, arg_typ, arg)
wasm_args.append(adr) wasm_args.append(adr)
continue continue
if isinstance(arg_typ, type3types.StructType3): st_args = prelude.struct.did_construct(arg_typ)
if st_args is not None:
adr = _allocate_memory_stored_value(runner, arg_typ, arg) adr = _allocate_memory_stored_value(runner, arg_typ, arg)
wasm_args.append(adr) wasm_args.append(adr)
continue continue
@ -145,41 +146,39 @@ def _write_memory_stored_value(
val_typ: type3types.Type3, val_typ: type3types.Type3,
val: Any, val: Any,
) -> int: ) -> int:
if val_typ is type3types.bytes_: if val_typ is prelude.bytes_:
adr2 = _allocate_memory_stored_value(runner, val_typ, val) 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
if isinstance(val_typ, type3types.StructType3): st_args = prelude.struct.did_construct(val_typ)
if st_args is not None:
adr2 = _allocate_memory_stored_value(runner, val_typ, val) 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
if isinstance(val_typ, type3types.PrimitiveType3): sa_args = prelude.static_array.did_construct(val_typ)
sa_args = type3types.static_array.did_construct(val_typ) if sa_args is not None:
if sa_args is not None: adr2 = _allocate_memory_stored_value(runner, val_typ, val)
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
tp_args = type3types.tuple_.did_construct(val_typ) tp_args = prelude.tuple_.did_construct(val_typ)
if tp_args is not None: if tp_args is not None:
adr2 = _allocate_memory_stored_value(runner, val_typ, val) 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
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)
raise NotImplementedError(val_typ, val)
def _allocate_memory_stored_value( def _allocate_memory_stored_value(
runner: runners.RunnerBase, runner: runners.RunnerBase,
val_typ: type3types.Type3, val_typ: type3types.Type3,
val: Any val: Any
) -> int: ) -> int:
if val_typ is type3types.bytes_: 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))
@ -189,8 +188,7 @@ def _allocate_memory_stored_value(
runner.interpreter_write_memory(adr + 4, val) runner.interpreter_write_memory(adr + 4, val)
return adr return adr
assert isinstance(val_typ, type3types.PrimitiveType3) sa_args = prelude.static_array.did_construct(val_typ)
sa_args = type3types.static_array.did_construct(val_typ)
if sa_args is not None: if sa_args is not None:
assert isinstance(val, tuple) assert isinstance(val, tuple)
@ -211,7 +209,7 @@ def _allocate_memory_stored_value(
val_el_typ: type3types.Type3 val_el_typ: type3types.Type3
tp_args = type3types.tuple_.did_construct(val_typ) tp_args = prelude.tuple_.did_construct(val_typ)
if tp_args is not None: if tp_args is not None:
assert isinstance(val, tuple) assert isinstance(val, tuple)
@ -224,12 +222,13 @@ def _allocate_memory_stored_value(
offset = adr offset = adr
for val_el_val, val_el_typ in zip(val, tp_args): for val_el_val, val_el_typ in zip(val, tp_args):
assert not isinstance(val_el_typ, type3types.PlaceholderForType) assert not isinstance(val_el_typ, type3placeholders.PlaceholderForType)
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
if isinstance(val_typ, type3types.StructType3): st_args = prelude.struct.did_construct(val_typ)
if st_args is not None:
assert isinstance(val, dict) assert isinstance(val, dict)
alloc_size = calculate_alloc_size(val_typ) alloc_size = calculate_alloc_size(val_typ)
@ -237,11 +236,11 @@ def _allocate_memory_stored_value(
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')
assert list(val.keys()) == list(val_typ.members.keys()) assert list(val.keys()) == list(st_args)
offset = adr offset = adr
for val_el_name, val_el_typ in val_typ.members.items(): for val_el_name, val_el_typ in st_args.items():
assert not isinstance(val_el_typ, type3types.PlaceholderForType) assert not isinstance(val_el_typ, type3placeholders.PlaceholderForType)
val_el_val = val[val_el_name] val_el_val = val[val_el_name]
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)
@ -256,18 +255,18 @@ def _load_memory_stored_returned_value(
) -> Any: ) -> Any:
ret_type3 = runner.phasm_ast.functions[func_name].returns_type3 ret_type3 = runner.phasm_ast.functions[func_name].returns_type3
if ret_type3 is type3types.none: if ret_type3 is prelude.none:
return None return None
if ret_type3 is type3types.bool_: if ret_type3 is prelude.bool_:
assert isinstance(wasm_value, int), wasm_value assert isinstance(wasm_value, int), wasm_value
return 0 != wasm_value return 0 != wasm_value
if ret_type3 in (type3types.i8, type3types.i32, type3types.i64): if ret_type3 in (prelude.i8, prelude.i32, prelude.i64):
assert isinstance(wasm_value, int), wasm_value assert isinstance(wasm_value, int), wasm_value
return wasm_value return wasm_value
if ret_type3 in (type3types.u8, type3types.u32, type3types.u64): if ret_type3 in (prelude.u8, prelude.u32, prelude.u64):
assert isinstance(wasm_value, int), wasm_value assert isinstance(wasm_value, int), wasm_value
if wasm_value < 0: if wasm_value < 0:
@ -284,98 +283,100 @@ def _load_memory_stored_returned_value(
return wasm_value return wasm_value
if ret_type3 in (type3types.f32, type3types.f64, ): if ret_type3 in (prelude.f32, prelude.f64, ):
assert isinstance(wasm_value, float), wasm_value assert isinstance(wasm_value, float), wasm_value
return wasm_value return wasm_value
if ret_type3 is type3types.bytes_: if ret_type3 is prelude.bytes_:
assert isinstance(wasm_value, int), wasm_value assert isinstance(wasm_value, int), wasm_value
return _load_bytes_from_address(runner, ret_type3, wasm_value) return _load_bytes_from_address(runner, ret_type3, wasm_value)
assert isinstance(ret_type3, type3types.PrimitiveType3) # Type hint assert isinstance(ret_type3, type3types.Type3) # Type hint
sa_args = type3types.static_array.did_construct(ret_type3) sa_args = prelude.static_array.did_construct(ret_type3)
if sa_args is not None: if sa_args is not None:
assert isinstance(wasm_value, int), wasm_value assert isinstance(wasm_value, int), wasm_value
return _load_static_array_from_address(runner, sa_args[0], sa_args[1], wasm_value) return _load_static_array_from_address(runner, sa_args[0], sa_args[1], wasm_value)
tp_args = type3types.tuple_.did_construct(ret_type3) tp_args = prelude.tuple_.did_construct(ret_type3)
if tp_args is not None: if tp_args is not None:
assert isinstance(wasm_value, int), wasm_value assert isinstance(wasm_value, int), wasm_value
return _load_tuple_from_address(runner, tp_args, wasm_value) return _load_tuple_from_address(runner, tp_args, wasm_value)
if isinstance(ret_type3, type3types.StructType3): st_args = prelude.struct.did_construct(ret_type3)
return _load_struct_from_address(runner, ret_type3, wasm_value) if st_args is not None:
return _load_struct_from_address(runner, st_args, wasm_value)
raise NotImplementedError(ret_type3, 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 type3types.u8: if typ is prelude.u8:
# See compiler.py, LOAD_STORE_TYPE_MAP and module_data_u8 # See compiler.py, LOAD_STORE_TYPE_MAP and module_data_u8
assert len(inp) == 4 assert len(inp) == 4
return struct.unpack('<I', inp)[0] return struct.unpack('<I', inp)[0]
if typ is type3types.u32: if typ is prelude.u32:
assert len(inp) == 4 assert len(inp) == 4
return struct.unpack('<I', inp)[0] return struct.unpack('<I', inp)[0]
if typ is type3types.u64: if typ is prelude.u64:
assert len(inp) == 8 assert len(inp) == 8
return struct.unpack('<Q', inp)[0] return struct.unpack('<Q', inp)[0]
if typ is type3types.i8: if typ is prelude.i8:
# See compiler.py, LOAD_STORE_TYPE_MAP and module_data_i8 # See compiler.py, LOAD_STORE_TYPE_MAP and module_data_i8
assert len(inp) == 4 assert len(inp) == 4
return struct.unpack('<i', inp)[0] return struct.unpack('<i', inp)[0]
if typ is type3types.i32: if typ is prelude.i32:
assert len(inp) == 4 assert len(inp) == 4
return struct.unpack('<i', inp)[0] return struct.unpack('<i', inp)[0]
if typ is type3types.i64: if typ is prelude.i64:
assert len(inp) == 8 assert len(inp) == 8
return struct.unpack('<q', inp)[0] return struct.unpack('<q', inp)[0]
if typ is type3types.f32: if typ is prelude.f32:
assert len(inp) == 4 assert len(inp) == 4
return struct.unpack('<f', inp)[0] return struct.unpack('<f', inp)[0]
if typ is type3types.f64: if typ is prelude.f64:
assert len(inp) == 8 assert len(inp) == 8
return struct.unpack('<d', inp)[0] return struct.unpack('<d', inp)[0]
if typ is type3types.bytes_: if typ is prelude.bytes_:
# Note: For bytes, inp should contain a 4 byte pointer # Note: For bytes, 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_bytes_from_address(runner, typ, adr) return _load_bytes_from_address(runner, typ, adr)
if type3classes.InternalPassAsPointer in typ.classes: if prelude.InternalPassAsPointer in typ.classes:
# 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]
assert isinstance(typ, type3types.PrimitiveType3) assert isinstance(typ, type3types.Type3)
sa_args = type3types.static_array.did_construct(typ) sa_args = prelude.static_array.did_construct(typ)
if sa_args is not None: if sa_args is not None:
sa_type, sa_len = sa_args sa_type, sa_len = sa_args
return _load_static_array_from_address(runner, sa_type, sa_len, adr) return _load_static_array_from_address(runner, sa_type, sa_len, adr)
tp_args = type3types.tuple_.did_construct(typ) tp_args = prelude.tuple_.did_construct(typ)
if tp_args is not None: if tp_args is not None:
return _load_tuple_from_address(runner, tp_args, adr) return _load_tuple_from_address(runner, tp_args, adr)
if isinstance(typ, type3types.StructType3): st_args = prelude.struct.did_construct(typ)
# Note: For structs, inp should contain a 4 byte pointer if st_args is not None:
assert len(inp) == 4 # Note: For structs, inp should contain a 4 byte pointer
adr = struct.unpack('<I', inp)[0] assert len(inp) == 4
adr = struct.unpack('<I', inp)[0]
return _load_struct_from_address(runner, typ, adr) return _load_struct_from_address(runner, st_args, adr)
raise NotImplementedError(typ, inp) raise NotImplementedError(typ, inp)
@ -393,10 +394,10 @@ 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(runner: runners.RunnerBase, sub_typ: type3types.PrimitiveType3, len_typ: type3types.IntType3, adr: int) -> Any: def _load_static_array_from_address(runner: runners.RunnerBase, sub_typ: type3types.Type3, len_typ: type3types.IntType3, adr: int) -> Any:
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')
assert not isinstance(sub_typ, type3types.PlaceholderForType) assert not isinstance(sub_typ, type3placeholders.PlaceholderForType)
assert isinstance(len_typ, type3types.IntType3) assert isinstance(len_typ, type3types.IntType3)
sa_len = len_typ.value sa_len = len_typ.value
@ -411,7 +412,7 @@ def _load_static_array_from_address(runner: runners.RunnerBase, sub_typ: type3ty
for arg_bytes in _split_read_bytes(read_bytes, arg_sizes) for arg_bytes in _split_read_bytes(read_bytes, arg_sizes)
) )
def _load_tuple_from_address(runner: runners.RunnerBase, typ_args: tuple[type3types.PrimitiveType3, ...], adr: int) -> Any: def _load_tuple_from_address(runner: runners.RunnerBase, typ_args: tuple[type3types.Type3, ...], adr: int) -> Any:
sys.stderr.write(f'Reading 0x{adr:08x} tuple {len(typ_args)}\n') sys.stderr.write(f'Reading 0x{adr:08x} tuple {len(typ_args)}\n')
arg_sizes = [ arg_sizes = [
@ -426,19 +427,13 @@ def _load_tuple_from_address(runner: runners.RunnerBase, typ_args: tuple[type3ty
for arg_typ, arg_bytes in zip(typ_args, _split_read_bytes(read_bytes, arg_sizes)) for arg_typ, arg_bytes in zip(typ_args, _split_read_bytes(read_bytes, arg_sizes))
) )
def _load_struct_from_address(runner: runners.RunnerBase, typ: type3types.Type3, adr: int) -> Any: def _load_struct_from_address(runner: runners.RunnerBase, st_args: dict[str, type3types.Type3], adr: int) -> Any:
sys.stderr.write(f'Reading 0x{adr:08x} {typ:s}\n') sys.stderr.write(f'Reading 0x{adr:08x} struct {list(st_args)}\n')
assert isinstance(typ, type3types.StructType3) name_list = list(st_args)
name_list = list(typ.members) typ_list = list(st_args.values())
assert len(typ_list) == len(st_args)
typ_list = [
x
for x in typ.members.values()
if not isinstance(x, type3types.PlaceholderForType)
]
assert len(typ_list) == len(typ.members)
arg_sizes = [ arg_sizes = [
calculate_alloc_size(x, is_member=True) calculate_alloc_size(x, is_member=True)