Compare commits
17 Commits
c2eaaa7e4a
...
23f9e60378
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
23f9e60378 | ||
|
|
ac4b46bbe7 | ||
|
|
67af569448 | ||
|
|
df5c1911bf | ||
|
|
b5f0fda133 | ||
|
|
6c627bca01 | ||
|
|
78c98b1e61 | ||
|
|
f8d107f4fa | ||
|
|
6b66935c67 | ||
|
|
d9a08cf0f7 | ||
|
|
f6cb1a8c1d | ||
|
|
45c38d5f88 | ||
|
|
42cb38d67d | ||
|
|
bee0c845a8 | ||
|
|
44b95af4ba | ||
|
|
1da1adac9f | ||
|
|
a2e1dfd799 |
24
TODO.md
24
TODO.md
@ -11,28 +11,22 @@
|
||||
- Implement a FizzBuzz example
|
||||
- Also, check the codes for FIXME and TODO
|
||||
- Allocation is done using pointers for members, is this desired?
|
||||
- Functions don't seem to be a thing on typing level yet?
|
||||
- static_array and tuple should probably not be PrimitiveType3, but instead subclass AppliedType3?
|
||||
- See if we want to replace Fractional with Real, and add Rational, Irrationl, Algebraic, Transendental
|
||||
- Implement q32? q64? Two i32/i64 divided?
|
||||
- Does Subscript do what we want? It's a language feature rather a normal typed thing. How would you implement your own Subscript-able type?
|
||||
- Clean up Subscript implementation - it's half implemented in the compiler. Makes more sense to move more parts to stdlib_types.
|
||||
- Have a set of rules or guidelines for the constraint comments, they're messy.
|
||||
- Why is expression_subscript_bytes using a helper method but expression_subscript_static_array is not?
|
||||
|
||||
- Find pytest.mark.skip
|
||||
- There's a weird resolve_as reference in calculate_alloc_size
|
||||
- Either there should be more of them or less
|
||||
- At first glance, looks like failure in the typing system
|
||||
- Related to the FIXME in phasm_type3?
|
||||
- Related: Parser is putting stuff in ModuleDataBlock
|
||||
- Casting is not implemented except u32 which is special cased
|
||||
- Parser is putting stuff in ModuleDataBlock
|
||||
- Surely the compiler should build data blocks
|
||||
- Make prelude more an actual thing
|
||||
- Merge in type3types.LOOKUP_TABLE
|
||||
- Merge in compiler.INSTANCES
|
||||
- Make it less build in - have a environment class of some kind
|
||||
- Implemented Bounded: https://hackage.haskell.org/package/base-4.21.0.0/docs/Prelude.html#t:Bounded
|
||||
- Try to implement the min and max functions using select
|
||||
- Filter out methods that aren't used; other the other way around (easier?) only add __ methods when needed
|
||||
- Move UnaryOp.operator into type class methods
|
||||
- Move foldr into type class methods
|
||||
- PrimitiveType is just the type, nothing primitive about it (change the name). u32 are still basic types or something.
|
||||
- Clean up Subscript implementation - it's half implemented in the compiler. Makes more sense to move more parts to stdlib_types.
|
||||
- Functions don't seem to be a thing on typing level yet?
|
||||
- Related to the FIXME in phasm_type3?
|
||||
- Type constuctor should also be able to constuct placeholders - somehow.
|
||||
- Make struct a type constructor?
|
||||
- Add InternalPassAsPointer to Struct?
|
||||
|
||||
@ -51,7 +51,7 @@ _CRC32_Table: u32[256] = (
|
||||
)
|
||||
|
||||
def _crc32_f(crc: u32, byt: u8) -> u32:
|
||||
return (crc >> 8) ^ _CRC32_Table[(crc & 0xFF) ^ u32(byt)]
|
||||
return (crc >> 8) ^ _CRC32_Table[(crc & 0xFF) ^ extend(byt)]
|
||||
|
||||
@exported
|
||||
def crc32(data: bytes) -> u32:
|
||||
|
||||
@ -6,8 +6,7 @@ It's intented to be a "any color, as long as it's black" kind of renderer
|
||||
from typing import Generator
|
||||
|
||||
from . import ourlang, prelude
|
||||
from .type3.placeholders import TYPE3_ASSERTION_ERROR, Type3OrPlaceholder
|
||||
from .type3.types import Type3
|
||||
from .type3.types import Type3, TypeApplication_Struct
|
||||
|
||||
|
||||
def phasm_render(inp: ourlang.Module) -> str:
|
||||
@ -18,12 +17,10 @@ def phasm_render(inp: ourlang.Module) -> str:
|
||||
|
||||
Statements = Generator[str, None, None]
|
||||
|
||||
def type3(inp: Type3OrPlaceholder) -> str:
|
||||
def type3(inp: Type3) -> str:
|
||||
"""
|
||||
Render: type's name
|
||||
"""
|
||||
assert isinstance(inp, Type3), TYPE3_ASSERTION_ERROR
|
||||
|
||||
if inp is prelude.none:
|
||||
return 'None'
|
||||
|
||||
@ -33,11 +30,10 @@ def struct_definition(inp: ourlang.StructDefinition) -> str:
|
||||
"""
|
||||
Render: TypeStruct's definition
|
||||
"""
|
||||
st_args = prelude.struct.did_construct(inp.struct_type3)
|
||||
assert st_args is not None
|
||||
assert isinstance(inp.struct_type3.application, TypeApplication_Struct)
|
||||
|
||||
result = f'class {inp.struct_type3.name}:\n'
|
||||
for mem, typ in st_args.items():
|
||||
for mem, typ in inp.struct_type3.application.arguments:
|
||||
result += f' {mem}: {type3(typ)}\n'
|
||||
|
||||
return result
|
||||
@ -67,7 +63,7 @@ def expression(inp: ourlang.Expression) -> str:
|
||||
) + ', )'
|
||||
|
||||
if isinstance(inp, ourlang.ConstantStruct):
|
||||
return inp.struct_name + '(' + ', '.join(
|
||||
return inp.struct_type3.name + '(' + ', '.join(
|
||||
expression(x)
|
||||
for x in inp.value
|
||||
) + ')'
|
||||
@ -75,16 +71,6 @@ def expression(inp: ourlang.Expression) -> str:
|
||||
if isinstance(inp, ourlang.VariableReference):
|
||||
return str(inp.variable.name)
|
||||
|
||||
if isinstance(inp, ourlang.UnaryOp):
|
||||
if inp.operator == 'cast':
|
||||
mtyp = type3(inp.type3)
|
||||
if mtyp is None:
|
||||
raise NotImplementedError(f'Casting to type {inp.type_var}')
|
||||
|
||||
return f'{mtyp}({expression(inp.right)})'
|
||||
|
||||
return f'{inp.operator}{expression(inp.right)}'
|
||||
|
||||
if isinstance(inp, ourlang.BinaryOp):
|
||||
return f'{expression(inp.left)} {inp.operator.name} {expression(inp.right)}'
|
||||
|
||||
|
||||
@ -2,18 +2,28 @@
|
||||
This module contains the code to convert parsed Ourlang into WebAssembly code
|
||||
"""
|
||||
import struct
|
||||
from typing import Dict, List, Optional
|
||||
from typing import List, Optional
|
||||
|
||||
from . import ourlang, prelude, wasm
|
||||
from .runtime import calculate_alloc_size, calculate_member_offset
|
||||
from .stdlib import alloc as stdlib_alloc
|
||||
from .stdlib import types as stdlib_types
|
||||
from .type3 import functions as type3functions
|
||||
from .type3 import placeholders as type3placeholders
|
||||
from .type3 import typeclasses as type3classes
|
||||
from .type3 import types as type3types
|
||||
from .type3.functions import TypeVariable
|
||||
from .type3.routers import NoRouteForTypeException, TypeApplicationRouter
|
||||
from .type3.typeclasses import Type3ClassMethod
|
||||
from .type3.types import (
|
||||
IntType3,
|
||||
Type3,
|
||||
TypeApplication_Struct,
|
||||
TypeApplication_TypeInt,
|
||||
TypeApplication_TypeStar,
|
||||
TypeConstructor_StaticArray,
|
||||
TypeConstructor_Tuple,
|
||||
)
|
||||
from .wasmgenerator import Generator as WasmGenerator
|
||||
|
||||
TYPE3_ASSERTION_ERROR = 'You must call phasm_type3 after calling phasm_parse before your program can be compiled'
|
||||
|
||||
LOAD_STORE_TYPE_MAP = {
|
||||
'i8': 'i32', # Have to use an u32, since there is no native i8 type
|
||||
'u8': 'i32', # Have to use an u32, since there is no native u8 type
|
||||
@ -28,217 +38,6 @@ LOAD_STORE_TYPE_MAP = {
|
||||
'bytes': 'i32', # Bytes are passed around as pointers
|
||||
}
|
||||
|
||||
# For now this is nice & clean, but this will get messy quick
|
||||
# Especially once we get functions with polymorphying applied types
|
||||
INSTANCES = {
|
||||
prelude.Eq.operators['==']: {
|
||||
'a=u8': stdlib_types.u8_eq_equals,
|
||||
'a=u32': stdlib_types.u32_eq_equals,
|
||||
'a=u64': stdlib_types.u64_eq_equals,
|
||||
'a=i8': stdlib_types.i8_eq_equals,
|
||||
'a=i32': stdlib_types.i32_eq_equals,
|
||||
'a=i64': stdlib_types.i64_eq_equals,
|
||||
'a=f32': stdlib_types.f32_eq_equals,
|
||||
'a=f64': stdlib_types.f64_eq_equals,
|
||||
},
|
||||
prelude.Eq.operators['!=']: {
|
||||
'a=u8': stdlib_types.u8_eq_not_equals,
|
||||
'a=u32': stdlib_types.u32_eq_not_equals,
|
||||
'a=u64': stdlib_types.u64_eq_not_equals,
|
||||
'a=i8': stdlib_types.i8_eq_not_equals,
|
||||
'a=i32': stdlib_types.i32_eq_not_equals,
|
||||
'a=i64': stdlib_types.i64_eq_not_equals,
|
||||
'a=f32': stdlib_types.f32_eq_not_equals,
|
||||
'a=f64': stdlib_types.f64_eq_not_equals,
|
||||
},
|
||||
prelude.Ord.methods['min']: {
|
||||
'a=u8': stdlib_types.u8_ord_min,
|
||||
'a=u32': stdlib_types.u32_ord_min,
|
||||
'a=u64': stdlib_types.u64_ord_min,
|
||||
'a=i8': stdlib_types.i8_ord_min,
|
||||
'a=i32': stdlib_types.i32_ord_min,
|
||||
'a=i64': stdlib_types.i64_ord_min,
|
||||
'a=f32': stdlib_types.f32_ord_min,
|
||||
'a=f64': stdlib_types.f64_ord_min,
|
||||
},
|
||||
prelude.Ord.methods['max']: {
|
||||
'a=u8': stdlib_types.u8_ord_max,
|
||||
'a=u32': stdlib_types.u32_ord_max,
|
||||
'a=u64': stdlib_types.u64_ord_max,
|
||||
'a=i8': stdlib_types.i8_ord_max,
|
||||
'a=i32': stdlib_types.i32_ord_max,
|
||||
'a=i64': stdlib_types.i64_ord_max,
|
||||
'a=f32': stdlib_types.f32_ord_max,
|
||||
'a=f64': stdlib_types.f64_ord_max,
|
||||
},
|
||||
prelude.Ord.operators['<']: {
|
||||
'a=u8': stdlib_types.u8_ord_less_than,
|
||||
'a=u32': stdlib_types.u32_ord_less_than,
|
||||
'a=u64': stdlib_types.u64_ord_less_than,
|
||||
'a=i8': stdlib_types.i8_ord_less_than,
|
||||
'a=i32': stdlib_types.i32_ord_less_than,
|
||||
'a=i64': stdlib_types.i64_ord_less_than,
|
||||
'a=f32': stdlib_types.f32_ord_less_than,
|
||||
'a=f64': stdlib_types.f64_ord_less_than,
|
||||
},
|
||||
prelude.Ord.operators['<=']: {
|
||||
'a=u8': stdlib_types.u8_ord_less_than_or_equal,
|
||||
'a=u32': stdlib_types.u32_ord_less_than_or_equal,
|
||||
'a=u64': stdlib_types.u64_ord_less_than_or_equal,
|
||||
'a=i8': stdlib_types.i8_ord_less_than_or_equal,
|
||||
'a=i32': stdlib_types.i32_ord_less_than_or_equal,
|
||||
'a=i64': stdlib_types.i64_ord_less_than_or_equal,
|
||||
'a=f32': stdlib_types.f32_ord_less_than_or_equal,
|
||||
'a=f64': stdlib_types.f64_ord_less_than_or_equal,
|
||||
},
|
||||
prelude.Ord.operators['>']: {
|
||||
'a=u8': stdlib_types.u8_ord_greater_than,
|
||||
'a=u32': stdlib_types.u32_ord_greater_than,
|
||||
'a=u64': stdlib_types.u64_ord_greater_than,
|
||||
'a=i8': stdlib_types.i8_ord_greater_than,
|
||||
'a=i32': stdlib_types.i32_ord_greater_than,
|
||||
'a=i64': stdlib_types.i64_ord_greater_than,
|
||||
'a=f32': stdlib_types.f32_ord_greater_than,
|
||||
'a=f64': stdlib_types.f64_ord_greater_than,
|
||||
},
|
||||
prelude.Ord.operators['>=']: {
|
||||
'a=u8': stdlib_types.u8_ord_greater_than_or_equal,
|
||||
'a=u32': stdlib_types.u32_ord_greater_than_or_equal,
|
||||
'a=u64': stdlib_types.u64_ord_greater_than_or_equal,
|
||||
'a=i8': stdlib_types.i8_ord_greater_than_or_equal,
|
||||
'a=i32': stdlib_types.i32_ord_greater_than_or_equal,
|
||||
'a=i64': stdlib_types.i64_ord_greater_than_or_equal,
|
||||
'a=f32': stdlib_types.f32_ord_greater_than_or_equal,
|
||||
'a=f64': stdlib_types.f64_ord_greater_than_or_equal,
|
||||
},
|
||||
prelude.Bits.methods['shl']: {
|
||||
'a=u8': stdlib_types.u8_bits_logical_shift_left,
|
||||
'a=u32': stdlib_types.u32_bits_logical_shift_left,
|
||||
'a=u64': stdlib_types.u64_bits_logical_shift_left,
|
||||
},
|
||||
prelude.Bits.methods['shr']: {
|
||||
'a=u8': stdlib_types.u8_bits_logical_shift_right,
|
||||
'a=u32': stdlib_types.u32_bits_logical_shift_right,
|
||||
'a=u64': stdlib_types.u64_bits_logical_shift_right,
|
||||
},
|
||||
prelude.Bits.methods['rotl']: {
|
||||
'a=u8': stdlib_types.u8_bits_rotate_left,
|
||||
'a=u32': stdlib_types.u32_bits_rotate_left,
|
||||
'a=u64': stdlib_types.u64_bits_rotate_left,
|
||||
},
|
||||
prelude.Bits.methods['rotr']: {
|
||||
'a=u8': stdlib_types.u8_bits_rotate_right,
|
||||
'a=u32': stdlib_types.u32_bits_rotate_right,
|
||||
'a=u64': stdlib_types.u64_bits_rotate_right,
|
||||
},
|
||||
prelude.Bits.operators['&']: {
|
||||
'a=u8': stdlib_types.u8_bits_bitwise_and,
|
||||
'a=u32': stdlib_types.u32_bits_bitwise_and,
|
||||
'a=u64': stdlib_types.u64_bits_bitwise_and,
|
||||
},
|
||||
prelude.Bits.operators['|']: {
|
||||
'a=u8': stdlib_types.u8_bits_bitwise_or,
|
||||
'a=u32': stdlib_types.u32_bits_bitwise_or,
|
||||
'a=u64': stdlib_types.u64_bits_bitwise_or,
|
||||
},
|
||||
prelude.Bits.operators['^']: {
|
||||
'a=u8': stdlib_types.u8_bits_bitwise_xor,
|
||||
'a=u32': stdlib_types.u32_bits_bitwise_xor,
|
||||
'a=u64': stdlib_types.u64_bits_bitwise_xor,
|
||||
},
|
||||
prelude.Floating.methods['sqrt']: {
|
||||
'a=f32': stdlib_types.f32_floating_sqrt,
|
||||
'a=f64': stdlib_types.f64_floating_sqrt,
|
||||
},
|
||||
prelude.Fractional.methods['ceil']: {
|
||||
'a=f32': stdlib_types.f32_fractional_ceil,
|
||||
'a=f64': stdlib_types.f64_fractional_ceil,
|
||||
},
|
||||
prelude.Fractional.methods['floor']: {
|
||||
'a=f32': stdlib_types.f32_fractional_floor,
|
||||
'a=f64': stdlib_types.f64_fractional_floor,
|
||||
},
|
||||
prelude.Fractional.methods['trunc']: {
|
||||
'a=f32': stdlib_types.f32_fractional_trunc,
|
||||
'a=f64': stdlib_types.f64_fractional_trunc,
|
||||
},
|
||||
prelude.Fractional.methods['nearest']: {
|
||||
'a=f32': stdlib_types.f32_fractional_nearest,
|
||||
'a=f64': stdlib_types.f64_fractional_nearest,
|
||||
},
|
||||
prelude.Fractional.operators['/']: {
|
||||
'a=f32': stdlib_types.f32_fractional_div,
|
||||
'a=f64': stdlib_types.f64_fractional_div,
|
||||
},
|
||||
prelude.Integral.operators['//']: {
|
||||
'a=u32': stdlib_types.u32_integral_div,
|
||||
'a=u64': stdlib_types.u64_integral_div,
|
||||
'a=i32': stdlib_types.i32_integral_div,
|
||||
'a=i64': stdlib_types.i64_integral_div,
|
||||
},
|
||||
prelude.Integral.operators['%']: {
|
||||
'a=u32': stdlib_types.u32_integral_rem,
|
||||
'a=u64': stdlib_types.u64_integral_rem,
|
||||
'a=i32': stdlib_types.i32_integral_rem,
|
||||
'a=i64': stdlib_types.i64_integral_rem,
|
||||
},
|
||||
prelude.IntNum.methods['abs']: {
|
||||
'a=i32': stdlib_types.i32_intnum_abs,
|
||||
'a=i64': stdlib_types.i64_intnum_abs,
|
||||
'a=f32': stdlib_types.f32_intnum_abs,
|
||||
'a=f64': stdlib_types.f64_intnum_abs,
|
||||
},
|
||||
prelude.IntNum.methods['neg']: {
|
||||
'a=i32': stdlib_types.i32_intnum_neg,
|
||||
'a=i64': stdlib_types.i64_intnum_neg,
|
||||
'a=f32': stdlib_types.f32_intnum_neg,
|
||||
'a=f64': stdlib_types.f64_intnum_neg,
|
||||
},
|
||||
prelude.NatNum.operators['+']: {
|
||||
'a=u32': stdlib_types.u32_natnum_add,
|
||||
'a=u64': stdlib_types.u64_natnum_add,
|
||||
'a=i32': stdlib_types.i32_natnum_add,
|
||||
'a=i64': stdlib_types.i64_natnum_add,
|
||||
'a=f32': stdlib_types.f32_natnum_add,
|
||||
'a=f64': stdlib_types.f64_natnum_add,
|
||||
},
|
||||
prelude.NatNum.operators['-']: {
|
||||
'a=u32': stdlib_types.u32_natnum_sub,
|
||||
'a=u64': stdlib_types.u64_natnum_sub,
|
||||
'a=i32': stdlib_types.i32_natnum_sub,
|
||||
'a=i64': stdlib_types.i64_natnum_sub,
|
||||
'a=f32': stdlib_types.f32_natnum_sub,
|
||||
'a=f64': stdlib_types.f64_natnum_sub,
|
||||
},
|
||||
prelude.NatNum.operators['*']: {
|
||||
'a=u32': stdlib_types.u32_natnum_mul,
|
||||
'a=u64': stdlib_types.u64_natnum_mul,
|
||||
'a=i32': stdlib_types.i32_natnum_mul,
|
||||
'a=i64': stdlib_types.i64_natnum_mul,
|
||||
'a=f32': stdlib_types.f32_natnum_mul,
|
||||
'a=f64': stdlib_types.f64_natnum_mul,
|
||||
},
|
||||
prelude.NatNum.operators['<<']: {
|
||||
'a=u32': stdlib_types.u32_natnum_arithmic_shift_left,
|
||||
'a=u64': stdlib_types.u64_natnum_arithmic_shift_left,
|
||||
'a=i32': stdlib_types.i32_natnum_arithmic_shift_left,
|
||||
'a=i64': stdlib_types.i64_natnum_arithmic_shift_left,
|
||||
'a=f32': stdlib_types.f32_natnum_arithmic_shift_left,
|
||||
'a=f64': stdlib_types.f64_natnum_arithmic_shift_left,
|
||||
},
|
||||
prelude.NatNum.operators['>>']: {
|
||||
'a=u32': stdlib_types.u32_natnum_arithmic_shift_right,
|
||||
'a=u64': stdlib_types.u64_natnum_arithmic_shift_right,
|
||||
'a=i32': stdlib_types.i32_natnum_arithmic_shift_right,
|
||||
'a=i64': stdlib_types.i64_natnum_arithmic_shift_right,
|
||||
'a=f32': stdlib_types.f32_natnum_arithmic_shift_right,
|
||||
'a=f64': stdlib_types.f64_natnum_arithmic_shift_right,
|
||||
},
|
||||
prelude.Sized_.methods['len']: {
|
||||
'a=bytes': stdlib_types.bytes_sized_len,
|
||||
},
|
||||
}
|
||||
|
||||
def phasm_compile(inp: ourlang.Module) -> wasm.Module:
|
||||
"""
|
||||
Public method for compiling a parsed Phasm module into
|
||||
@ -246,14 +45,14 @@ def phasm_compile(inp: ourlang.Module) -> wasm.Module:
|
||||
"""
|
||||
return module(inp)
|
||||
|
||||
def type3(inp: type3placeholders.Type3OrPlaceholder) -> wasm.WasmType:
|
||||
def type3(inp: Type3) -> wasm.WasmType:
|
||||
"""
|
||||
Compile: type
|
||||
|
||||
Types are used for example in WebAssembly function parameters
|
||||
and return types.
|
||||
"""
|
||||
assert isinstance(inp, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
|
||||
assert inp is not None, TYPE3_ASSERTION_ERROR
|
||||
|
||||
if inp == prelude.none:
|
||||
return wasm.WasmTypeNone()
|
||||
@ -296,7 +95,7 @@ def type3(inp: type3placeholders.Type3OrPlaceholder) -> wasm.WasmType:
|
||||
# And pointers are i32
|
||||
return wasm.WasmTypeInt32()
|
||||
|
||||
if prelude.InternalPassAsPointer in inp.classes:
|
||||
if (prelude.InternalPassAsPointer, (inp, )) in prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING:
|
||||
return wasm.WasmTypeInt32()
|
||||
|
||||
raise NotImplementedError(type3, inp)
|
||||
@ -305,25 +104,30 @@ def tuple_instantiation(wgn: WasmGenerator, inp: ourlang.TupleInstantiation) ->
|
||||
"""
|
||||
Compile: Instantiation (allocation) of a tuple
|
||||
"""
|
||||
assert isinstance(inp.type3, type3types.Type3)
|
||||
assert inp.type3 is not None, TYPE3_ASSERTION_ERROR
|
||||
|
||||
args: list[type3types.Type3] = []
|
||||
args: tuple[Type3, ...]
|
||||
|
||||
sa_args = prelude.static_array.did_construct(inp.type3)
|
||||
if sa_args is not None:
|
||||
sa_type, sa_len = sa_args
|
||||
args = [sa_type for _ in range(sa_len.value)]
|
||||
if isinstance(inp.type3.application, TypeApplication_TypeStar):
|
||||
# Possibly paranoid assert. If we have a future variadic type,
|
||||
# does it also do this tuple instantation like this?
|
||||
assert isinstance(inp.type3.application.constructor, TypeConstructor_Tuple)
|
||||
|
||||
if not args:
|
||||
tp_args = prelude.tuple_.did_construct(inp.type3)
|
||||
if tp_args is None:
|
||||
raise NotImplementedError
|
||||
args = inp.type3.application.arguments
|
||||
elif isinstance(inp.type3.application, TypeApplication_TypeInt):
|
||||
# Possibly paranoid assert. If we have a future type of kind * -> Int -> *,
|
||||
# does it also do this tuple instantation like this?
|
||||
assert isinstance(inp.type3.application.constructor, TypeConstructor_StaticArray)
|
||||
|
||||
args = list(tp_args)
|
||||
sa_type, sa_len = inp.type3.application.arguments
|
||||
|
||||
args = tuple(sa_type for _ in range(sa_len.value))
|
||||
else:
|
||||
raise NotImplementedError('tuple_instantiation', inp.type3)
|
||||
|
||||
comment_elements = ''
|
||||
for element in inp.elements:
|
||||
assert isinstance(element.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
|
||||
assert element.type3 is not None, TYPE3_ASSERTION_ERROR
|
||||
comment_elements += f'{element.type3.name}, '
|
||||
|
||||
tmp_var = wgn.temp_var_i32('tuple_adr')
|
||||
@ -337,17 +141,11 @@ def tuple_instantiation(wgn: WasmGenerator, inp: ourlang.TupleInstantiation) ->
|
||||
# Store each element individually
|
||||
offset = 0
|
||||
for element, exp_type3 in zip(inp.elements, args, strict=True):
|
||||
if isinstance(exp_type3, type3placeholders.PlaceholderForType):
|
||||
assert exp_type3.resolve_as is not None
|
||||
assert isinstance(exp_type3.resolve_as, type3types.Type3)
|
||||
exp_type3 = exp_type3.resolve_as
|
||||
|
||||
assert element.type3 == exp_type3
|
||||
|
||||
if prelude.InternalPassAsPointer in exp_type3.classes:
|
||||
if (prelude.InternalPassAsPointer, (exp_type3, )) in prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING:
|
||||
mtyp = 'i32'
|
||||
else:
|
||||
assert isinstance(exp_type3, type3types.Type3), NotImplementedError('Tuple of applied types / structs')
|
||||
mtyp = LOAD_STORE_TYPE_MAP[exp_type3.name]
|
||||
|
||||
wgn.add_statement('nop', comment='PRE')
|
||||
@ -361,6 +159,78 @@ def tuple_instantiation(wgn: WasmGenerator, inp: ourlang.TupleInstantiation) ->
|
||||
# Return the allocated address
|
||||
wgn.local.get(tmp_var)
|
||||
|
||||
def expression_subscript_bytes(
|
||||
attrs: tuple[WasmGenerator, ourlang.Subscript],
|
||||
) -> None:
|
||||
wgn, inp = attrs
|
||||
|
||||
expression(wgn, inp.varref)
|
||||
expression(wgn, inp.index)
|
||||
wgn.call(stdlib_types.__subscript_bytes__)
|
||||
|
||||
def expression_subscript_static_array(
|
||||
attrs: tuple[WasmGenerator, ourlang.Subscript],
|
||||
args: tuple[Type3, IntType3],
|
||||
) -> None:
|
||||
wgn, inp = attrs
|
||||
|
||||
el_type, el_len = args
|
||||
|
||||
# OPTIMIZE: If index is a constant, we can use offset instead of multiply
|
||||
# and we don't need to do the out of bounds check
|
||||
|
||||
expression(wgn, inp.varref)
|
||||
|
||||
tmp_var = wgn.temp_var_i32('index')
|
||||
expression(wgn, inp.index)
|
||||
wgn.local.tee(tmp_var)
|
||||
|
||||
# Out of bounds check based on el_len.value
|
||||
wgn.i32.const(el_len.value)
|
||||
wgn.i32.ge_u()
|
||||
with wgn.if_():
|
||||
wgn.unreachable(comment='Out of bounds')
|
||||
|
||||
wgn.local.get(tmp_var)
|
||||
wgn.i32.const(calculate_alloc_size(el_type))
|
||||
wgn.i32.mul()
|
||||
wgn.i32.add()
|
||||
|
||||
mtyp = LOAD_STORE_TYPE_MAP[el_type.name]
|
||||
|
||||
wgn.add_statement(f'{mtyp}.load')
|
||||
|
||||
def expression_subscript_tuple(
|
||||
attrs: tuple[WasmGenerator, ourlang.Subscript],
|
||||
args: tuple[Type3, ...],
|
||||
) -> None:
|
||||
wgn, inp = attrs
|
||||
|
||||
assert isinstance(inp.index, ourlang.ConstantPrimitive)
|
||||
assert isinstance(inp.index.value, int)
|
||||
|
||||
offset = 0
|
||||
for el_type in args[0:inp.index.value]:
|
||||
assert el_type is not None, TYPE3_ASSERTION_ERROR
|
||||
offset += calculate_alloc_size(el_type)
|
||||
|
||||
el_type = args[inp.index.value]
|
||||
assert el_type is not None, TYPE3_ASSERTION_ERROR
|
||||
|
||||
expression(wgn, inp.varref)
|
||||
|
||||
if (prelude.InternalPassAsPointer, (el_type, )) in prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING:
|
||||
mtyp = 'i32'
|
||||
else:
|
||||
mtyp = LOAD_STORE_TYPE_MAP[el_type.name]
|
||||
|
||||
wgn.add_statement(f'{mtyp}.load', f'offset={offset}')
|
||||
|
||||
SUBSCRIPT_ROUTER = TypeApplicationRouter[tuple[WasmGenerator, ourlang.Subscript], None]()
|
||||
SUBSCRIPT_ROUTER.add_n(prelude.bytes_, expression_subscript_bytes)
|
||||
SUBSCRIPT_ROUTER.add(prelude.static_array, expression_subscript_static_array)
|
||||
SUBSCRIPT_ROUTER.add(prelude.tuple_, expression_subscript_tuple)
|
||||
|
||||
def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
||||
"""
|
||||
Compile: Any expression
|
||||
@ -370,7 +240,7 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
||||
raise Exception
|
||||
|
||||
if isinstance(inp, ourlang.ConstantPrimitive):
|
||||
assert isinstance(inp.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
|
||||
assert inp.type3 is not None, TYPE3_ASSERTION_ERROR
|
||||
|
||||
if inp.type3 in (prelude.i8, prelude.u8, ):
|
||||
# No native u8 type - treat as i32, with caution
|
||||
@ -411,9 +281,9 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
||||
return
|
||||
|
||||
if isinstance(inp.variable, ourlang.ModuleConstantDef):
|
||||
assert isinstance(inp.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
|
||||
assert inp.type3 is not None, TYPE3_ASSERTION_ERROR
|
||||
|
||||
if prelude.InternalPassAsPointer in inp.type3.classes:
|
||||
if (prelude.InternalPassAsPointer, (inp.type3, )) in prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING:
|
||||
assert isinstance(inp.variable.constant, (ourlang.ConstantBytes, ourlang.ConstantStruct, ourlang.ConstantTuple, ))
|
||||
|
||||
address = inp.variable.constant.data_block.address
|
||||
@ -421,82 +291,62 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
||||
wgn.i32.const(address)
|
||||
return
|
||||
|
||||
if isinstance(inp.type3, type3types.Type3):
|
||||
expression(wgn, inp.variable.constant)
|
||||
return
|
||||
|
||||
raise NotImplementedError(expression, inp)
|
||||
|
||||
raise NotImplementedError(expression, inp.variable)
|
||||
|
||||
if isinstance(inp, ourlang.BinaryOp):
|
||||
expression(wgn, inp.left)
|
||||
expression(wgn, inp.right)
|
||||
|
||||
assert isinstance(inp.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
|
||||
|
||||
type_var_map: Dict[type3functions.TypeVariable, type3types.Type3] = {}
|
||||
type_var_map: dict[TypeVariable, Type3] = {}
|
||||
|
||||
for type_var, arg_expr in zip(inp.operator.signature.args, [inp.left, inp.right, inp], strict=True):
|
||||
if not isinstance(type_var, type3functions.TypeVariable):
|
||||
assert arg_expr.type3 is not None, TYPE3_ASSERTION_ERROR
|
||||
|
||||
if isinstance(type_var, Type3):
|
||||
# Fixed type, not part of the lookup requirements
|
||||
continue
|
||||
|
||||
assert isinstance(arg_expr.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
|
||||
if isinstance(type_var, TypeVariable):
|
||||
type_var_map[type_var] = arg_expr.type3
|
||||
continue
|
||||
|
||||
instance_key = ','.join(
|
||||
f'{k.letter}={v.name}'
|
||||
for k, v in type_var_map.items()
|
||||
)
|
||||
raise NotImplementedError(type_var, arg_expr.type3)
|
||||
|
||||
instance = INSTANCES.get(inp.operator, {}).get(instance_key, None)
|
||||
if instance is not None:
|
||||
instance(wgn)
|
||||
router = prelude.PRELUDE_TYPE_CLASS_INSTANCE_METHODS[inp.operator]
|
||||
router(wgn, type_var_map)
|
||||
return
|
||||
|
||||
raise NotImplementedError(inp.operator, instance_key)
|
||||
|
||||
if isinstance(inp, ourlang.UnaryOp):
|
||||
expression(wgn, inp.right)
|
||||
|
||||
assert isinstance(inp.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
|
||||
|
||||
if inp.operator == 'cast':
|
||||
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
|
||||
return
|
||||
|
||||
raise NotImplementedError(expression, inp.type3, inp.operator)
|
||||
|
||||
if isinstance(inp, ourlang.FunctionCall):
|
||||
for arg in inp.arguments:
|
||||
expression(wgn, arg)
|
||||
|
||||
if isinstance(inp.function, type3classes.Type3ClassMethod):
|
||||
if isinstance(inp.function, Type3ClassMethod):
|
||||
# FIXME: Duplicate code with BinaryOp
|
||||
type_var_map = {}
|
||||
|
||||
for type_var, arg_expr in zip(inp.function.signature.args, inp.arguments + [inp], strict=True):
|
||||
if not isinstance(type_var, type3functions.TypeVariable):
|
||||
assert arg_expr.type3 is not None, TYPE3_ASSERTION_ERROR
|
||||
|
||||
if isinstance(type_var, Type3):
|
||||
# Fixed type, not part of the lookup requirements
|
||||
continue
|
||||
|
||||
assert isinstance(arg_expr.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
|
||||
if isinstance(type_var, TypeVariable):
|
||||
type_var_map[type_var] = arg_expr.type3
|
||||
continue
|
||||
|
||||
instance_key = ','.join(
|
||||
f'{k.letter}={v.name}'
|
||||
for k, v in type_var_map.items()
|
||||
)
|
||||
raise NotImplementedError(type_var, arg_expr.type3)
|
||||
|
||||
instance = INSTANCES.get(inp.function, {}).get(instance_key, None)
|
||||
if instance is not None:
|
||||
instance(wgn)
|
||||
router = prelude.PRELUDE_TYPE_CLASS_INSTANCE_METHODS[inp.function]
|
||||
try:
|
||||
router(wgn, type_var_map)
|
||||
except NoRouteForTypeException:
|
||||
raise NotImplementedError(str(inp.function), type_var_map)
|
||||
return
|
||||
|
||||
raise NotImplementedError(inp.function, instance_key)
|
||||
|
||||
wgn.add_statement('call', '${}'.format(inp.function.name))
|
||||
return
|
||||
|
||||
@ -505,169 +355,29 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
||||
return
|
||||
|
||||
if isinstance(inp, ourlang.Subscript):
|
||||
assert isinstance(inp.varref.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
|
||||
assert inp.varref.type3 is not None, TYPE3_ASSERTION_ERROR
|
||||
|
||||
if inp.varref.type3 is prelude.bytes_:
|
||||
expression(wgn, inp.varref)
|
||||
expression(wgn, inp.index)
|
||||
wgn.call(stdlib_types.__subscript_bytes__)
|
||||
# Type checker guarantees we don't get routing errors
|
||||
SUBSCRIPT_ROUTER((wgn, inp, ), inp.varref.type3)
|
||||
return
|
||||
|
||||
assert isinstance(inp.varref.type3, type3types.Type3)
|
||||
|
||||
sa_args = prelude.static_array.did_construct(inp.varref.type3)
|
||||
if sa_args is not None:
|
||||
el_type, el_len = sa_args
|
||||
|
||||
# OPTIMIZE: If index is a constant, we can use offset instead of multiply
|
||||
# and we don't need to do the out of bounds check
|
||||
|
||||
expression(wgn, inp.varref)
|
||||
|
||||
tmp_var = wgn.temp_var_i32('index')
|
||||
expression(wgn, inp.index)
|
||||
wgn.local.tee(tmp_var)
|
||||
|
||||
# Out of bounds check based on el_len.value
|
||||
wgn.i32.const(el_len.value)
|
||||
wgn.i32.ge_u()
|
||||
with wgn.if_():
|
||||
wgn.unreachable(comment='Out of bounds')
|
||||
|
||||
wgn.local.get(tmp_var)
|
||||
wgn.i32.const(calculate_alloc_size(el_type))
|
||||
wgn.i32.mul()
|
||||
wgn.i32.add()
|
||||
|
||||
assert isinstance(el_type, type3types.Type3), NotImplementedError('Tuple of applied types / structs')
|
||||
mtyp = LOAD_STORE_TYPE_MAP[el_type.name]
|
||||
|
||||
wgn.add_statement(f'{mtyp}.load')
|
||||
return
|
||||
|
||||
tp_args = prelude.tuple_.did_construct(inp.varref.type3)
|
||||
if tp_args is not None:
|
||||
assert isinstance(inp.index, ourlang.ConstantPrimitive)
|
||||
assert isinstance(inp.index.value, int)
|
||||
|
||||
offset = 0
|
||||
for el_type in tp_args[0:inp.index.value]:
|
||||
assert isinstance(el_type, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
|
||||
offset += calculate_alloc_size(el_type)
|
||||
|
||||
el_type = tp_args[inp.index.value]
|
||||
assert isinstance(el_type, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
|
||||
|
||||
expression(wgn, inp.varref)
|
||||
|
||||
if prelude.InternalPassAsPointer in el_type.classes:
|
||||
mtyp = 'i32'
|
||||
else:
|
||||
assert isinstance(el_type, type3types.Type3), NotImplementedError('Tuple of applied types / structs')
|
||||
mtyp = LOAD_STORE_TYPE_MAP[el_type.name]
|
||||
|
||||
wgn.add_statement(f'{mtyp}.load', f'offset={offset}')
|
||||
return
|
||||
|
||||
raise NotImplementedError(expression, inp, inp.varref.type3)
|
||||
|
||||
if isinstance(inp, ourlang.AccessStructMember):
|
||||
assert isinstance(inp.struct_type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
|
||||
assert inp.struct_type3 is not None, TYPE3_ASSERTION_ERROR
|
||||
|
||||
st_args = prelude.struct.did_construct(inp.struct_type3)
|
||||
assert st_args is not None
|
||||
assert isinstance(inp.struct_type3.application, TypeApplication_Struct)
|
||||
|
||||
member_type = st_args[inp.member]
|
||||
member_type = dict(inp.struct_type3.application.arguments)[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)
|
||||
wgn.add_statement(f'{mtyp}.load', 'offset=' + str(calculate_member_offset(
|
||||
inp.struct_type3.name, st_args, inp.member
|
||||
inp.struct_type3.name, inp.struct_type3.application.arguments, inp.member
|
||||
)))
|
||||
return
|
||||
|
||||
raise NotImplementedError(expression, inp)
|
||||
|
||||
# def expression_fold(wgn: WasmGenerator, inp: ourlang.Fold) -> None:
|
||||
# """
|
||||
# Compile: Fold expression
|
||||
# """
|
||||
# assert isinstance(inp.type3, type3types.Type3), type3placeholders.TYPE3_ASSERTION_ERROR
|
||||
|
||||
# if inp.iter.type3 is not prelude.bytes_:
|
||||
# raise NotImplementedError(expression_fold, inp, inp.iter.type3)
|
||||
|
||||
# wgn.add_statement('nop', comment='acu :: u8')
|
||||
# acu_var = wgn.temp_var_u8(f'fold_{codestyle.type3(inp.type3)}_acu')
|
||||
# wgn.add_statement('nop', comment='adr :: bytes*')
|
||||
# adr_var = wgn.temp_var_i32('fold_i32_adr')
|
||||
# wgn.add_statement('nop', comment='len :: i32')
|
||||
# len_var = wgn.temp_var_i32('fold_i32_len')
|
||||
|
||||
# wgn.add_statement('nop', comment='acu = base')
|
||||
# expression(wgn, inp.base)
|
||||
# wgn.local.set(acu_var)
|
||||
|
||||
# wgn.add_statement('nop', comment='adr = adr(iter)')
|
||||
# expression(wgn, inp.iter)
|
||||
# wgn.local.set(adr_var)
|
||||
|
||||
# wgn.add_statement('nop', comment='len = len(iter)')
|
||||
# wgn.local.get(adr_var)
|
||||
# wgn.i32.load()
|
||||
# wgn.local.set(len_var)
|
||||
|
||||
# wgn.add_statement('nop', comment='i = 0')
|
||||
# idx_var = wgn.temp_var_i32(f'fold_{codestyle.type3(inp.type3)}_idx')
|
||||
# wgn.i32.const(0)
|
||||
# wgn.local.set(idx_var)
|
||||
|
||||
# wgn.add_statement('nop', comment='if i < len')
|
||||
# wgn.local.get(idx_var)
|
||||
# wgn.local.get(len_var)
|
||||
# wgn.i32.lt_u()
|
||||
# with wgn.if_():
|
||||
# # From here on, adr_var is the address of byte we're referencing
|
||||
# # This is akin to calling stdlib_types.__subscript_bytes__
|
||||
# # But since we already know we are inside of bounds,
|
||||
# # can just bypass it and load the memory directly.
|
||||
# wgn.local.get(adr_var)
|
||||
# wgn.i32.const(3) # Bytes header -1, since we do a +1 every loop
|
||||
# wgn.i32.add()
|
||||
# wgn.local.set(adr_var)
|
||||
|
||||
# wgn.add_statement('nop', comment='while True')
|
||||
# with wgn.loop():
|
||||
# wgn.add_statement('nop', comment='acu = func(acu, iter[i])')
|
||||
# wgn.local.get(acu_var)
|
||||
|
||||
# # Get the next byte, write back the address
|
||||
# wgn.local.get(adr_var)
|
||||
# wgn.i32.const(1)
|
||||
# wgn.i32.add()
|
||||
# wgn.local.tee(adr_var)
|
||||
# wgn.i32.load8_u()
|
||||
|
||||
# wgn.add_statement('call', f'${inp.func.name}')
|
||||
# wgn.local.set(acu_var)
|
||||
|
||||
# wgn.add_statement('nop', comment='i = i + 1')
|
||||
# wgn.local.get(idx_var)
|
||||
# wgn.i32.const(1)
|
||||
# wgn.i32.add()
|
||||
# wgn.local.set(idx_var)
|
||||
|
||||
# wgn.add_statement('nop', comment='if i >= len: break')
|
||||
# wgn.local.get(idx_var)
|
||||
# wgn.local.get(len_var)
|
||||
# wgn.i32.lt_u()
|
||||
# wgn.br_if(0)
|
||||
|
||||
# # return acu
|
||||
# wgn.local.get(acu_var)
|
||||
|
||||
def statement_return(wgn: WasmGenerator, inp: ourlang.StatementReturn) -> None:
|
||||
"""
|
||||
Compile: Return statement
|
||||
@ -825,7 +535,7 @@ def module_data(inp: ourlang.ModuleData) -> bytes:
|
||||
data_list: List[bytes] = []
|
||||
|
||||
for constant in block.data:
|
||||
assert isinstance(constant.type3, type3types.Type3), (id(constant), type3placeholders.TYPE3_ASSERTION_ERROR)
|
||||
assert constant.type3 is not None, TYPE3_ASSERTION_ERROR
|
||||
|
||||
if isinstance(constant, ourlang.ConstantMemoryStored) and block is not constant.data_block:
|
||||
# It's stored in a different block
|
||||
@ -955,8 +665,9 @@ def module(inp: ourlang.Module) -> wasm.Module:
|
||||
return result
|
||||
|
||||
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
|
||||
assert isinstance(inp.struct_type3.application, TypeApplication_Struct)
|
||||
|
||||
st_args = inp.struct_type3.application.arguments
|
||||
|
||||
tmp_var = wgn.temp_var_i32('struct_adr')
|
||||
|
||||
@ -966,9 +677,9 @@ def _generate_struct_constructor(wgn: WasmGenerator, inp: ourlang.StructConstruc
|
||||
wgn.local.set(tmp_var)
|
||||
|
||||
# Store each member individually
|
||||
for memname, mtyp3 in st_args.items():
|
||||
for memname, mtyp3 in st_args:
|
||||
mtyp: Optional[str]
|
||||
if prelude.InternalPassAsPointer in mtyp3.classes:
|
||||
if (prelude.InternalPassAsPointer, (mtyp3, )) in prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING:
|
||||
mtyp = 'i32'
|
||||
else:
|
||||
mtyp = LOAD_STORE_TYPE_MAP.get(mtyp3.name)
|
||||
|
||||
@ -5,9 +5,8 @@ from typing import Dict, Iterable, List, Optional, Union
|
||||
|
||||
from . import prelude
|
||||
from .type3.functions import FunctionSignature, TypeVariableContext
|
||||
from .type3.placeholders import PlaceholderForType, Type3OrPlaceholder
|
||||
from .type3.typeclasses import Type3ClassMethod
|
||||
from .type3.types import Type3
|
||||
from .type3.types import Type3, TypeApplication_Struct
|
||||
|
||||
|
||||
class Expression:
|
||||
@ -16,10 +15,10 @@ class Expression:
|
||||
"""
|
||||
__slots__ = ('type3', )
|
||||
|
||||
type3: Type3OrPlaceholder
|
||||
type3: Type3 | None
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.type3 = PlaceholderForType([self])
|
||||
self.type3 = None
|
||||
|
||||
class Constant(Expression):
|
||||
"""
|
||||
@ -98,21 +97,21 @@ class ConstantStruct(ConstantMemoryStored):
|
||||
"""
|
||||
A Struct constant value expression within a statement
|
||||
"""
|
||||
__slots__ = ('struct_name', 'value', )
|
||||
__slots__ = ('struct_type3', 'value', )
|
||||
|
||||
struct_name: str
|
||||
struct_type3: Type3
|
||||
value: List[Union[ConstantPrimitive, ConstantBytes, ConstantTuple, 'ConstantStruct']]
|
||||
|
||||
def __init__(self, struct_name: str, value: List[Union[ConstantPrimitive, ConstantBytes, ConstantTuple, 'ConstantStruct']], data_block: 'ModuleDataBlock') -> None:
|
||||
def __init__(self, struct_type3: Type3, value: List[Union[ConstantPrimitive, ConstantBytes, ConstantTuple, 'ConstantStruct']], data_block: 'ModuleDataBlock') -> None:
|
||||
super().__init__(data_block)
|
||||
self.struct_name = struct_name
|
||||
self.struct_type3 = struct_type3
|
||||
self.value = value
|
||||
|
||||
def __repr__(self) -> str:
|
||||
# Do not repr the whole ModuleDataBlock
|
||||
# As this has a reference back to this constant for its data
|
||||
# which it needs to compile the data into the program
|
||||
return f'ConstantStruct({repr(self.struct_name)}, {repr(self.value)}, @{repr(self.data_block.address)})'
|
||||
return f'ConstantStruct({self.struct_type3!r}, {self.value!r}, @{self.data_block.address!r})'
|
||||
|
||||
class VariableReference(Expression):
|
||||
"""
|
||||
@ -126,21 +125,6 @@ class VariableReference(Expression):
|
||||
super().__init__()
|
||||
self.variable = variable
|
||||
|
||||
class UnaryOp(Expression):
|
||||
"""
|
||||
A unary operator expression within a statement
|
||||
"""
|
||||
__slots__ = ('operator', 'right', )
|
||||
|
||||
operator: str
|
||||
right: Expression
|
||||
|
||||
def __init__(self, operator: str, right: Expression) -> None:
|
||||
super().__init__()
|
||||
|
||||
self.operator = operator
|
||||
self.right = right
|
||||
|
||||
class BinaryOp(Expression):
|
||||
"""
|
||||
A binary operator expression within a statement
|
||||
@ -212,10 +196,10 @@ class AccessStructMember(Expression):
|
||||
__slots__ = ('varref', 'struct_type3', 'member', )
|
||||
|
||||
varref: VariableReference
|
||||
struct_type3: Type3OrPlaceholder
|
||||
struct_type3: Type3
|
||||
member: str
|
||||
|
||||
def __init__(self, varref: VariableReference, struct_type3: Type3OrPlaceholder, member: str) -> None:
|
||||
def __init__(self, varref: VariableReference, struct_type3: Type3, member: str) -> None:
|
||||
super().__init__()
|
||||
|
||||
self.varref = varref
|
||||
@ -268,7 +252,7 @@ class FunctionParam:
|
||||
__slots__ = ('name', 'type3', )
|
||||
|
||||
name: str
|
||||
type3: Type3OrPlaceholder
|
||||
type3: Type3
|
||||
|
||||
def __init__(self, name: str, type3: Type3) -> None:
|
||||
self.name = name
|
||||
@ -326,9 +310,9 @@ class StructConstructor(Function):
|
||||
def __init__(self, struct_type3: Type3) -> None:
|
||||
super().__init__(f'@{struct_type3.name}@__init___@', -1)
|
||||
|
||||
st_args = prelude.struct.did_construct(struct_type3)
|
||||
assert st_args is not None
|
||||
for mem, typ in st_args.items():
|
||||
assert isinstance(struct_type3.application, TypeApplication_Struct)
|
||||
|
||||
for mem, typ in struct_type3.application.arguments:
|
||||
self.posonlyargs.append(FunctionParam(mem, typ, ))
|
||||
self.signature.args.append(typ)
|
||||
|
||||
|
||||
@ -28,12 +28,11 @@ from .ourlang import (
|
||||
StructDefinition,
|
||||
Subscript,
|
||||
TupleInstantiation,
|
||||
UnaryOp,
|
||||
VariableReference,
|
||||
)
|
||||
from .prelude import PRELUDE_METHODS, PRELUDE_OPERATORS, PRELUDE_TYPES
|
||||
from .type3 import typeclasses as type3typeclasses
|
||||
from .type3 import types as type3types
|
||||
from .type3.typeclasses import Type3ClassMethod
|
||||
from .type3.types import IntType3, Type3
|
||||
|
||||
|
||||
def phasm_parse(source: str) -> Module:
|
||||
@ -163,8 +162,8 @@ class OurVisitor:
|
||||
|
||||
arg_type = self.visit_type(module, arg.annotation)
|
||||
|
||||
# if isisntance(arg_type, TypeVariable):
|
||||
# arg_type = PlaceHolderForType()
|
||||
# FIXME: Allow TypeVariable in the function signature
|
||||
# This would also require FunctionParam to accept a placeholder
|
||||
|
||||
function.signature.args.append(arg_type)
|
||||
function.posonlyargs.append(FunctionParam(
|
||||
@ -226,7 +225,7 @@ class OurVisitor:
|
||||
_not_implemented(not node.keywords, 'ClassDef.keywords')
|
||||
_not_implemented(not node.decorator_list, 'ClassDef.decorator_list')
|
||||
|
||||
members: Dict[str, type3types.Type3] = {}
|
||||
members: Dict[str, Type3] = {}
|
||||
|
||||
for stmt in node.body:
|
||||
if not isinstance(stmt, ast.AnnAssign):
|
||||
@ -246,7 +245,7 @@ class OurVisitor:
|
||||
|
||||
members[stmt.target.id] = self.visit_type(module, stmt.annotation)
|
||||
|
||||
return StructDefinition(prelude.struct(node.name, members, set()), node.lineno)
|
||||
return StructDefinition(prelude.struct(node.name, tuple(members.items())), node.lineno)
|
||||
|
||||
def pre_visit_Module_AnnAssign(self, module: Module, node: ast.AnnAssign) -> ModuleConstantDef:
|
||||
if not isinstance(node.target, ast.Name):
|
||||
@ -352,7 +351,7 @@ class OurVisitor:
|
||||
|
||||
def visit_Module_FunctionDef_expr(self, module: Module, function: Function, our_locals: OurLocals, node: ast.expr) -> Expression:
|
||||
if isinstance(node, ast.BinOp):
|
||||
operator: Union[str, type3typeclasses.Type3ClassMethod]
|
||||
operator: Union[str, Type3ClassMethod]
|
||||
|
||||
if isinstance(node.op, ast.Add):
|
||||
operator = '+'
|
||||
@ -388,19 +387,6 @@ class OurVisitor:
|
||||
self.visit_Module_FunctionDef_expr(module, function, our_locals, node.right),
|
||||
)
|
||||
|
||||
if isinstance(node, ast.UnaryOp):
|
||||
if isinstance(node.op, ast.UAdd):
|
||||
operator = '+'
|
||||
elif isinstance(node.op, ast.USub):
|
||||
operator = '-'
|
||||
else:
|
||||
raise NotImplementedError(f'Operator {node.op}')
|
||||
|
||||
return UnaryOp(
|
||||
operator,
|
||||
self.visit_Module_FunctionDef_expr(module, function, our_locals, node.operand),
|
||||
)
|
||||
|
||||
if isinstance(node, ast.Compare):
|
||||
if 1 < len(node.ops):
|
||||
raise NotImplementedError('Multiple operators')
|
||||
@ -475,7 +461,7 @@ class OurVisitor:
|
||||
|
||||
raise NotImplementedError(f'{node} as expr in FunctionDef')
|
||||
|
||||
def visit_Module_FunctionDef_Call(self, module: Module, function: Function, our_locals: OurLocals, node: ast.Call) -> Union[FunctionCall, UnaryOp]:
|
||||
def visit_Module_FunctionDef_Call(self, module: Module, function: Function, our_locals: OurLocals, node: ast.Call) -> Union[FunctionCall]:
|
||||
if node.keywords:
|
||||
_raise_static_error(node, 'Keyword calling not supported') # Yet?
|
||||
|
||||
@ -484,42 +470,10 @@ class OurVisitor:
|
||||
if not isinstance(node.func.ctx, ast.Load):
|
||||
_raise_static_error(node, 'Must be load context')
|
||||
|
||||
func: Union[Function, type3typeclasses.Type3ClassMethod]
|
||||
func: Union[Function, Type3ClassMethod]
|
||||
|
||||
if node.func.id in PRELUDE_METHODS:
|
||||
func = PRELUDE_METHODS[node.func.id]
|
||||
elif node.func.id == 'u32':
|
||||
if 1 != len(node.args):
|
||||
_raise_static_error(node, f'Function {node.func.id} requires 1 arguments but {len(node.args)} are given')
|
||||
|
||||
unary_op = UnaryOp(
|
||||
'cast',
|
||||
self.visit_Module_FunctionDef_expr(module, function, our_locals, node.args[0]),
|
||||
)
|
||||
unary_op.type3 = prelude.u32
|
||||
return unary_op
|
||||
# elif node.func.id == 'foldl':
|
||||
# if 3 != len(node.args):
|
||||
# _raise_static_error(node, f'Function {node.func.id} requires 3 arguments but {len(node.args)} are given')
|
||||
|
||||
# # TODO: This is not generic, you cannot return a function
|
||||
# subnode = node.args[0]
|
||||
# if not isinstance(subnode, ast.Name):
|
||||
# raise NotImplementedError(f'Calling methods that are not a name {subnode}')
|
||||
# if not isinstance(subnode.ctx, ast.Load):
|
||||
# _raise_static_error(subnode, 'Must be load context')
|
||||
# if subnode.id not in module.functions:
|
||||
# _raise_static_error(subnode, 'Reference to undefined function')
|
||||
# func = module.functions[subnode.id]
|
||||
# if 2 != len(func.posonlyargs):
|
||||
# _raise_static_error(node, f'Function {node.func.id} requires a function with 2 arguments but a function with {len(func.posonlyargs)} args is given')
|
||||
|
||||
# return Fold(
|
||||
# Fold.Direction.LEFT,
|
||||
# func,
|
||||
# self.visit_Module_FunctionDef_expr(module, function, our_locals, node.args[1]),
|
||||
# self.visit_Module_FunctionDef_expr(module, function, our_locals, node.args[2]),
|
||||
# )
|
||||
else:
|
||||
if node.func.id not in module.functions:
|
||||
_raise_static_error(node, 'Call to undefined function')
|
||||
@ -607,7 +561,8 @@ class OurVisitor:
|
||||
if not isinstance(node.func.ctx, ast.Load):
|
||||
_raise_static_error(node.func, 'Must be load context')
|
||||
|
||||
if node.func.id not in module.struct_definitions:
|
||||
struct_def = module.struct_definitions.get(node.func.id)
|
||||
if struct_def is None:
|
||||
_raise_static_error(node.func, 'Undefined struct')
|
||||
|
||||
if node.keywords:
|
||||
@ -623,7 +578,7 @@ class OurVisitor:
|
||||
|
||||
data_block = ModuleDataBlock(struct_data)
|
||||
module.data.blocks.append(data_block)
|
||||
return ConstantStruct(node.func.id, struct_data, data_block)
|
||||
return ConstantStruct(struct_def.struct_type3, struct_data, data_block)
|
||||
|
||||
_not_implemented(node.kind is None, 'Constant.kind')
|
||||
|
||||
@ -640,7 +595,7 @@ class OurVisitor:
|
||||
|
||||
raise NotImplementedError(f'{node.value} as constant')
|
||||
|
||||
def visit_type(self, module: Module, node: ast.expr) -> type3types.Type3:
|
||||
def visit_type(self, module: Module, node: ast.expr) -> Type3:
|
||||
if isinstance(node, ast.Constant):
|
||||
if node.value is None:
|
||||
return prelude.none
|
||||
@ -668,7 +623,7 @@ class OurVisitor:
|
||||
|
||||
return prelude.static_array(
|
||||
self.visit_type(module, node.value),
|
||||
type3types.IntType3(node.slice.value),
|
||||
IntType3(node.slice.value),
|
||||
)
|
||||
|
||||
if isinstance(node, ast.Tuple):
|
||||
|
||||
@ -1,54 +1,129 @@
|
||||
"""
|
||||
The prelude are all the builtin types, type classes and methods
|
||||
"""
|
||||
from typing import Any, Callable
|
||||
from warnings import warn
|
||||
|
||||
from ..type3.typeclasses import (
|
||||
Type3Class,
|
||||
from phasm.stdlib import types as stdtypes
|
||||
from phasm.wasmgenerator import Generator
|
||||
|
||||
from ..type3.functions import (
|
||||
Constraint_TypeClassInstanceExists,
|
||||
TypeConstructorVariable,
|
||||
TypeVariable,
|
||||
instance_type_class,
|
||||
TypeVariableApplication_Nullary,
|
||||
)
|
||||
from ..type3.routers import TypeClassArgsRouter, TypeVariableLookup
|
||||
from ..type3.typeclasses import Type3Class, Type3ClassMethod
|
||||
from ..type3.types import (
|
||||
IntType3,
|
||||
Type3,
|
||||
TypeApplication_Nullary,
|
||||
TypeConstructor_Base,
|
||||
TypeConstructor_StaticArray,
|
||||
TypeConstructor_Struct,
|
||||
TypeConstructor_Tuple,
|
||||
)
|
||||
|
||||
none = Type3('none', [])
|
||||
PRELUDE_TYPE_CLASS_INSTANCES_EXISTING: set[tuple[Type3Class, tuple[Type3 | TypeConstructor_Base[Any], ...]]] = set()
|
||||
|
||||
PRELUDE_TYPE_CLASS_INSTANCE_METHODS: dict[Type3ClassMethod, TypeClassArgsRouter[Generator, None]] = {}
|
||||
|
||||
class MissingImplementationException(Exception):
|
||||
pass
|
||||
|
||||
class MissingImplementationWarning(Warning):
|
||||
pass
|
||||
|
||||
def instance_type_class(
|
||||
cls: Type3Class,
|
||||
*typ: Type3 | TypeConstructor_Base[Any],
|
||||
methods: dict[str, Callable[[Generator, TypeVariableLookup], None]] = {},
|
||||
operators: dict[str, Callable[[Generator, TypeVariableLookup], None]] = {},
|
||||
) -> None:
|
||||
global PRELUDE_TYPE_CLASS_INSTANCES_EXISTING
|
||||
global PRELUDE_TYPE_CLASS_INSTANCE_METHODS
|
||||
|
||||
assert len(cls.args) == len(typ)
|
||||
|
||||
tv_map: dict[TypeVariable, Type3] = {}
|
||||
tc_map: dict[TypeConstructorVariable, TypeConstructor_Base[Any]] = {}
|
||||
for arg_tv, arg_tp in zip(cls.args, typ, strict=True):
|
||||
if isinstance(arg_tv, TypeVariable):
|
||||
assert isinstance(arg_tp, Type3)
|
||||
tv_map[arg_tv] = arg_tp
|
||||
elif isinstance(arg_tv, TypeConstructorVariable):
|
||||
assert isinstance(arg_tp, TypeConstructor_Base)
|
||||
tc_map[arg_tv] = arg_tp
|
||||
else:
|
||||
raise NotImplementedError(arg_tv, arg_tp)
|
||||
|
||||
# TODO: Check for required existing instantiations
|
||||
|
||||
PRELUDE_TYPE_CLASS_INSTANCES_EXISTING.add((cls, tuple(typ), ))
|
||||
|
||||
for method_name, method in cls.methods.items():
|
||||
router = PRELUDE_TYPE_CLASS_INSTANCE_METHODS.get(method)
|
||||
if router is None:
|
||||
router = TypeClassArgsRouter[Generator, None](cls.args)
|
||||
PRELUDE_TYPE_CLASS_INSTANCE_METHODS[method] = router
|
||||
|
||||
try:
|
||||
generator = methods[method_name]
|
||||
except KeyError:
|
||||
warn(MissingImplementationWarning(str(method), cls.name + ' ' + ' '.join(x.name for x in typ)))
|
||||
continue
|
||||
|
||||
router.add(tv_map, tc_map, generator)
|
||||
|
||||
for operator_name, operator in cls.operators.items():
|
||||
router = PRELUDE_TYPE_CLASS_INSTANCE_METHODS.get(operator)
|
||||
if router is None:
|
||||
router = TypeClassArgsRouter[Generator, None](cls.args)
|
||||
PRELUDE_TYPE_CLASS_INSTANCE_METHODS[operator] = router
|
||||
|
||||
try:
|
||||
generator = operators[operator_name]
|
||||
except KeyError:
|
||||
warn(MissingImplementationWarning(str(operator), cls.name + ' ' + ' '.join(x.name for x in typ)))
|
||||
continue
|
||||
|
||||
router.add(tv_map, tc_map, generator)
|
||||
|
||||
none = Type3('none', TypeApplication_Nullary(None, None))
|
||||
"""
|
||||
The none type, for when functions simply don't return anything. e.g., IO().
|
||||
"""
|
||||
|
||||
bool_ = Type3('bool', [])
|
||||
bool_ = Type3('bool', TypeApplication_Nullary(None, None))
|
||||
"""
|
||||
The bool type, either True or False
|
||||
|
||||
Suffixes with an underscores, as it's a Python builtin
|
||||
"""
|
||||
|
||||
u8 = Type3('u8', [])
|
||||
u8 = Type3('u8', TypeApplication_Nullary(None, None))
|
||||
"""
|
||||
The unsigned 8-bit integer type.
|
||||
|
||||
Operations on variables employ modular arithmetic, with modulus 2^8.
|
||||
"""
|
||||
|
||||
u32 = Type3('u32', [])
|
||||
u32 = Type3('u32', TypeApplication_Nullary(None, None))
|
||||
"""
|
||||
The unsigned 32-bit integer type.
|
||||
|
||||
Operations on variables employ modular arithmetic, with modulus 2^32.
|
||||
"""
|
||||
|
||||
u64 = Type3('u64', [])
|
||||
u64 = Type3('u64', TypeApplication_Nullary(None, None))
|
||||
"""
|
||||
The unsigned 64-bit integer type.
|
||||
|
||||
Operations on variables employ modular arithmetic, with modulus 2^64.
|
||||
"""
|
||||
|
||||
i8 = Type3('i8', [])
|
||||
i8 = Type3('i8', TypeApplication_Nullary(None, None))
|
||||
"""
|
||||
The signed 8-bit integer type.
|
||||
|
||||
@ -56,7 +131,7 @@ Operations on variables employ modular arithmetic, with modulus 2^8, but
|
||||
with the middel point being 0.
|
||||
"""
|
||||
|
||||
i32 = Type3('i32', [])
|
||||
i32 = Type3('i32', TypeApplication_Nullary(None, None))
|
||||
"""
|
||||
The unsigned 32-bit integer type.
|
||||
|
||||
@ -64,7 +139,7 @@ Operations on variables employ modular arithmetic, with modulus 2^32, but
|
||||
with the middel point being 0.
|
||||
"""
|
||||
|
||||
i64 = Type3('i64', [])
|
||||
i64 = Type3('i64', TypeApplication_Nullary(None, None))
|
||||
"""
|
||||
The unsigned 64-bit integer type.
|
||||
|
||||
@ -72,22 +147,25 @@ Operations on variables employ modular arithmetic, with modulus 2^64, but
|
||||
with the middel point being 0.
|
||||
"""
|
||||
|
||||
f32 = Type3('f32', [])
|
||||
f32 = Type3('f32', TypeApplication_Nullary(None, None))
|
||||
"""
|
||||
A 32-bits IEEE 754 float, of 32 bits width.
|
||||
"""
|
||||
|
||||
f64 = Type3('f64', [])
|
||||
f64 = Type3('f64', TypeApplication_Nullary(None, None))
|
||||
"""
|
||||
A 32-bits IEEE 754 float, of 64 bits width.
|
||||
"""
|
||||
|
||||
bytes_ = Type3('bytes', [])
|
||||
bytes_ = Type3('bytes', TypeApplication_Nullary(None, None))
|
||||
"""
|
||||
This is a runtime-determined length piece of memory that can be indexed at runtime.
|
||||
"""
|
||||
|
||||
static_array = TypeConstructor_StaticArray('static_array', [], [])
|
||||
def sa_on_create(args: tuple[Type3, IntType3], typ: Type3) -> None:
|
||||
instance_type_class(InternalPassAsPointer, typ)
|
||||
|
||||
static_array = TypeConstructor_StaticArray('static_array', on_create=sa_on_create)
|
||||
"""
|
||||
A type constructor.
|
||||
|
||||
@ -97,7 +175,10 @@ It should be applied with one argument. It has a runtime-dynamic length
|
||||
of the same type repeated.
|
||||
"""
|
||||
|
||||
tuple_ = TypeConstructor_Tuple('tuple', [], [])
|
||||
def tp_on_create(args: tuple[Type3, ...], typ: Type3) -> None:
|
||||
instance_type_class(InternalPassAsPointer, typ)
|
||||
|
||||
tuple_ = TypeConstructor_Tuple('tuple', on_create=tp_on_create)
|
||||
"""
|
||||
This is a fixed length piece of memory.
|
||||
|
||||
@ -105,7 +186,10 @@ 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', [], [])
|
||||
def st_on_create(args: tuple[tuple[str, Type3], ...], typ: Type3) -> None:
|
||||
instance_type_class(InternalPassAsPointer, typ)
|
||||
|
||||
struct = TypeConstructor_Struct('struct', on_create=st_on_create)
|
||||
"""
|
||||
This is like a tuple, but each argument is named, so that developers
|
||||
can get and set fields by name.
|
||||
@ -125,37 +209,61 @@ PRELUDE_TYPES: dict[str, Type3] = {
|
||||
'bytes': bytes_,
|
||||
}
|
||||
|
||||
a = TypeVariable('a')
|
||||
b = TypeVariable('b')
|
||||
a = TypeVariable('a', TypeVariableApplication_Nullary(None, None))
|
||||
b = TypeVariable('b', TypeVariableApplication_Nullary(None, None))
|
||||
|
||||
t = TypeConstructorVariable('t')
|
||||
|
||||
InternalPassAsPointer = Type3Class('InternalPassAsPointer', [a], methods={}, operators={})
|
||||
InternalPassAsPointer = Type3Class('InternalPassAsPointer', (a, ), methods={}, operators={})
|
||||
"""
|
||||
Internal type class to keep track which types we pass arounds as a pointer.
|
||||
"""
|
||||
|
||||
instance_type_class(InternalPassAsPointer, bytes_)
|
||||
instance_type_class(InternalPassAsPointer, static_array)
|
||||
instance_type_class(InternalPassAsPointer, tuple_)
|
||||
instance_type_class(InternalPassAsPointer, struct)
|
||||
# instance_type_class(InternalPassAsPointer, static_array)
|
||||
# instance_type_class(InternalPassAsPointer, tuple_)
|
||||
# instance_type_class(InternalPassAsPointer, struct)
|
||||
|
||||
Eq = Type3Class('Eq', [a], methods={}, operators={
|
||||
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)
|
||||
instance_type_class(Eq, u8, operators={
|
||||
'==': stdtypes.u8_eq_equals,
|
||||
'!=': stdtypes.u8_eq_not_equals,
|
||||
})
|
||||
instance_type_class(Eq, u32, operators={
|
||||
'==': stdtypes.u32_eq_equals,
|
||||
'!=': stdtypes.u32_eq_not_equals,
|
||||
})
|
||||
instance_type_class(Eq, u64, operators={
|
||||
'==': stdtypes.u64_eq_equals,
|
||||
'!=': stdtypes.u64_eq_not_equals,
|
||||
})
|
||||
instance_type_class(Eq, i8, operators={
|
||||
'==': stdtypes.i8_eq_equals,
|
||||
'!=': stdtypes.i8_eq_not_equals,
|
||||
})
|
||||
instance_type_class(Eq, i32, operators={
|
||||
'==': stdtypes.i32_eq_equals,
|
||||
'!=': stdtypes.i32_eq_not_equals,
|
||||
})
|
||||
instance_type_class(Eq, i64, operators={
|
||||
'==': stdtypes.i64_eq_equals,
|
||||
'!=': stdtypes.i64_eq_not_equals,
|
||||
})
|
||||
instance_type_class(Eq, f32, operators={
|
||||
'==': stdtypes.f32_eq_equals,
|
||||
'!=': stdtypes.f32_eq_not_equals,
|
||||
})
|
||||
instance_type_class(Eq, f64, operators={
|
||||
'==': stdtypes.f64_eq_equals,
|
||||
'!=': stdtypes.f64_eq_not_equals,
|
||||
})
|
||||
|
||||
Ord = Type3Class('Ord', [a], methods={
|
||||
Ord = Type3Class('Ord', (a, ), methods={
|
||||
'min': [a, a, a],
|
||||
'max': [a, a, a],
|
||||
}, operators={
|
||||
@ -165,16 +273,80 @@ Ord = Type3Class('Ord', [a], methods={
|
||||
'>=': [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)
|
||||
instance_type_class(Ord, u8, methods={
|
||||
'min': stdtypes.u8_ord_min,
|
||||
'max': stdtypes.u8_ord_max,
|
||||
}, operators={
|
||||
'<': stdtypes.u8_ord_less_than,
|
||||
'<=': stdtypes.u8_ord_less_than_or_equal,
|
||||
'>': stdtypes.u8_ord_greater_than,
|
||||
'>=': stdtypes.u8_ord_greater_than_or_equal,
|
||||
})
|
||||
instance_type_class(Ord, u32, methods={
|
||||
'min': stdtypes.u32_ord_min,
|
||||
'max': stdtypes.u32_ord_max,
|
||||
}, operators={
|
||||
'<': stdtypes.u32_ord_less_than,
|
||||
'<=': stdtypes.u32_ord_less_than_or_equal,
|
||||
'>': stdtypes.u32_ord_greater_than,
|
||||
'>=': stdtypes.u32_ord_greater_than_or_equal,
|
||||
})
|
||||
instance_type_class(Ord, u64, methods={
|
||||
'min': stdtypes.u64_ord_min,
|
||||
'max': stdtypes.u64_ord_max,
|
||||
}, operators={
|
||||
'<': stdtypes.u64_ord_less_than,
|
||||
'<=': stdtypes.u64_ord_less_than_or_equal,
|
||||
'>': stdtypes.u64_ord_greater_than,
|
||||
'>=': stdtypes.u64_ord_greater_than_or_equal,
|
||||
})
|
||||
instance_type_class(Ord, i8, methods={
|
||||
'min': stdtypes.i8_ord_min,
|
||||
'max': stdtypes.i8_ord_max,
|
||||
}, operators={
|
||||
'<': stdtypes.i8_ord_less_than,
|
||||
'<=': stdtypes.i8_ord_less_than_or_equal,
|
||||
'>': stdtypes.i8_ord_greater_than,
|
||||
'>=': stdtypes.i8_ord_greater_than_or_equal,
|
||||
})
|
||||
instance_type_class(Ord, i32, methods={
|
||||
'min': stdtypes.i32_ord_min,
|
||||
'max': stdtypes.i32_ord_max,
|
||||
}, operators={
|
||||
'<': stdtypes.i32_ord_less_than,
|
||||
'<=': stdtypes.i32_ord_less_than_or_equal,
|
||||
'>': stdtypes.i32_ord_greater_than,
|
||||
'>=': stdtypes.i32_ord_greater_than_or_equal,
|
||||
})
|
||||
instance_type_class(Ord, i64, methods={
|
||||
'min': stdtypes.i64_ord_min,
|
||||
'max': stdtypes.i64_ord_max,
|
||||
}, operators={
|
||||
'<': stdtypes.i64_ord_less_than,
|
||||
'<=': stdtypes.i64_ord_less_than_or_equal,
|
||||
'>': stdtypes.i64_ord_greater_than,
|
||||
'>=': stdtypes.i64_ord_greater_than_or_equal,
|
||||
})
|
||||
instance_type_class(Ord, f32, methods={
|
||||
'min': stdtypes.f32_ord_min,
|
||||
'max': stdtypes.f32_ord_max,
|
||||
}, operators={
|
||||
'<': stdtypes.f32_ord_less_than,
|
||||
'<=': stdtypes.f32_ord_less_than_or_equal,
|
||||
'>': stdtypes.f32_ord_greater_than,
|
||||
'>=': stdtypes.f32_ord_greater_than_or_equal,
|
||||
})
|
||||
instance_type_class(Ord, f64, methods={
|
||||
'min': stdtypes.f64_ord_min,
|
||||
'max': stdtypes.f64_ord_max,
|
||||
}, operators={
|
||||
'<': stdtypes.f64_ord_less_than,
|
||||
'<=': stdtypes.f64_ord_less_than_or_equal,
|
||||
'>': stdtypes.f64_ord_greater_than,
|
||||
'>=': stdtypes.f64_ord_greater_than_or_equal,
|
||||
})
|
||||
|
||||
Bits = Type3Class('Bits', [a], methods={
|
||||
Bits = Type3Class('Bits', (a, ), methods={
|
||||
'shl': [a, u32, a], # Logical shift left
|
||||
'shr': [a, u32, a], # Logical shift right
|
||||
'rotl': [a, u32, a], # Rotate bits left
|
||||
@ -186,11 +358,38 @@ Bits = Type3Class('Bits', [a], methods={
|
||||
'^': [a, a, a], # Bit-wise xor
|
||||
})
|
||||
|
||||
instance_type_class(Bits, u8)
|
||||
instance_type_class(Bits, u32)
|
||||
instance_type_class(Bits, u64)
|
||||
instance_type_class(Bits, u8, methods={
|
||||
'shl': stdtypes.u8_bits_logical_shift_left,
|
||||
'shr': stdtypes.u8_bits_logical_shift_right,
|
||||
'rotl': stdtypes.u8_bits_rotate_left,
|
||||
'rotr': stdtypes.u8_bits_rotate_right,
|
||||
}, operators={
|
||||
'&': stdtypes.u8_bits_bitwise_and,
|
||||
'|': stdtypes.u8_bits_bitwise_or,
|
||||
'^': stdtypes.u8_bits_bitwise_xor,
|
||||
})
|
||||
instance_type_class(Bits, u32, methods={
|
||||
'shl': stdtypes.u32_bits_logical_shift_left,
|
||||
'shr': stdtypes.u32_bits_logical_shift_right,
|
||||
'rotl': stdtypes.u32_bits_rotate_left,
|
||||
'rotr': stdtypes.u32_bits_rotate_right,
|
||||
}, operators={
|
||||
'&': stdtypes.u32_bits_bitwise_and,
|
||||
'|': stdtypes.u32_bits_bitwise_or,
|
||||
'^': stdtypes.u32_bits_bitwise_xor,
|
||||
})
|
||||
instance_type_class(Bits, u64, methods={
|
||||
'shl': stdtypes.u64_bits_logical_shift_left,
|
||||
'shr': stdtypes.u64_bits_logical_shift_right,
|
||||
'rotl': stdtypes.u64_bits_rotate_left,
|
||||
'rotr': stdtypes.u64_bits_rotate_right,
|
||||
}, operators={
|
||||
'&': stdtypes.u64_bits_bitwise_and,
|
||||
'|': stdtypes.u64_bits_bitwise_or,
|
||||
'^': stdtypes.u64_bits_bitwise_xor,
|
||||
})
|
||||
|
||||
NatNum = Type3Class('NatNum', [a], methods={}, operators={
|
||||
NatNum = Type3Class('NatNum', (a, ), methods={}, operators={
|
||||
'+': [a, a, a],
|
||||
'-': [a, a, a],
|
||||
'*': [a, a, a],
|
||||
@ -198,35 +397,95 @@ NatNum = Type3Class('NatNum', [a], methods={}, operators={
|
||||
'>>': [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)
|
||||
instance_type_class(NatNum, u32, operators={
|
||||
'+': stdtypes.u32_natnum_add,
|
||||
'-': stdtypes.u32_natnum_sub,
|
||||
'*': stdtypes.u32_natnum_mul,
|
||||
'<<': stdtypes.u32_natnum_arithmic_shift_left,
|
||||
'>>': stdtypes.u32_natnum_arithmic_shift_right,
|
||||
})
|
||||
instance_type_class(NatNum, u64, operators={
|
||||
'+': stdtypes.u64_natnum_add,
|
||||
'-': stdtypes.u64_natnum_sub,
|
||||
'*': stdtypes.u64_natnum_mul,
|
||||
'<<': stdtypes.u64_natnum_arithmic_shift_left,
|
||||
'>>': stdtypes.u64_natnum_arithmic_shift_right,
|
||||
})
|
||||
instance_type_class(NatNum, i32, operators={
|
||||
'+': stdtypes.i32_natnum_add,
|
||||
'-': stdtypes.i32_natnum_sub,
|
||||
'*': stdtypes.i32_natnum_mul,
|
||||
'<<': stdtypes.i32_natnum_arithmic_shift_left,
|
||||
'>>': stdtypes.i32_natnum_arithmic_shift_right,
|
||||
})
|
||||
instance_type_class(NatNum, i64, operators={
|
||||
'+': stdtypes.i64_natnum_add,
|
||||
'-': stdtypes.i64_natnum_sub,
|
||||
'*': stdtypes.i64_natnum_mul,
|
||||
'<<': stdtypes.i64_natnum_arithmic_shift_left,
|
||||
'>>': stdtypes.i64_natnum_arithmic_shift_right,
|
||||
})
|
||||
instance_type_class(NatNum, f32, operators={
|
||||
'+': stdtypes.f32_natnum_add,
|
||||
'-': stdtypes.f32_natnum_sub,
|
||||
'*': stdtypes.f32_natnum_mul,
|
||||
'<<': stdtypes.f32_natnum_arithmic_shift_left,
|
||||
'>>': stdtypes.f32_natnum_arithmic_shift_right,
|
||||
})
|
||||
instance_type_class(NatNum, f64, operators={
|
||||
'+': stdtypes.f64_natnum_add,
|
||||
'-': stdtypes.f64_natnum_sub,
|
||||
'*': stdtypes.f64_natnum_mul,
|
||||
'<<': stdtypes.f64_natnum_arithmic_shift_left,
|
||||
'>>': stdtypes.f64_natnum_arithmic_shift_right,
|
||||
})
|
||||
|
||||
IntNum = Type3Class('IntNum', [a], methods={
|
||||
IntNum = Type3Class('IntNum', (a, ), methods={
|
||||
'abs': [a, a],
|
||||
'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)
|
||||
instance_type_class(IntNum, i32, methods={
|
||||
'abs': stdtypes.i32_intnum_abs,
|
||||
'neg': stdtypes.i32_intnum_neg,
|
||||
})
|
||||
instance_type_class(IntNum, i64, methods={
|
||||
'abs': stdtypes.i64_intnum_abs,
|
||||
'neg': stdtypes.i64_intnum_neg,
|
||||
})
|
||||
instance_type_class(IntNum, f32, methods={
|
||||
'abs': stdtypes.f32_intnum_abs,
|
||||
'neg': stdtypes.f32_intnum_neg,
|
||||
})
|
||||
instance_type_class(IntNum, f64, methods={
|
||||
'abs': stdtypes.f64_intnum_abs,
|
||||
'neg': stdtypes.f64_intnum_neg,
|
||||
})
|
||||
|
||||
Integral = Type3Class('Eq', [a], methods={
|
||||
Integral = Type3Class('Eq', (a, ), methods={
|
||||
}, operators={
|
||||
'//': [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)
|
||||
instance_type_class(Integral, u32, operators={
|
||||
'//': stdtypes.u32_integral_div,
|
||||
'%': stdtypes.u32_integral_rem,
|
||||
})
|
||||
instance_type_class(Integral, u64, operators={
|
||||
'//': stdtypes.u64_integral_div,
|
||||
'%': stdtypes.u64_integral_rem,
|
||||
})
|
||||
instance_type_class(Integral, i32, operators={
|
||||
'//': stdtypes.i32_integral_div,
|
||||
'%': stdtypes.i32_integral_rem,
|
||||
})
|
||||
instance_type_class(Integral, i64, operators={
|
||||
'//': stdtypes.i64_integral_div,
|
||||
'%': stdtypes.i64_integral_rem,
|
||||
})
|
||||
|
||||
Fractional = Type3Class('Fractional', [a], methods={
|
||||
Fractional = Type3Class('Fractional', (a, ), methods={
|
||||
'ceil': [a, a],
|
||||
'floor': [a, a],
|
||||
'trunc': [a, a],
|
||||
@ -235,28 +494,95 @@ Fractional = Type3Class('Fractional', [a], methods={
|
||||
'/': [a, a, a],
|
||||
}, inherited_classes=[NatNum])
|
||||
|
||||
instance_type_class(Fractional, f32)
|
||||
instance_type_class(Fractional, f64)
|
||||
instance_type_class(Fractional, f32, methods={
|
||||
'ceil': stdtypes.f32_fractional_ceil,
|
||||
'floor': stdtypes.f32_fractional_floor,
|
||||
'trunc': stdtypes.f32_fractional_trunc,
|
||||
'nearest': stdtypes.f32_fractional_nearest,
|
||||
}, operators={
|
||||
'/': stdtypes.f32_fractional_div,
|
||||
})
|
||||
instance_type_class(Fractional, f64, methods={
|
||||
'ceil': stdtypes.f64_fractional_ceil,
|
||||
'floor': stdtypes.f64_fractional_floor,
|
||||
'trunc': stdtypes.f64_fractional_trunc,
|
||||
'nearest': stdtypes.f64_fractional_nearest,
|
||||
}, operators={
|
||||
'/': stdtypes.f64_fractional_div,
|
||||
})
|
||||
|
||||
Floating = Type3Class('Floating', [a], methods={
|
||||
Floating = Type3Class('Floating', (a, ), methods={
|
||||
'sqrt': [a, a],
|
||||
}, operators={}, inherited_classes=[Fractional])
|
||||
|
||||
# FIXME: Do we want to expose copysign?
|
||||
|
||||
instance_type_class(Floating, f32)
|
||||
instance_type_class(Floating, f64)
|
||||
instance_type_class(Floating, f32, methods={
|
||||
'sqrt': stdtypes.f32_floating_sqrt,
|
||||
})
|
||||
instance_type_class(Floating, f64, methods={
|
||||
'sqrt': stdtypes.f64_floating_sqrt,
|
||||
})
|
||||
|
||||
Sized_ = Type3Class('Sized', [a], methods={
|
||||
Sized_ = Type3Class('Sized', (a, ), methods={
|
||||
'len': [a, u32],
|
||||
}, operators={}) # FIXME: Once we get type class families, add [] here
|
||||
|
||||
instance_type_class(Sized_, bytes_)
|
||||
instance_type_class(Sized_, bytes_, methods={
|
||||
'len': stdtypes.bytes_sized_len,
|
||||
})
|
||||
|
||||
Foldable = Type3Class('Foldable', [t], methods={
|
||||
'foldl': [[a, b, b], b, t(a), b],
|
||||
Extendable = Type3Class('Extendable', (a, b, ), methods={
|
||||
'extend': [a, b],
|
||||
'wrap': [b, a],
|
||||
}, operators={})
|
||||
|
||||
instance_type_class(Extendable, u8, u32, methods={
|
||||
'extend': stdtypes.u8_u32_extend,
|
||||
'wrap': stdtypes.u8_u32_wrap,
|
||||
})
|
||||
instance_type_class(Extendable, u8, u64, methods={
|
||||
'extend': stdtypes.u8_u64_extend,
|
||||
'wrap': stdtypes.u8_u64_wrap,
|
||||
})
|
||||
instance_type_class(Extendable, u32, u64, methods={
|
||||
'extend': stdtypes.u32_u64_extend,
|
||||
'wrap': stdtypes.u32_u64_wrap,
|
||||
})
|
||||
instance_type_class(Extendable, i8, i32, methods={
|
||||
'extend': stdtypes.i8_i32_extend,
|
||||
'wrap': stdtypes.i8_i32_wrap,
|
||||
})
|
||||
instance_type_class(Extendable, i8, i64, methods={
|
||||
'extend': stdtypes.i8_i64_extend,
|
||||
'wrap': stdtypes.i8_i64_wrap,
|
||||
})
|
||||
instance_type_class(Extendable, i32, i64, methods={
|
||||
'extend': stdtypes.i32_i64_extend,
|
||||
'wrap': stdtypes.i32_i64_wrap,
|
||||
})
|
||||
|
||||
Promotable = Type3Class('Promotable', (a, b, ), methods={
|
||||
'promote': [a, b],
|
||||
'demote': [b, a],
|
||||
}, operators={})
|
||||
|
||||
instance_type_class(Promotable, f32, f64, methods={
|
||||
'promote': stdtypes.f32_f64_promote,
|
||||
'demote': stdtypes.f32_f64_demote,
|
||||
})
|
||||
|
||||
Foldable = Type3Class('Foldable', (t, ), methods={
|
||||
'sum': [t(a), a],
|
||||
'foldl': [[a, b, b], b, t(a), b],
|
||||
}, operators={}, additional_context={
|
||||
'sum': [Constraint_TypeClassInstanceExists(NatNum, (a, ))],
|
||||
})
|
||||
|
||||
instance_type_class(Foldable, static_array, methods={
|
||||
'sum': stdtypes.static_array_sum,
|
||||
})
|
||||
|
||||
PRELUDE_TYPE_CLASSES = {
|
||||
'Eq': Eq,
|
||||
'Ord': Ord,
|
||||
@ -266,6 +592,8 @@ PRELUDE_TYPE_CLASSES = {
|
||||
'Integral': Integral,
|
||||
'Fractional': Fractional,
|
||||
'Floating': Floating,
|
||||
'Extendable': Extendable,
|
||||
'Promotable': Promotable,
|
||||
}
|
||||
|
||||
PRELUDE_OPERATORS = {
|
||||
@ -288,4 +616,7 @@ PRELUDE_METHODS = {
|
||||
**IntNum.methods,
|
||||
**NatNum.methods,
|
||||
**Sized_.methods,
|
||||
**Extendable.methods,
|
||||
**Promotable.methods,
|
||||
**Foldable.methods,
|
||||
}
|
||||
|
||||
@ -1,8 +1,40 @@
|
||||
from . import prelude
|
||||
from .type3 import types as type3types
|
||||
from .type3.routers import NoRouteForTypeException, TypeApplicationRouter
|
||||
from .type3.types import IntType3, Type3
|
||||
|
||||
|
||||
def calculate_alloc_size(typ: type3types.Type3, is_member: bool = False) -> int:
|
||||
def calculate_alloc_size_static_array(is_member: bool, args: tuple[Type3, IntType3]) -> int:
|
||||
if is_member:
|
||||
return 4
|
||||
|
||||
sa_type, sa_len = args
|
||||
|
||||
return sa_len.value * calculate_alloc_size(sa_type, is_member=True)
|
||||
|
||||
def calculate_alloc_size_tuple(is_member: bool, args: tuple[Type3, ...]) -> int:
|
||||
if is_member:
|
||||
return 4
|
||||
|
||||
return sum(
|
||||
calculate_alloc_size(x, is_member=True)
|
||||
for x in args
|
||||
)
|
||||
|
||||
def calculate_alloc_size_struct(is_member: bool, args: tuple[tuple[str, Type3], ...]) -> int:
|
||||
if is_member:
|
||||
return 4
|
||||
|
||||
return sum(
|
||||
calculate_alloc_size(x, is_member=True)
|
||||
for _, x in args
|
||||
)
|
||||
|
||||
ALLOC_SIZE_ROUTER = TypeApplicationRouter[bool, int]()
|
||||
ALLOC_SIZE_ROUTER.add(prelude.static_array, calculate_alloc_size_static_array)
|
||||
ALLOC_SIZE_ROUTER.add(prelude.struct, calculate_alloc_size_struct)
|
||||
ALLOC_SIZE_ROUTER.add(prelude.tuple_, calculate_alloc_size_tuple)
|
||||
|
||||
def calculate_alloc_size(typ: Type3, is_member: bool = False) -> int:
|
||||
if typ in (prelude.u8, prelude.i8, ):
|
||||
return 4 # FIXME: We allocate 4 bytes for every u8 since you load them into an i32
|
||||
|
||||
@ -12,51 +44,20 @@ def calculate_alloc_size(typ: type3types.Type3, is_member: bool = False) -> int:
|
||||
if typ in (prelude.u64, prelude.i64, prelude.f64, ):
|
||||
return 8
|
||||
|
||||
if typ == prelude.bytes_:
|
||||
try:
|
||||
return ALLOC_SIZE_ROUTER(is_member, typ)
|
||||
except NoRouteForTypeException:
|
||||
if is_member:
|
||||
# By default, 'boxed' or 'constructed' types are
|
||||
# stored as pointers when a member of a struct or tuple
|
||||
return 4
|
||||
|
||||
raise NotImplementedError # When does this happen?
|
||||
raise NotImplementedError(typ)
|
||||
|
||||
st_args = prelude.struct.did_construct(typ)
|
||||
if st_args is not None:
|
||||
if is_member:
|
||||
# Structs referred to by other structs or tuples are pointers
|
||||
return 4
|
||||
|
||||
return sum(
|
||||
calculate_alloc_size(x, is_member=True)
|
||||
for x in st_args.values()
|
||||
)
|
||||
|
||||
sa_args = prelude.static_array.did_construct(typ)
|
||||
if sa_args is not None:
|
||||
if is_member:
|
||||
# tuples referred to by other structs or tuples are pointers
|
||||
return 4
|
||||
|
||||
sa_type, sa_len = sa_args
|
||||
|
||||
return sa_len.value * calculate_alloc_size(sa_type, is_member=True)
|
||||
|
||||
tp_args = prelude.tuple_.did_construct(typ)
|
||||
if tp_args is not None:
|
||||
if is_member:
|
||||
# tuples referred to by other structs or tuples are pointers
|
||||
return 4
|
||||
|
||||
size = 0
|
||||
for arg in tp_args:
|
||||
size += calculate_alloc_size(arg, is_member=True)
|
||||
|
||||
return size
|
||||
|
||||
raise NotImplementedError(calculate_alloc_size, typ)
|
||||
|
||||
def calculate_member_offset(st_name: str, st_args: dict[str, type3types.Type3], needle: str) -> int:
|
||||
def calculate_member_offset(st_name: str, st_args: tuple[tuple[str, Type3], ...], needle: str) -> int:
|
||||
result = 0
|
||||
|
||||
for memnam, memtyp in st_args.items():
|
||||
for memnam, memtyp in st_args:
|
||||
if needle == memnam:
|
||||
return result
|
||||
|
||||
|
||||
@ -2,6 +2,8 @@
|
||||
stdlib: Standard types that are not wasm primitives
|
||||
"""
|
||||
from phasm.stdlib import alloc
|
||||
from phasm.type3.routers import TypeVariableLookup
|
||||
from phasm.type3.types import IntType3, Type3
|
||||
from phasm.wasmgenerator import Generator, func_wrapper
|
||||
from phasm.wasmgenerator import VarType_i32 as i32
|
||||
from phasm.wasmgenerator import VarType_i64 as i64
|
||||
@ -39,8 +41,7 @@ def __subscript_bytes__(g: Generator, adr: i32, ofs: i32) -> i32:
|
||||
Returns an index from a bytes value
|
||||
|
||||
If ofs is more than the length of the bytes, this
|
||||
function returns 0, following the 'no undefined behaviour'
|
||||
philosophy.
|
||||
function stop as unreachable.
|
||||
|
||||
adr i32 The pointer for the allocated bytes
|
||||
ofs i32 The offset within the allocated bytes
|
||||
@ -388,444 +389,579 @@ def __u8_rotr__(g: Generator, x: i32, r: i32) -> i32:
|
||||
## ###
|
||||
## class Eq
|
||||
|
||||
def u8_eq_equals(g: Generator) -> None:
|
||||
def u8_eq_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.eq()
|
||||
|
||||
def u32_eq_equals(g: Generator) -> None:
|
||||
def u32_eq_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.eq()
|
||||
|
||||
def u64_eq_equals(g: Generator) -> None:
|
||||
def u64_eq_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i64.eq()
|
||||
|
||||
def i8_eq_equals(g: Generator) -> None:
|
||||
def i8_eq_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.eq()
|
||||
|
||||
def i32_eq_equals(g: Generator) -> None:
|
||||
def i32_eq_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.eq()
|
||||
|
||||
def i64_eq_equals(g: Generator) -> None:
|
||||
def i64_eq_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i64.eq()
|
||||
|
||||
def f32_eq_equals(g: Generator) -> None:
|
||||
def f32_eq_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.f32.eq()
|
||||
|
||||
def f64_eq_equals(g: Generator) -> None:
|
||||
def f64_eq_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.f64.eq()
|
||||
|
||||
def u8_eq_not_equals(g: Generator) -> None:
|
||||
def u8_eq_not_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.ne()
|
||||
|
||||
def u32_eq_not_equals(g: Generator) -> None:
|
||||
def u32_eq_not_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.ne()
|
||||
|
||||
def u64_eq_not_equals(g: Generator) -> None:
|
||||
def u64_eq_not_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i64.ne()
|
||||
|
||||
def i8_eq_not_equals(g: Generator) -> None:
|
||||
def i8_eq_not_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.ne()
|
||||
|
||||
def i32_eq_not_equals(g: Generator) -> None:
|
||||
def i32_eq_not_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.ne()
|
||||
|
||||
def i64_eq_not_equals(g: Generator) -> None:
|
||||
def i64_eq_not_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i64.ne()
|
||||
|
||||
def f32_eq_not_equals(g: Generator) -> None:
|
||||
def f32_eq_not_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.f32.ne()
|
||||
|
||||
def f64_eq_not_equals(g: Generator) -> None:
|
||||
def f64_eq_not_equals(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.f64.ne()
|
||||
|
||||
## ###
|
||||
## class Ord
|
||||
|
||||
def u8_ord_min(g: Generator) -> None:
|
||||
def u8_ord_min(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('call $stdlib.types.__u32_ord_min__')
|
||||
|
||||
def u32_ord_min(g: Generator) -> None:
|
||||
def u32_ord_min(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('call $stdlib.types.__u32_ord_min__')
|
||||
|
||||
def u64_ord_min(g: Generator) -> None:
|
||||
def u64_ord_min(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('call $stdlib.types.__u64_ord_min__')
|
||||
|
||||
def i8_ord_min(g: Generator) -> None:
|
||||
def i8_ord_min(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('call $stdlib.types.__i32_ord_min__')
|
||||
|
||||
def i32_ord_min(g: Generator) -> None:
|
||||
def i32_ord_min(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('call $stdlib.types.__i32_ord_min__')
|
||||
|
||||
def i64_ord_min(g: Generator) -> None:
|
||||
def i64_ord_min(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('call $stdlib.types.__i64_ord_min__')
|
||||
|
||||
def f32_ord_min(g: Generator) -> None:
|
||||
def f32_ord_min(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.f32.min()
|
||||
|
||||
def f64_ord_min(g: Generator) -> None:
|
||||
def f64_ord_min(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.f64.min()
|
||||
|
||||
def u8_ord_max(g: Generator) -> None:
|
||||
def u8_ord_max(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('call $stdlib.types.__u32_ord_max__')
|
||||
|
||||
def u32_ord_max(g: Generator) -> None:
|
||||
def u32_ord_max(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('call $stdlib.types.__u32_ord_max__')
|
||||
|
||||
def u64_ord_max(g: Generator) -> None:
|
||||
def u64_ord_max(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('call $stdlib.types.__u64_ord_max__')
|
||||
|
||||
def i8_ord_max(g: Generator) -> None:
|
||||
def i8_ord_max(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('call $stdlib.types.__i32_ord_max__')
|
||||
|
||||
def i32_ord_max(g: Generator) -> None:
|
||||
def i32_ord_max(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('call $stdlib.types.__i32_ord_max__')
|
||||
|
||||
def i64_ord_max(g: Generator) -> None:
|
||||
def i64_ord_max(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('call $stdlib.types.__i64_ord_max__')
|
||||
|
||||
def f32_ord_max(g: Generator) -> None:
|
||||
def f32_ord_max(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.f32.max()
|
||||
|
||||
def f64_ord_max(g: Generator) -> None:
|
||||
def f64_ord_max(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.f64.max()
|
||||
|
||||
|
||||
def u8_ord_less_than(g: Generator) -> None:
|
||||
def u8_ord_less_than(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.lt_u()
|
||||
|
||||
def u32_ord_less_than(g: Generator) -> None:
|
||||
def u32_ord_less_than(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.lt_u()
|
||||
|
||||
def u64_ord_less_than(g: Generator) -> None:
|
||||
def u64_ord_less_than(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i64.lt_u()
|
||||
|
||||
def i8_ord_less_than(g: Generator) -> None:
|
||||
def i8_ord_less_than(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.lt_s()
|
||||
|
||||
def i32_ord_less_than(g: Generator) -> None:
|
||||
def i32_ord_less_than(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.lt_s()
|
||||
|
||||
def i64_ord_less_than(g: Generator) -> None:
|
||||
def i64_ord_less_than(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i64.lt_s()
|
||||
|
||||
def f32_ord_less_than(g: Generator) -> None:
|
||||
def f32_ord_less_than(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.f32.lt()
|
||||
|
||||
def f64_ord_less_than(g: Generator) -> None:
|
||||
def f64_ord_less_than(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.f64.lt()
|
||||
|
||||
def u8_ord_less_than_or_equal(g: Generator) -> None:
|
||||
def u8_ord_less_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.le_u()
|
||||
|
||||
def u32_ord_less_than_or_equal(g: Generator) -> None:
|
||||
def u32_ord_less_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.le_u()
|
||||
|
||||
def u64_ord_less_than_or_equal(g: Generator) -> None:
|
||||
def u64_ord_less_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i64.le_u()
|
||||
|
||||
def i8_ord_less_than_or_equal(g: Generator) -> None:
|
||||
def i8_ord_less_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.le_s()
|
||||
|
||||
def i32_ord_less_than_or_equal(g: Generator) -> None:
|
||||
def i32_ord_less_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.le_s()
|
||||
|
||||
def i64_ord_less_than_or_equal(g: Generator) -> None:
|
||||
def i64_ord_less_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i64.le_s()
|
||||
|
||||
def f32_ord_less_than_or_equal(g: Generator) -> None:
|
||||
def f32_ord_less_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.f32.le()
|
||||
|
||||
def f64_ord_less_than_or_equal(g: Generator) -> None:
|
||||
def f64_ord_less_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.f64.le()
|
||||
|
||||
def u8_ord_greater_than(g: Generator) -> None:
|
||||
def u8_ord_greater_than(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.gt_u()
|
||||
|
||||
def u32_ord_greater_than(g: Generator) -> None:
|
||||
def u32_ord_greater_than(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.gt_u()
|
||||
|
||||
def u64_ord_greater_than(g: Generator) -> None:
|
||||
def u64_ord_greater_than(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i64.gt_u()
|
||||
|
||||
def i8_ord_greater_than(g: Generator) -> None:
|
||||
def i8_ord_greater_than(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.gt_s()
|
||||
|
||||
def i32_ord_greater_than(g: Generator) -> None:
|
||||
def i32_ord_greater_than(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.gt_s()
|
||||
|
||||
def i64_ord_greater_than(g: Generator) -> None:
|
||||
def i64_ord_greater_than(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i64.gt_s()
|
||||
|
||||
def f32_ord_greater_than(g: Generator) -> None:
|
||||
def f32_ord_greater_than(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.f32.gt()
|
||||
|
||||
def f64_ord_greater_than(g: Generator) -> None:
|
||||
def f64_ord_greater_than(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.f64.gt()
|
||||
|
||||
def u8_ord_greater_than_or_equal(g: Generator) -> None:
|
||||
def u8_ord_greater_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.ge_u()
|
||||
|
||||
def u32_ord_greater_than_or_equal(g: Generator) -> None:
|
||||
def u32_ord_greater_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.ge_u()
|
||||
|
||||
def u64_ord_greater_than_or_equal(g: Generator) -> None:
|
||||
def u64_ord_greater_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i64.ge_u()
|
||||
|
||||
def i8_ord_greater_than_or_equal(g: Generator) -> None:
|
||||
def i8_ord_greater_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.ge_s()
|
||||
|
||||
def i32_ord_greater_than_or_equal(g: Generator) -> None:
|
||||
def i32_ord_greater_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.ge_s()
|
||||
|
||||
def i64_ord_greater_than_or_equal(g: Generator) -> None:
|
||||
def i64_ord_greater_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i64.ge_s()
|
||||
|
||||
def f32_ord_greater_than_or_equal(g: Generator) -> None:
|
||||
def f32_ord_greater_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.f32.ge()
|
||||
|
||||
def f64_ord_greater_than_or_equal(g: Generator) -> None:
|
||||
def f64_ord_greater_than_or_equal(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.f64.ge()
|
||||
|
||||
## ###
|
||||
## class Bits
|
||||
|
||||
def u8_bits_logical_shift_left(g: Generator) -> None:
|
||||
def u8_bits_logical_shift_left(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.shl()
|
||||
g.i32.const(255)
|
||||
g.i32.and_()
|
||||
|
||||
def u32_bits_logical_shift_left(g: Generator) -> None:
|
||||
def u32_bits_logical_shift_left(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.shl()
|
||||
|
||||
def u64_bits_logical_shift_left(g: Generator) -> None:
|
||||
def u64_bits_logical_shift_left(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i64.extend_i32_u()
|
||||
g.i64.shl()
|
||||
|
||||
def u8_bits_logical_shift_right(g: Generator) -> None:
|
||||
def u8_bits_logical_shift_right(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.shr_u()
|
||||
|
||||
def u32_bits_logical_shift_right(g: Generator) -> None:
|
||||
def u32_bits_logical_shift_right(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.shr_u()
|
||||
|
||||
def u64_bits_logical_shift_right(g: Generator) -> None:
|
||||
def u64_bits_logical_shift_right(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i64.extend_i32_u()
|
||||
g.i64.shr_u()
|
||||
|
||||
def u8_bits_rotate_left(g: Generator) -> None:
|
||||
def u8_bits_rotate_left(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('call $stdlib.types.__u8_rotl__')
|
||||
|
||||
def u32_bits_rotate_left(g: Generator) -> None:
|
||||
def u32_bits_rotate_left(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.rotl()
|
||||
|
||||
def u64_bits_rotate_left(g: Generator) -> None:
|
||||
def u64_bits_rotate_left(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i64.extend_i32_u()
|
||||
g.i64.rotl()
|
||||
|
||||
def u8_bits_rotate_right(g: Generator) -> None:
|
||||
def u8_bits_rotate_right(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('call $stdlib.types.__u8_rotr__')
|
||||
|
||||
def u32_bits_rotate_right(g: Generator) -> None:
|
||||
def u32_bits_rotate_right(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.rotr()
|
||||
|
||||
def u64_bits_rotate_right(g: Generator) -> None:
|
||||
def u64_bits_rotate_right(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i64.extend_i32_u()
|
||||
g.i64.rotr()
|
||||
|
||||
def u8_bits_bitwise_and(g: Generator) -> None:
|
||||
def u8_bits_bitwise_and(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.and_()
|
||||
|
||||
def u32_bits_bitwise_and(g: Generator) -> None:
|
||||
def u32_bits_bitwise_and(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.and_()
|
||||
|
||||
def u64_bits_bitwise_and(g: Generator) -> None:
|
||||
def u64_bits_bitwise_and(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i64.and_()
|
||||
|
||||
def u8_bits_bitwise_or(g: Generator) -> None:
|
||||
def u8_bits_bitwise_or(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.or_()
|
||||
|
||||
def u32_bits_bitwise_or(g: Generator) -> None:
|
||||
def u32_bits_bitwise_or(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.or_()
|
||||
|
||||
def u64_bits_bitwise_or(g: Generator) -> None:
|
||||
def u64_bits_bitwise_or(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i64.or_()
|
||||
|
||||
def u8_bits_bitwise_xor(g: Generator) -> None:
|
||||
def u8_bits_bitwise_xor(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.xor()
|
||||
|
||||
def u32_bits_bitwise_xor(g: Generator) -> None:
|
||||
def u32_bits_bitwise_xor(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.xor()
|
||||
|
||||
def u64_bits_bitwise_xor(g: Generator) -> None:
|
||||
def u64_bits_bitwise_xor(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i64.xor()
|
||||
|
||||
## ###
|
||||
## class Fractional
|
||||
|
||||
def f32_fractional_ceil(g: Generator) -> None:
|
||||
def f32_fractional_ceil(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.f32.ceil()
|
||||
|
||||
def f64_fractional_ceil(g: Generator) -> None:
|
||||
def f64_fractional_ceil(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.f64.ceil()
|
||||
|
||||
def f32_fractional_floor(g: Generator) -> None:
|
||||
def f32_fractional_floor(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.f32.floor()
|
||||
|
||||
def f64_fractional_floor(g: Generator) -> None:
|
||||
def f64_fractional_floor(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.f64.floor()
|
||||
|
||||
def f32_fractional_trunc(g: Generator) -> None:
|
||||
def f32_fractional_trunc(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.f32.trunc()
|
||||
|
||||
def f64_fractional_trunc(g: Generator) -> None:
|
||||
def f64_fractional_trunc(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.f64.trunc()
|
||||
|
||||
def f32_fractional_nearest(g: Generator) -> None:
|
||||
def f32_fractional_nearest(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.f32.nearest()
|
||||
|
||||
def f64_fractional_nearest(g: Generator) -> None:
|
||||
def f64_fractional_nearest(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.f64.nearest()
|
||||
|
||||
def f32_fractional_div(g: Generator) -> None:
|
||||
def f32_fractional_div(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.f32.div()
|
||||
|
||||
def f64_fractional_div(g: Generator) -> None:
|
||||
def f64_fractional_div(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.f64.div()
|
||||
|
||||
## ###
|
||||
## class Floating
|
||||
|
||||
def f32_floating_sqrt(g: Generator) -> None:
|
||||
def f32_floating_sqrt(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('f32.sqrt')
|
||||
|
||||
def f64_floating_sqrt(g: Generator) -> None:
|
||||
def f64_floating_sqrt(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('f64.sqrt')
|
||||
|
||||
## ###
|
||||
## class Integral
|
||||
|
||||
def u32_integral_div(g: Generator) -> None:
|
||||
def u32_integral_div(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('i32.div_u')
|
||||
|
||||
def u64_integral_div(g: Generator) -> None:
|
||||
def u64_integral_div(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('i64.div_u')
|
||||
|
||||
def i32_integral_div(g: Generator) -> None:
|
||||
def i32_integral_div(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('i32.div_s')
|
||||
|
||||
def i64_integral_div(g: Generator) -> None:
|
||||
def i64_integral_div(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('i64.div_s')
|
||||
|
||||
def u32_integral_rem(g: Generator) -> None:
|
||||
def u32_integral_rem(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('i32.rem_u')
|
||||
|
||||
def u64_integral_rem(g: Generator) -> None:
|
||||
def u64_integral_rem(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('i64.rem_u')
|
||||
|
||||
def i32_integral_rem(g: Generator) -> None:
|
||||
def i32_integral_rem(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('i32.rem_s')
|
||||
|
||||
def i64_integral_rem(g: Generator) -> None:
|
||||
def i64_integral_rem(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('i64.rem_s')
|
||||
|
||||
## ###
|
||||
## class NatNum
|
||||
|
||||
def u32_natnum_add(g: Generator) -> None:
|
||||
def u32_natnum_add(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('i32.add')
|
||||
|
||||
def u64_natnum_add(g: Generator) -> None:
|
||||
def u64_natnum_add(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('i64.add')
|
||||
|
||||
def i32_natnum_add(g: Generator) -> None:
|
||||
def i32_natnum_add(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('i32.add')
|
||||
|
||||
def i64_natnum_add(g: Generator) -> None:
|
||||
def i64_natnum_add(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('i64.add')
|
||||
|
||||
def f32_natnum_add(g: Generator) -> None:
|
||||
def f32_natnum_add(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('f32.add')
|
||||
|
||||
def f64_natnum_add(g: Generator) -> None:
|
||||
def f64_natnum_add(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('f64.add')
|
||||
|
||||
def u32_natnum_sub(g: Generator) -> None:
|
||||
def u32_natnum_sub(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('i32.sub')
|
||||
|
||||
def u64_natnum_sub(g: Generator) -> None:
|
||||
def u64_natnum_sub(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('i64.sub')
|
||||
|
||||
def i32_natnum_sub(g: Generator) -> None:
|
||||
def i32_natnum_sub(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('i32.sub')
|
||||
|
||||
def i64_natnum_sub(g: Generator) -> None:
|
||||
def i64_natnum_sub(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('i64.sub')
|
||||
|
||||
def f32_natnum_sub(g: Generator) -> None:
|
||||
def f32_natnum_sub(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('f32.sub')
|
||||
|
||||
def f64_natnum_sub(g: Generator) -> None:
|
||||
def f64_natnum_sub(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('f64.sub')
|
||||
|
||||
def u32_natnum_mul(g: Generator) -> None:
|
||||
def u32_natnum_mul(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('i32.mul')
|
||||
|
||||
def u64_natnum_mul(g: Generator) -> None:
|
||||
def u64_natnum_mul(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('i64.mul')
|
||||
|
||||
def i32_natnum_mul(g: Generator) -> None:
|
||||
def i32_natnum_mul(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('i32.mul')
|
||||
|
||||
def i64_natnum_mul(g: Generator) -> None:
|
||||
def i64_natnum_mul(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('i64.mul')
|
||||
|
||||
def f32_natnum_mul(g: Generator) -> None:
|
||||
def f32_natnum_mul(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('f32.mul')
|
||||
|
||||
def f64_natnum_mul(g: Generator) -> None:
|
||||
def f64_natnum_mul(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('f64.mul')
|
||||
|
||||
def u32_natnum_arithmic_shift_left(g: Generator) -> None:
|
||||
def u32_natnum_arithmic_shift_left(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.shl()
|
||||
|
||||
def u64_natnum_arithmic_shift_left(g: Generator) -> None:
|
||||
def u64_natnum_arithmic_shift_left(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i64.extend_i32_u()
|
||||
g.i64.shl()
|
||||
|
||||
def i32_natnum_arithmic_shift_left(g: Generator) -> None:
|
||||
def i32_natnum_arithmic_shift_left(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.shl()
|
||||
|
||||
def i64_natnum_arithmic_shift_left(g: Generator) -> None:
|
||||
def i64_natnum_arithmic_shift_left(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i64.extend_i32_u()
|
||||
g.i64.shl()
|
||||
|
||||
def f32_natnum_arithmic_shift_left(g: Generator) -> None:
|
||||
def f32_natnum_arithmic_shift_left(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('call $stdlib.types.__u32_pow2__')
|
||||
g.f32.convert_i32_u()
|
||||
g.f32.mul()
|
||||
|
||||
def f64_natnum_arithmic_shift_left(g: Generator) -> None:
|
||||
def f64_natnum_arithmic_shift_left(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('call $stdlib.types.__u32_pow2__')
|
||||
g.f64.convert_i32_u()
|
||||
g.f64.mul()
|
||||
|
||||
def u32_natnum_arithmic_shift_right(g: Generator) -> None:
|
||||
def u32_natnum_arithmic_shift_right(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.shr_u()
|
||||
|
||||
def u64_natnum_arithmic_shift_right(g: Generator) -> None:
|
||||
def u64_natnum_arithmic_shift_right(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i64.extend_i32_u()
|
||||
g.i64.shr_u()
|
||||
|
||||
def i32_natnum_arithmic_shift_right(g: Generator) -> None:
|
||||
def i32_natnum_arithmic_shift_right(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.shr_s()
|
||||
|
||||
def i64_natnum_arithmic_shift_right(g: Generator) -> None:
|
||||
def i64_natnum_arithmic_shift_right(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i64.extend_i32_u()
|
||||
g.i64.shr_s()
|
||||
|
||||
def f32_natnum_arithmic_shift_right(g: Generator) -> None:
|
||||
def f32_natnum_arithmic_shift_right(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('call $stdlib.types.__u32_pow2__')
|
||||
g.f32.convert_i32_u()
|
||||
g.f32.div()
|
||||
|
||||
def f64_natnum_arithmic_shift_right(g: Generator) -> None:
|
||||
def f64_natnum_arithmic_shift_right(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('call $stdlib.types.__u32_pow2__')
|
||||
g.f64.convert_i32_u()
|
||||
g.f64.div()
|
||||
@ -833,35 +969,219 @@ def f64_natnum_arithmic_shift_right(g: Generator) -> None:
|
||||
## ###
|
||||
## class IntNum
|
||||
|
||||
def i32_intnum_abs(g: Generator) -> None:
|
||||
def i32_intnum_abs(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('call $stdlib.types.__i32_intnum_abs__')
|
||||
|
||||
def i64_intnum_abs(g: Generator) -> None:
|
||||
def i64_intnum_abs(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.add_statement('call $stdlib.types.__i64_intnum_abs__')
|
||||
|
||||
def f32_intnum_abs(g: Generator) -> None:
|
||||
def f32_intnum_abs(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.f32.abs()
|
||||
|
||||
def f64_intnum_abs(g: Generator) -> None:
|
||||
def f64_intnum_abs(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.f64.abs()
|
||||
|
||||
def i32_intnum_neg(g: Generator) -> None:
|
||||
def i32_intnum_neg(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.const(-1)
|
||||
g.i32.mul()
|
||||
|
||||
def i64_intnum_neg(g: Generator) -> None:
|
||||
def i64_intnum_neg(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i64.const(-1)
|
||||
g.i64.mul()
|
||||
|
||||
def f32_intnum_neg(g: Generator) -> None:
|
||||
def f32_intnum_neg(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.f32.neg()
|
||||
|
||||
def f64_intnum_neg(g: Generator) -> None:
|
||||
def f64_intnum_neg(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.f64.neg()
|
||||
|
||||
## ###
|
||||
## Class Sized
|
||||
|
||||
def bytes_sized_len(g: Generator) -> None:
|
||||
def bytes_sized_len(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
# The length is stored in the first 4 bytes
|
||||
g.i32.load()
|
||||
|
||||
## ###
|
||||
## Extendable
|
||||
|
||||
def u8_u32_extend(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
# No-op
|
||||
# u8 is already stored as u32
|
||||
pass
|
||||
|
||||
def u8_u64_extend(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i64.extend_i32_u()
|
||||
|
||||
def u32_u64_extend(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i64.extend_i32_u()
|
||||
|
||||
def i8_i32_extend(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
# No-op
|
||||
# i8 is already stored as i32
|
||||
pass
|
||||
|
||||
def i8_i64_extend(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i64.extend_i32_s()
|
||||
|
||||
def i32_i64_extend(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i64.extend_i32_s()
|
||||
|
||||
def u8_u32_wrap(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.const(0xFF)
|
||||
g.i32.and_()
|
||||
|
||||
def u8_u64_wrap(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.wrap_i64()
|
||||
g.i32.const(0xFF)
|
||||
g.i32.and_()
|
||||
|
||||
def u32_u64_wrap(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.wrap_i64()
|
||||
|
||||
def i8_i32_wrap(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.const(0xFF)
|
||||
g.i32.and_()
|
||||
|
||||
def i8_i64_wrap(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.wrap_i64()
|
||||
|
||||
def i32_i64_wrap(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.i32.wrap_i64()
|
||||
|
||||
## ###
|
||||
## Promotable
|
||||
|
||||
def f32_f64_promote(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.f64.promote_f32()
|
||||
|
||||
def f32_f64_demote(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
del tv_map
|
||||
g.f32.demote_f64()
|
||||
|
||||
def static_array_sum(g: Generator, tv_map: TypeVariableLookup) -> None:
|
||||
assert len(tv_map) == 1
|
||||
sa_type, sa_len = next(iter(tv_map.values()))
|
||||
assert isinstance(sa_type, Type3)
|
||||
assert isinstance(sa_len, IntType3)
|
||||
|
||||
if sa_len.value < 1:
|
||||
raise NotImplementedError('Default value in case sum is empty')
|
||||
|
||||
# FIXME: We should probably use LOAD_STORE_TYPE_MAP for this?
|
||||
mtyp_map = {
|
||||
'u32': 'i32',
|
||||
'u64': 'i64',
|
||||
'i32': 'i32',
|
||||
'i64': 'i64',
|
||||
'f32': 'f32',
|
||||
'f64': 'f64',
|
||||
}
|
||||
|
||||
# FIXME: We should probably use calc_alloc_size for this?
|
||||
type_var_size_map = {
|
||||
'u32': 4,
|
||||
'u64': 8,
|
||||
'i32': 4,
|
||||
'i64': 8,
|
||||
'f32': 4,
|
||||
'f64': 8,
|
||||
}
|
||||
|
||||
type_var_add_generator = {
|
||||
'u32': u32_natnum_add,
|
||||
'u64': u64_natnum_add,
|
||||
'i32': i32_natnum_add,
|
||||
'i64': i64_natnum_add,
|
||||
'f32': f32_natnum_add,
|
||||
'f64': f64_natnum_add,
|
||||
}
|
||||
|
||||
# By default, constructed types are passed as pointers
|
||||
# FIXME: We don't know what add function to call
|
||||
sa_type_mtyp = mtyp_map.get(sa_type.name, 'i32')
|
||||
sa_type_alloc_size = type_var_size_map.get(sa_type.name, 4)
|
||||
sa_type_add_gen = type_var_add_generator[sa_type.name]
|
||||
|
||||
# Definitions
|
||||
sum_adr = g.temp_var(i32('sum_adr'))
|
||||
sum_stop = g.temp_var(i32('sum_stop'))
|
||||
|
||||
# Stack before: [adr]
|
||||
# Stack after: [sum]
|
||||
|
||||
# adr = {address of what's currently on stack}
|
||||
# Stack: [adr] -> []
|
||||
g.nop(comment=f'Start sum for {sa_type.name}[{sa_len.value}]')
|
||||
g.local.set(sum_adr)
|
||||
|
||||
# stop = adr + ar_len * sa_type_alloc_size
|
||||
# Stack: []
|
||||
g.nop(comment='Calculate address at which to stop looping')
|
||||
g.local.get(sum_adr)
|
||||
g.i32.const(sa_len.value * sa_type_alloc_size)
|
||||
g.i32.add()
|
||||
g.local.set(sum_stop)
|
||||
|
||||
# sum = *adr
|
||||
# Stack: [] -> [sum]
|
||||
g.nop(comment='Get the first array value as starting point')
|
||||
g.local.get(sum_adr)
|
||||
g.add_statement(f'{sa_type_mtyp}.load')
|
||||
|
||||
# Since we did the first one, increase adr
|
||||
# adr = adr + sa_type_alloc_size
|
||||
# Stack: [sum] -> [sum]
|
||||
g.local.get(sum_adr)
|
||||
g.i32.const(sa_type_alloc_size)
|
||||
g.i32.add()
|
||||
g.local.set(sum_adr)
|
||||
|
||||
if sa_len.value > 1:
|
||||
with g.loop(params=[sa_type_mtyp], result=sa_type_mtyp):
|
||||
# sum = sum + *adr
|
||||
# Stack: [sum] -> [sum + *adr]
|
||||
g.nop(comment='Add array value')
|
||||
g.local.get(sum_adr)
|
||||
g.add_statement(f'{sa_type_mtyp}.load')
|
||||
sa_type_add_gen(g, {})
|
||||
|
||||
# adr = adr + sa_type_alloc_size
|
||||
# Stack: [sum] -> [sum]
|
||||
g.nop(comment='Calculate address of the next value')
|
||||
g.local.get(sum_adr)
|
||||
g.i32.const(sa_type_alloc_size)
|
||||
g.i32.add()
|
||||
g.local.tee(sum_adr)
|
||||
|
||||
# loop if adr < stop
|
||||
g.nop(comment='Check if address exceeds array bounds')
|
||||
g.local.get(sum_stop)
|
||||
g.i32.lt_u()
|
||||
g.br_if(0)
|
||||
# else: sum x[1] === x => so we don't need to loop
|
||||
|
||||
g.nop(comment=f'Completed sum for {sa_type.name}[{sa_len.value}]')
|
||||
# End result: [sum]
|
||||
|
||||
@ -3,10 +3,22 @@ This module contains possible constraints generated based on the AST
|
||||
|
||||
These need to be resolved before the program can be compiled.
|
||||
"""
|
||||
from typing import Dict, List, Optional, Tuple, Union
|
||||
from typing import Any, Dict, Iterable, List, Optional, Tuple, Union
|
||||
|
||||
from .. import ourlang, prelude
|
||||
from . import placeholders, typeclasses, types
|
||||
from .placeholders import PlaceholderForType, Type3OrPlaceholder
|
||||
from .routers import NoRouteForTypeException, TypeApplicationRouter
|
||||
from .typeclasses import Type3Class
|
||||
from .types import (
|
||||
IntType3,
|
||||
Type3,
|
||||
TypeApplication_Nullary,
|
||||
TypeApplication_Struct,
|
||||
TypeApplication_TypeInt,
|
||||
TypeApplication_TypeStar,
|
||||
TypeConstructor_Base,
|
||||
TypeConstructor_Struct,
|
||||
)
|
||||
|
||||
|
||||
class Error:
|
||||
@ -32,20 +44,26 @@ class RequireTypeSubstitutes:
|
||||
typing of the program, so this constraint can be updated.
|
||||
"""
|
||||
|
||||
SubstitutionMap = Dict[placeholders.PlaceholderForType, types.Type3]
|
||||
SubstitutionMap = Dict[PlaceholderForType, Type3]
|
||||
|
||||
NewConstraintList = List['ConstraintBase']
|
||||
|
||||
CheckResult = Union[None, SubstitutionMap, Error, NewConstraintList, RequireTypeSubstitutes]
|
||||
|
||||
HumanReadableRet = Tuple[str, Dict[str, Union[str, ourlang.Expression, types.Type3, placeholders.PlaceholderForType]]]
|
||||
HumanReadableRet = Tuple[str, Dict[str, Union[None, int, str, ourlang.Expression, Type3, PlaceholderForType]]]
|
||||
|
||||
class Context:
|
||||
"""
|
||||
Context for constraints
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
__slots__ = ('type_class_instances_existing', )
|
||||
|
||||
# Constraint_TypeClassInstanceExists
|
||||
type_class_instances_existing: set[tuple[Type3Class, tuple[Union[Type3, TypeConstructor_Base[Any], TypeConstructor_Struct], ...]]]
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.type_class_instances_existing = set()
|
||||
|
||||
class ConstraintBase:
|
||||
"""
|
||||
@ -92,23 +110,23 @@ class SameTypeConstraint(ConstraintBase):
|
||||
"""
|
||||
__slots__ = ('type_list', )
|
||||
|
||||
type_list: List[placeholders.Type3OrPlaceholder]
|
||||
type_list: List[Type3OrPlaceholder]
|
||||
|
||||
def __init__(self, *type_list: placeholders.Type3OrPlaceholder, comment: Optional[str] = None) -> None:
|
||||
def __init__(self, *type_list: Type3OrPlaceholder, comment: Optional[str] = None) -> None:
|
||||
super().__init__(comment=comment)
|
||||
|
||||
assert len(type_list) > 1
|
||||
self.type_list = [*type_list]
|
||||
|
||||
def check(self) -> CheckResult:
|
||||
known_types: List[types.Type3] = []
|
||||
known_types: List[Type3] = []
|
||||
phft_list = []
|
||||
for typ in self.type_list:
|
||||
if isinstance(typ, types.Type3):
|
||||
if isinstance(typ, Type3):
|
||||
known_types.append(typ)
|
||||
continue
|
||||
|
||||
if isinstance(typ, placeholders.PlaceholderForType):
|
||||
if isinstance(typ, PlaceholderForType):
|
||||
if typ.resolve_as is not None:
|
||||
known_types.append(typ.resolve_as)
|
||||
else:
|
||||
@ -125,7 +143,7 @@ class SameTypeConstraint(ConstraintBase):
|
||||
if ktyp != first_type:
|
||||
return Error(f'{ktyp:s} must be {first_type:s} instead', comment=self.comment)
|
||||
|
||||
if not placeholders:
|
||||
if not phft_list:
|
||||
return None
|
||||
|
||||
for phft in phft_list:
|
||||
@ -150,25 +168,59 @@ class SameTypeConstraint(ConstraintBase):
|
||||
|
||||
return f'SameTypeConstraint({args}, comment={repr(self.comment)})'
|
||||
|
||||
class SameTypeArgumentConstraint(ConstraintBase):
|
||||
__slots__ = ('tc_var', 'arg_var', )
|
||||
|
||||
tc_var: PlaceholderForType
|
||||
arg_var: PlaceholderForType
|
||||
|
||||
def __init__(self, tc_var: PlaceholderForType, arg_var: PlaceholderForType, *, comment: str) -> None:
|
||||
super().__init__(comment=comment)
|
||||
|
||||
self.tc_var = tc_var
|
||||
self.arg_var = arg_var
|
||||
|
||||
def check(self) -> CheckResult:
|
||||
if self.tc_var.resolve_as is None or self.arg_var.resolve_as is None:
|
||||
return RequireTypeSubstitutes()
|
||||
|
||||
tc_typ = self.tc_var.resolve_as
|
||||
arg_typ = self.arg_var.resolve_as
|
||||
|
||||
if isinstance(tc_typ.application, TypeApplication_Nullary):
|
||||
return Error(f'{tc_typ:s} must be a constructed type instead')
|
||||
|
||||
if isinstance(tc_typ.application, TypeApplication_TypeStar):
|
||||
# Sure, it's a constructed type. But it's like a struct,
|
||||
# though without the way to implement type classes
|
||||
# Presumably, doing a naked `foo :: t a -> a`
|
||||
# doesn't work since you don't have any info on t
|
||||
# So we can let the MustImplementTypeClassConstraint handle it.
|
||||
return None
|
||||
|
||||
# FIXME: This feels sketchy. Shouldn't the type variable
|
||||
# have the exact same number as arguments?
|
||||
if isinstance(tc_typ.application, TypeApplication_TypeInt):
|
||||
if tc_typ.application.arguments[0] == arg_typ:
|
||||
return None
|
||||
|
||||
return Error(f'{tc_typ.application.arguments[0]:s} must be {arg_typ:s} instead')
|
||||
|
||||
raise NotImplementedError(tc_typ, arg_typ)
|
||||
|
||||
class TupleMatchConstraint(ConstraintBase):
|
||||
def __init__(self, exp_type: placeholders.Type3OrPlaceholder, args: List[placeholders.Type3OrPlaceholder], comment: str):
|
||||
__slots__ = ('exp_type', 'args', )
|
||||
|
||||
exp_type: Type3OrPlaceholder
|
||||
args: list[Type3OrPlaceholder]
|
||||
|
||||
def __init__(self, exp_type: Type3OrPlaceholder, args: Iterable[Type3OrPlaceholder], comment: str):
|
||||
super().__init__(comment=comment)
|
||||
|
||||
self.exp_type = exp_type
|
||||
self.args = list(args)
|
||||
|
||||
def check(self) -> CheckResult:
|
||||
exp_type = self.exp_type
|
||||
if isinstance(exp_type, placeholders.PlaceholderForType):
|
||||
if exp_type.resolve_as is None:
|
||||
return RequireTypeSubstitutes()
|
||||
|
||||
exp_type = exp_type.resolve_as
|
||||
|
||||
assert isinstance(exp_type, types.Type3)
|
||||
|
||||
sa_args = prelude.static_array.did_construct(exp_type)
|
||||
if sa_args is not None:
|
||||
def _generate_static_array(self, sa_args: tuple[Type3, IntType3]) -> CheckResult:
|
||||
sa_type, sa_len = sa_args
|
||||
|
||||
if sa_len.value != len(self.args):
|
||||
@ -179,8 +231,7 @@ class TupleMatchConstraint(ConstraintBase):
|
||||
for arg in self.args
|
||||
]
|
||||
|
||||
tp_args = prelude.tuple_.did_construct(exp_type)
|
||||
if tp_args is not None:
|
||||
def _generate_tuple(self, tp_args: tuple[Type3, ...]) -> CheckResult:
|
||||
if len(tp_args) != len(self.args):
|
||||
return Error('Mismatch between applied types argument count', comment=self.comment)
|
||||
|
||||
@ -189,91 +240,85 @@ class TupleMatchConstraint(ConstraintBase):
|
||||
for arg, oth_arg in zip(self.args, tp_args, strict=True)
|
||||
]
|
||||
|
||||
raise NotImplementedError(exp_type)
|
||||
|
||||
class CastableConstraint(ConstraintBase):
|
||||
"""
|
||||
A type can be cast to another type
|
||||
"""
|
||||
__slots__ = ('from_type3', 'to_type3', )
|
||||
|
||||
from_type3: placeholders.Type3OrPlaceholder
|
||||
to_type3: placeholders.Type3OrPlaceholder
|
||||
|
||||
def __init__(self, from_type3: placeholders.Type3OrPlaceholder, to_type3: placeholders.Type3OrPlaceholder, comment: Optional[str] = None) -> None:
|
||||
super().__init__(comment=comment)
|
||||
|
||||
self.from_type3 = from_type3
|
||||
self.to_type3 = to_type3
|
||||
GENERATE_ROUTER = TypeApplicationRouter['TupleMatchConstraint', CheckResult]()
|
||||
GENERATE_ROUTER.add(prelude.static_array, _generate_static_array)
|
||||
GENERATE_ROUTER.add(prelude.tuple_, _generate_tuple)
|
||||
|
||||
def check(self) -> CheckResult:
|
||||
ftyp = self.from_type3
|
||||
if isinstance(ftyp, placeholders.PlaceholderForType) and ftyp.resolve_as is not None:
|
||||
ftyp = ftyp.resolve_as
|
||||
|
||||
ttyp = self.to_type3
|
||||
if isinstance(ttyp, placeholders.PlaceholderForType) and ttyp.resolve_as is not None:
|
||||
ttyp = ttyp.resolve_as
|
||||
|
||||
if isinstance(ftyp, placeholders.PlaceholderForType) or isinstance(ttyp, placeholders.PlaceholderForType):
|
||||
exp_type = self.exp_type
|
||||
if isinstance(exp_type, PlaceholderForType):
|
||||
if exp_type.resolve_as is None:
|
||||
return RequireTypeSubstitutes()
|
||||
|
||||
if ftyp is prelude.u8 and ttyp is prelude.u32:
|
||||
return None
|
||||
exp_type = exp_type.resolve_as
|
||||
|
||||
return Error(f'Cannot cast {ftyp.name} to {ttyp.name}')
|
||||
|
||||
def human_readable(self) -> HumanReadableRet:
|
||||
return (
|
||||
'{to_type3}({from_type3})',
|
||||
{
|
||||
'to_type3': self.to_type3,
|
||||
'from_type3': self.from_type3,
|
||||
},
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'CastableConstraint({repr(self.from_type3)}, {repr(self.to_type3)}, comment={repr(self.comment)})'
|
||||
try:
|
||||
return self.__class__.GENERATE_ROUTER(self, exp_type)
|
||||
except NoRouteForTypeException:
|
||||
raise NotImplementedError(exp_type)
|
||||
|
||||
class MustImplementTypeClassConstraint(ConstraintBase):
|
||||
"""
|
||||
A type must implement a given type class
|
||||
"""
|
||||
__slots__ = ('type_class3', 'type3', )
|
||||
__slots__ = ('context', 'type_class3', 'types', )
|
||||
|
||||
type_class3: typeclasses.Type3Class
|
||||
type3: placeholders.Type3OrPlaceholder
|
||||
context: Context
|
||||
type_class3: Type3Class
|
||||
types: list[Type3OrPlaceholder]
|
||||
|
||||
def __init__(self, type_class3: typeclasses.Type3Class, type3: placeholders.Type3OrPlaceholder, comment: Optional[str] = None) -> None:
|
||||
def __init__(self, context: Context, type_class3: Type3Class, typ_list: list[Type3OrPlaceholder], comment: Optional[str] = None) -> None:
|
||||
super().__init__(comment=comment)
|
||||
|
||||
self.context = context
|
||||
self.type_class3 = type_class3
|
||||
self.type3 = type3
|
||||
self.types = typ_list
|
||||
|
||||
def check(self) -> CheckResult:
|
||||
typ = self.type3
|
||||
if isinstance(typ, placeholders.PlaceholderForType) and typ.resolve_as is not None:
|
||||
typ_list: list[Type3 | TypeConstructor_Base[Any] | TypeConstructor_Struct] = []
|
||||
for typ in self.types:
|
||||
if isinstance(typ, PlaceholderForType) and typ.resolve_as is not None:
|
||||
typ = typ.resolve_as
|
||||
|
||||
if isinstance(typ, placeholders.PlaceholderForType):
|
||||
if isinstance(typ, PlaceholderForType):
|
||||
return RequireTypeSubstitutes()
|
||||
|
||||
if self.type_class3 in typ.classes:
|
||||
if isinstance(typ.application, (TypeApplication_Nullary, TypeApplication_Struct, )):
|
||||
typ_list.append(typ)
|
||||
continue
|
||||
|
||||
if isinstance(typ.application, (TypeApplication_TypeInt, TypeApplication_TypeStar)):
|
||||
typ_list.append(typ.application.constructor)
|
||||
continue
|
||||
|
||||
raise NotImplementedError(typ, typ.application)
|
||||
|
||||
assert len(typ_list) == len(self.types)
|
||||
|
||||
key = (self.type_class3, tuple(typ_list), )
|
||||
if key in self.context.type_class_instances_existing:
|
||||
return None
|
||||
|
||||
return Error(f'{typ.name} does not implement the {self.type_class3} type class')
|
||||
typ_cls_name = self.type_class3 if isinstance(self.type_class3, str) else self.type_class3.name
|
||||
typ_name_list = ' '.join(x.name for x in typ_list)
|
||||
return Error(f'Missing type class instantation: {typ_cls_name} {typ_name_list}')
|
||||
|
||||
def human_readable(self) -> HumanReadableRet:
|
||||
keys = {
|
||||
f'type{idx}': typ
|
||||
for idx, typ in enumerate(self.types)
|
||||
}
|
||||
|
||||
return (
|
||||
'{type3} derives {type_class3}',
|
||||
'Exists instance {type_class3} ' + ' '.join(f'{{{x}}}' for x in keys),
|
||||
{
|
||||
'type_class3': str(self.type_class3),
|
||||
'type3': self.type3,
|
||||
**keys,
|
||||
},
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'MustImplementTypeClassConstraint({repr(self.type_class3)}, {repr(self.type3)}, comment={repr(self.comment)})'
|
||||
return f'MustImplementTypeClassConstraint({repr(self.type_class3)}, {repr(self.types)}, comment={repr(self.comment)})'
|
||||
|
||||
class LiteralFitsConstraint(ConstraintBase):
|
||||
"""
|
||||
@ -281,12 +326,12 @@ class LiteralFitsConstraint(ConstraintBase):
|
||||
"""
|
||||
__slots__ = ('type3', 'literal', )
|
||||
|
||||
type3: placeholders.Type3OrPlaceholder
|
||||
type3: Type3OrPlaceholder
|
||||
literal: Union[ourlang.ConstantPrimitive, ourlang.ConstantBytes, ourlang.ConstantTuple, ourlang.ConstantStruct]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
type3: placeholders.Type3OrPlaceholder,
|
||||
type3: Type3OrPlaceholder,
|
||||
literal: Union[ourlang.ConstantPrimitive, ourlang.ConstantBytes, ourlang.ConstantTuple, ourlang.ConstantStruct],
|
||||
comment: Optional[str] = None,
|
||||
) -> None:
|
||||
@ -295,6 +340,91 @@ class LiteralFitsConstraint(ConstraintBase):
|
||||
self.type3 = type3
|
||||
self.literal = literal
|
||||
|
||||
def _generate_static_array(self, sa_args: tuple[Type3, IntType3]) -> CheckResult:
|
||||
if not isinstance(self.literal, ourlang.ConstantTuple):
|
||||
return Error('Must be tuple', comment=self.comment)
|
||||
|
||||
sa_type, sa_len = sa_args
|
||||
|
||||
if sa_len.value != len(self.literal.value):
|
||||
return Error('Member count mismatch', comment=self.comment)
|
||||
|
||||
res: list[ConstraintBase] = []
|
||||
|
||||
res.extend(
|
||||
LiteralFitsConstraint(sa_type, y)
|
||||
for y in self.literal.value
|
||||
)
|
||||
|
||||
# Generate placeholders so each Literal expression
|
||||
# gets updated when we figure out the type of the
|
||||
# expression the literal is used in
|
||||
res.extend(
|
||||
SameTypeConstraint(sa_type, PlaceholderForType([y]))
|
||||
for y in self.literal.value
|
||||
)
|
||||
|
||||
return res
|
||||
|
||||
def _generate_struct(self, st_args: tuple[tuple[str, Type3], ...]) -> CheckResult:
|
||||
if not isinstance(self.literal, ourlang.ConstantStruct):
|
||||
return Error('Must be struct')
|
||||
|
||||
if len(st_args) != len(self.literal.value):
|
||||
return Error('Struct element count mismatch')
|
||||
|
||||
res: list[ConstraintBase] = []
|
||||
|
||||
res.extend(
|
||||
LiteralFitsConstraint(x, y)
|
||||
for (_, x), y in zip(st_args, self.literal.value, strict=True)
|
||||
)
|
||||
|
||||
# Generate placeholders so each Literal expression
|
||||
# gets updated when we figure out the type of the
|
||||
# expression the literal is used in
|
||||
res.extend(
|
||||
SameTypeConstraint(x_t, PlaceholderForType([y]), comment=f'{self.literal.struct_type3.name}.{x_n}')
|
||||
for (x_n, x_t, ), y in zip(st_args, self.literal.value, strict=True)
|
||||
)
|
||||
|
||||
res.append(SameTypeConstraint(
|
||||
self.literal.struct_type3,
|
||||
self.type3,
|
||||
comment='Struct types must match',
|
||||
))
|
||||
|
||||
return res
|
||||
|
||||
def _generate_tuple(self, tp_args: tuple[Type3, ...]) -> CheckResult:
|
||||
if not isinstance(self.literal, ourlang.ConstantTuple):
|
||||
return Error('Must be tuple', comment=self.comment)
|
||||
|
||||
if len(tp_args) != len(self.literal.value):
|
||||
return Error('Tuple element count mismatch', comment=self.comment)
|
||||
|
||||
res: list[ConstraintBase] = []
|
||||
|
||||
res.extend(
|
||||
LiteralFitsConstraint(x, y)
|
||||
for x, y in zip(tp_args, self.literal.value, strict=True)
|
||||
)
|
||||
|
||||
# Generate placeholders so each Literal expression
|
||||
# gets updated when we figure out the type of the
|
||||
# expression the literal is used in
|
||||
res.extend(
|
||||
SameTypeConstraint(x, PlaceholderForType([y]))
|
||||
for x, y in zip(tp_args, self.literal.value, strict=True)
|
||||
)
|
||||
|
||||
return res
|
||||
|
||||
GENERATE_ROUTER = TypeApplicationRouter['LiteralFitsConstraint', CheckResult]()
|
||||
GENERATE_ROUTER.add(prelude.static_array, _generate_static_array)
|
||||
GENERATE_ROUTER.add(prelude.struct, _generate_struct)
|
||||
GENERATE_ROUTER.add(prelude.tuple_, _generate_tuple)
|
||||
|
||||
def check(self) -> CheckResult:
|
||||
int_table: Dict[str, Tuple[int, bool]] = {
|
||||
'u8': (1, False),
|
||||
@ -310,7 +440,7 @@ class LiteralFitsConstraint(ConstraintBase):
|
||||
'f64': None,
|
||||
}
|
||||
|
||||
if isinstance(self.type3, placeholders.PlaceholderForType):
|
||||
if isinstance(self.type3, PlaceholderForType):
|
||||
if self.type3.resolve_as is None:
|
||||
return RequireTypeSubstitutes()
|
||||
|
||||
@ -345,80 +475,12 @@ class LiteralFitsConstraint(ConstraintBase):
|
||||
|
||||
return Error('Must be bytes', comment=self.comment) # FIXME: Add line information
|
||||
|
||||
res: NewConstraintList
|
||||
exp_type = self.type3
|
||||
|
||||
assert isinstance(self.type3, types.Type3)
|
||||
|
||||
tp_args = prelude.tuple_.did_construct(self.type3)
|
||||
if tp_args is not None:
|
||||
if not isinstance(self.literal, ourlang.ConstantTuple):
|
||||
return Error('Must be tuple', comment=self.comment)
|
||||
|
||||
if len(tp_args) != len(self.literal.value):
|
||||
return Error('Tuple element count mismatch', comment=self.comment)
|
||||
|
||||
res = []
|
||||
|
||||
res.extend(
|
||||
LiteralFitsConstraint(x, y)
|
||||
for x, y in zip(tp_args, self.literal.value, strict=True)
|
||||
)
|
||||
res.extend(
|
||||
SameTypeConstraint(x, y.type3)
|
||||
for x, y in zip(tp_args, self.literal.value, strict=True)
|
||||
)
|
||||
|
||||
return res
|
||||
|
||||
sa_args = prelude.static_array.did_construct(self.type3)
|
||||
if sa_args is not None:
|
||||
if not isinstance(self.literal, ourlang.ConstantTuple):
|
||||
return Error('Must be tuple', comment=self.comment)
|
||||
|
||||
sa_type, sa_len = sa_args
|
||||
|
||||
if sa_len.value != len(self.literal.value):
|
||||
return Error('Member count mismatch', comment=self.comment)
|
||||
|
||||
res = []
|
||||
|
||||
res.extend(
|
||||
LiteralFitsConstraint(sa_type, y)
|
||||
for y in self.literal.value
|
||||
)
|
||||
res.extend(
|
||||
SameTypeConstraint(sa_type, y.type3)
|
||||
for y in self.literal.value
|
||||
)
|
||||
|
||||
return res
|
||||
|
||||
st_args = prelude.struct.did_construct(self.type3)
|
||||
if st_args is not None:
|
||||
if not isinstance(self.literal, ourlang.ConstantStruct):
|
||||
return Error('Must be struct')
|
||||
|
||||
if self.literal.struct_name != self.type3.name:
|
||||
return Error('Struct mismatch')
|
||||
|
||||
|
||||
if len(st_args) != len(self.literal.value):
|
||||
return Error('Struct element count mismatch')
|
||||
|
||||
res = []
|
||||
|
||||
res.extend(
|
||||
LiteralFitsConstraint(x, y)
|
||||
for x, y in zip(st_args.values(), self.literal.value, strict=True)
|
||||
)
|
||||
res.extend(
|
||||
SameTypeConstraint(x_t, y.type3, comment=f'{self.literal.struct_name}.{x_n}')
|
||||
for (x_n, x_t, ), y in zip(st_args.items(), self.literal.value, strict=True)
|
||||
)
|
||||
|
||||
return res
|
||||
|
||||
raise NotImplementedError(self.type3, self.literal)
|
||||
try:
|
||||
return self.__class__.GENERATE_ROUTER(self, exp_type)
|
||||
except NoRouteForTypeException:
|
||||
raise NotImplementedError(exp_type)
|
||||
|
||||
def human_readable(self) -> HumanReadableRet:
|
||||
return (
|
||||
@ -436,73 +498,75 @@ class CanBeSubscriptedConstraint(ConstraintBase):
|
||||
"""
|
||||
A value that is subscipted, i.e. a[0] (tuple) or a[b] (static array)
|
||||
"""
|
||||
__slots__ = ('ret_type3', 'type3', 'index', 'index_type3', )
|
||||
__slots__ = ('ret_type3', 'type3', 'index_type3', 'index_const', )
|
||||
|
||||
ret_type3: placeholders.Type3OrPlaceholder
|
||||
type3: placeholders.Type3OrPlaceholder
|
||||
index: ourlang.Expression
|
||||
index_type3: placeholders.Type3OrPlaceholder
|
||||
ret_type3: PlaceholderForType
|
||||
type3: PlaceholderForType
|
||||
index_type3: PlaceholderForType
|
||||
index_const: int | None
|
||||
|
||||
def __init__(self, ret_type3: placeholders.Type3OrPlaceholder, type3: placeholders.Type3OrPlaceholder, index: ourlang.Expression, comment: Optional[str] = None) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
ret_type3: PlaceholderForType,
|
||||
type3: PlaceholderForType,
|
||||
index_type3: PlaceholderForType,
|
||||
index_const: int | None,
|
||||
comment: Optional[str] = None,
|
||||
) -> None:
|
||||
super().__init__(comment=comment)
|
||||
|
||||
self.ret_type3 = ret_type3
|
||||
self.type3 = type3
|
||||
self.index = index
|
||||
self.index_type3 = index.type3
|
||||
self.index_type3 = index_type3
|
||||
self.index_const = index_const
|
||||
|
||||
def check(self) -> CheckResult:
|
||||
exp_type = self.type3
|
||||
if isinstance(exp_type, placeholders.PlaceholderForType):
|
||||
if exp_type.resolve_as is None:
|
||||
return RequireTypeSubstitutes()
|
||||
|
||||
exp_type = exp_type.resolve_as
|
||||
|
||||
assert isinstance(exp_type, types.Type3)
|
||||
|
||||
sa_args = prelude.static_array.did_construct(exp_type)
|
||||
if sa_args is not None:
|
||||
sa_type, sa_len = sa_args
|
||||
|
||||
result: List[ConstraintBase] = [
|
||||
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'),
|
||||
]
|
||||
|
||||
if isinstance(self.index, ourlang.ConstantPrimitive):
|
||||
assert isinstance(self.index.value, int)
|
||||
|
||||
if self.index.value < 0 or sa_len.value <= self.index.value:
|
||||
return Error('Tuple index out of range')
|
||||
|
||||
return result
|
||||
|
||||
# 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
|
||||
# we use a[0] and a[1] and allow for a[2] and on.
|
||||
tp_args = prelude.tuple_.did_construct(exp_type)
|
||||
if tp_args is not None:
|
||||
if not isinstance(self.index, ourlang.ConstantPrimitive):
|
||||
return Error('Must index with literal')
|
||||
|
||||
if not isinstance(self.index.value, int):
|
||||
return Error('Must index with integer literal')
|
||||
|
||||
if self.index.value < 0 or len(tp_args) <= self.index.value:
|
||||
return Error('Tuple index out of range')
|
||||
|
||||
return [
|
||||
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}'),
|
||||
]
|
||||
|
||||
if exp_type is prelude.bytes_:
|
||||
def _generate_bytes(self) -> CheckResult:
|
||||
return [
|
||||
SameTypeConstraint(prelude.u32, self.index_type3, comment='([]) :: bytes -> u32 -> u8'),
|
||||
SameTypeConstraint(prelude.u8, self.ret_type3, comment='([]) :: bytes -> u32 -> u8'),
|
||||
]
|
||||
|
||||
def _generate_static_array(self, sa_args: tuple[Type3, IntType3]) -> CheckResult:
|
||||
sa_type, sa_len = sa_args
|
||||
|
||||
if self.index_const is not None and (self.index_const < 0 or sa_len.value <= self.index_const):
|
||||
return Error('Tuple index out of range')
|
||||
|
||||
return [
|
||||
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'),
|
||||
]
|
||||
|
||||
def _generate_tuple(self, tp_args: tuple[Type3, ...]) -> CheckResult:
|
||||
# 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
|
||||
# we use a[0] and a[1] and allow for a[2] and on.
|
||||
|
||||
if self.index_const is None:
|
||||
return Error('Must index with integer literal')
|
||||
|
||||
if self.index_const < 0 or len(tp_args) <= self.index_const:
|
||||
return Error('Tuple index out of range')
|
||||
|
||||
return [
|
||||
SameTypeConstraint(prelude.u32, self.index_type3, comment='([]) :: Subscriptable a => a b -> u32 -> b'),
|
||||
SameTypeConstraint(tp_args[self.index_const], self.ret_type3, comment=f'Tuple subscript index {self.index_const}'),
|
||||
]
|
||||
|
||||
GENERATE_ROUTER = TypeApplicationRouter['CanBeSubscriptedConstraint', CheckResult]()
|
||||
GENERATE_ROUTER.add_n(prelude.bytes_, _generate_bytes)
|
||||
GENERATE_ROUTER.add(prelude.static_array, _generate_static_array)
|
||||
GENERATE_ROUTER.add(prelude.tuple_, _generate_tuple)
|
||||
|
||||
def check(self) -> CheckResult:
|
||||
if self.type3.resolve_as is None:
|
||||
return RequireTypeSubstitutes()
|
||||
|
||||
exp_type = self.type3.resolve_as
|
||||
|
||||
try:
|
||||
return self.__class__.GENERATE_ROUTER(self, exp_type)
|
||||
except NoRouteForTypeException:
|
||||
return Error(f'{exp_type.name} cannot be subscripted')
|
||||
|
||||
def human_readable(self) -> HumanReadableRet:
|
||||
@ -510,9 +574,9 @@ class CanBeSubscriptedConstraint(ConstraintBase):
|
||||
'{type3}[{index}]',
|
||||
{
|
||||
'type3': self.type3,
|
||||
'index': self.index,
|
||||
'index': self.index_type3 if self.index_const is None else self.index_const,
|
||||
},
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'CanBeSubscriptedConstraint({repr(self.type3)}, {repr(self.index)}, comment={repr(self.comment)})'
|
||||
return f'CanBeSubscriptedConstraint({self.ret_type3!r}, {self.type3!r}, {self.index_type3!r}, {self.index_const!r}, comment={repr(self.comment)})'
|
||||
|
||||
@ -6,109 +6,180 @@ The constraints solver can then try to resolve all constraints.
|
||||
from typing import Generator, List
|
||||
|
||||
from .. import ourlang, prelude
|
||||
from . import functions as functions
|
||||
from . import placeholders as placeholders
|
||||
from . import typeclasses as typeclasses
|
||||
from . import types as type3types
|
||||
from .constraints import (
|
||||
CanBeSubscriptedConstraint,
|
||||
CastableConstraint,
|
||||
ConstraintBase,
|
||||
Context,
|
||||
LiteralFitsConstraint,
|
||||
MustImplementTypeClassConstraint,
|
||||
SameTypeArgumentConstraint,
|
||||
SameTypeConstraint,
|
||||
TupleMatchConstraint,
|
||||
)
|
||||
from .functions import (
|
||||
Constraint_TypeClassInstanceExists,
|
||||
FunctionSignature,
|
||||
TypeVariable,
|
||||
TypeVariableApplication_Unary,
|
||||
)
|
||||
from .placeholders import PlaceholderForType
|
||||
from .types import Type3, TypeApplication_Struct
|
||||
|
||||
ConstraintGenerator = Generator[ConstraintBase, None, None]
|
||||
|
||||
def phasm_type3_generate_constraints(inp: ourlang.Module) -> List[ConstraintBase]:
|
||||
ctx = Context()
|
||||
ctx.type_class_instances_existing.update(prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING)
|
||||
|
||||
return [*module(ctx, inp)]
|
||||
|
||||
def constant(ctx: Context, inp: ourlang.Constant) -> ConstraintGenerator:
|
||||
def constant(ctx: Context, inp: ourlang.Constant, phft: PlaceholderForType) -> ConstraintGenerator:
|
||||
if isinstance(inp, (ourlang.ConstantPrimitive, ourlang.ConstantBytes, ourlang.ConstantTuple, ourlang.ConstantStruct)):
|
||||
yield LiteralFitsConstraint(
|
||||
inp.type3, inp,
|
||||
phft, inp,
|
||||
comment='The given literal must fit the expected type'
|
||||
)
|
||||
return
|
||||
|
||||
raise NotImplementedError(constant, inp)
|
||||
|
||||
def expression(ctx: Context, inp: ourlang.Expression) -> ConstraintGenerator:
|
||||
if isinstance(inp, ourlang.Constant):
|
||||
yield from constant(ctx, inp)
|
||||
return
|
||||
def expression_binary_op(ctx: Context, inp: ourlang.BinaryOp, phft: PlaceholderForType) -> ConstraintGenerator:
|
||||
return _expression_function_call(
|
||||
ctx,
|
||||
f'({inp.operator.name})',
|
||||
inp.operator.signature,
|
||||
[inp.left, inp.right],
|
||||
inp,
|
||||
phft,
|
||||
)
|
||||
|
||||
if isinstance(inp, ourlang.VariableReference):
|
||||
yield SameTypeConstraint(inp.variable.type3, inp.type3,
|
||||
comment=f'typeOf("{inp.variable.name}") == typeOf({inp.variable.name})')
|
||||
return
|
||||
def expression_function_call(ctx: Context, inp: ourlang.FunctionCall, phft: PlaceholderForType) -> ConstraintGenerator:
|
||||
return _expression_function_call(
|
||||
ctx,
|
||||
inp.function.name,
|
||||
inp.function.signature,
|
||||
inp.arguments,
|
||||
inp,
|
||||
phft,
|
||||
)
|
||||
|
||||
if isinstance(inp, ourlang.UnaryOp):
|
||||
if 'cast' == inp.operator:
|
||||
yield from expression(ctx, inp.right)
|
||||
yield CastableConstraint(inp.right.type3, inp.type3)
|
||||
return
|
||||
def _expression_function_call(
|
||||
ctx: Context,
|
||||
func_name: str,
|
||||
signature: FunctionSignature,
|
||||
arguments: list[ourlang.Expression],
|
||||
return_expr: ourlang.Expression,
|
||||
return_phft: PlaceholderForType,
|
||||
) -> ConstraintGenerator:
|
||||
"""
|
||||
Generates all type-level constraints for a function call.
|
||||
|
||||
raise NotImplementedError(expression, inp, inp.operator)
|
||||
|
||||
if isinstance(inp, ourlang.BinaryOp) or isinstance(inp, ourlang.FunctionCall):
|
||||
signature = inp.operator.signature if isinstance(inp, ourlang.BinaryOp) else inp.function.signature
|
||||
arguments = [inp.left, inp.right] if isinstance(inp, ourlang.BinaryOp) else inp.arguments
|
||||
|
||||
func_name = f'({inp.operator.name})' if isinstance(inp, ourlang.BinaryOp) else inp.function.name
|
||||
|
||||
type_var_map = {
|
||||
x: placeholders.PlaceholderForType([])
|
||||
for x in signature.args
|
||||
if isinstance(x, functions.TypeVariable)
|
||||
A Binary operator functions pretty much the same as a function call
|
||||
with two arguments - it's only a syntactic difference.
|
||||
"""
|
||||
# First create placeholders for all arguments, and generate their constraints
|
||||
arg_placeholders = {
|
||||
arg_expr: PlaceholderForType([arg_expr])
|
||||
for arg_expr in arguments
|
||||
}
|
||||
arg_placeholders[return_expr] = return_phft
|
||||
|
||||
for call_arg in arguments:
|
||||
yield from expression(ctx, call_arg)
|
||||
yield from expression(ctx, call_arg, arg_placeholders[call_arg])
|
||||
|
||||
for type_var, constraint_list in signature.context.constraints.items():
|
||||
assert type_var in type_var_map # When can this happen?
|
||||
# Then generate placeholders the function signature
|
||||
# and apply constraints that the function requires
|
||||
# Skip any fully reference types
|
||||
# Making this a map ensures that if a function signature has
|
||||
# the same type on multiple arguments, we only get one
|
||||
# placeholder here. These don't need to update anything once
|
||||
# subsituted - that's done by arg_placeholders.
|
||||
type_var_map = {
|
||||
x: PlaceholderForType([])
|
||||
for x in signature.args
|
||||
if isinstance(x, TypeVariable)
|
||||
}
|
||||
|
||||
for constraint in constraint_list:
|
||||
if isinstance(constraint, functions.TypeVariableConstraint_TypeHasTypeClass):
|
||||
for constraint in signature.context.constraints:
|
||||
if isinstance(constraint, Constraint_TypeClassInstanceExists):
|
||||
yield MustImplementTypeClassConstraint(
|
||||
ctx,
|
||||
constraint.type_class3,
|
||||
type_var_map[type_var],
|
||||
[type_var_map[x] for x in constraint.types],
|
||||
)
|
||||
continue
|
||||
|
||||
raise NotImplementedError(constraint)
|
||||
|
||||
for arg_no, (sig_part, arg_expr) in enumerate(zip(signature.args, arguments + [inp], strict=True)):
|
||||
# If some of the function arguments are type constructors,
|
||||
# we need to deal with those separately.
|
||||
# That is, given `foo :: t a -> a` we need to ensure
|
||||
# that both a's are the same.
|
||||
for sig_arg in signature.args:
|
||||
if isinstance(sig_arg, Type3):
|
||||
# Not a type variable at all
|
||||
continue
|
||||
|
||||
if sig_arg.application.constructor is None:
|
||||
# Not a type variable for a type constructor
|
||||
continue
|
||||
|
||||
if not isinstance(sig_arg.application, TypeVariableApplication_Unary):
|
||||
raise NotImplementedError(sig_arg.application)
|
||||
|
||||
assert sig_arg.application.arguments in type_var_map # When does this happen?
|
||||
|
||||
yield SameTypeArgumentConstraint(
|
||||
type_var_map[sig_arg],
|
||||
type_var_map[sig_arg.application.arguments],
|
||||
comment=f'Ensure `{sig_arg.application.arguments.name}` matches in {signature}',
|
||||
)
|
||||
|
||||
# Lastly, tie the signature and expression together
|
||||
for arg_no, (sig_part, arg_expr) in enumerate(zip(signature.args, arguments + [return_expr], strict=True)):
|
||||
if arg_no == len(arguments):
|
||||
comment = f'The type of a function call to {func_name} is the same as the type that the function returns'
|
||||
else:
|
||||
comment = f'The type of the value passed to argument {arg_no} of function {func_name} should match the type of that argument'
|
||||
|
||||
if isinstance(sig_part, functions.TypeVariable):
|
||||
yield SameTypeConstraint(type_var_map[sig_part], arg_expr.type3, comment=comment)
|
||||
if isinstance(sig_part, TypeVariable):
|
||||
yield SameTypeConstraint(type_var_map[sig_part], arg_placeholders[arg_expr], comment=comment)
|
||||
continue
|
||||
|
||||
if isinstance(sig_part, type3types.Type3):
|
||||
yield SameTypeConstraint(sig_part, arg_expr.type3, comment=comment)
|
||||
if isinstance(sig_part, Type3):
|
||||
yield SameTypeConstraint(sig_part, arg_placeholders[arg_expr], comment=comment)
|
||||
continue
|
||||
|
||||
raise NotImplementedError(sig_part)
|
||||
return
|
||||
|
||||
def expression(ctx: Context, inp: ourlang.Expression, phft: PlaceholderForType) -> ConstraintGenerator:
|
||||
if isinstance(inp, ourlang.Constant):
|
||||
yield from constant(ctx, inp, phft)
|
||||
return
|
||||
|
||||
if isinstance(inp, ourlang.VariableReference):
|
||||
yield SameTypeConstraint(inp.variable.type3, phft,
|
||||
comment=f'typeOf("{inp.variable.name}") == typeOf({inp.variable.name})')
|
||||
return
|
||||
|
||||
if isinstance(inp, ourlang.BinaryOp):
|
||||
yield from expression_binary_op(ctx, inp, phft)
|
||||
return
|
||||
|
||||
if isinstance(inp, ourlang.FunctionCall):
|
||||
yield from expression_function_call(ctx, inp, phft)
|
||||
return
|
||||
|
||||
if isinstance(inp, ourlang.TupleInstantiation):
|
||||
r_type = []
|
||||
for arg in inp.elements:
|
||||
yield from expression(ctx, arg)
|
||||
r_type.append(arg.type3)
|
||||
arg_phft = PlaceholderForType([arg])
|
||||
yield from expression(ctx, arg, arg_phft)
|
||||
r_type.append(arg_phft)
|
||||
|
||||
yield TupleMatchConstraint(
|
||||
inp.type3,
|
||||
phft,
|
||||
r_type,
|
||||
comment='The type of a tuple is a combination of its members'
|
||||
)
|
||||
@ -116,34 +187,44 @@ def expression(ctx: Context, inp: ourlang.Expression) -> ConstraintGenerator:
|
||||
return
|
||||
|
||||
if isinstance(inp, ourlang.Subscript):
|
||||
yield from expression(ctx, inp.varref)
|
||||
yield from expression(ctx, inp.index)
|
||||
varref_phft = PlaceholderForType([inp.varref])
|
||||
index_phft = PlaceholderForType([inp.index])
|
||||
|
||||
yield CanBeSubscriptedConstraint(inp.type3, inp.varref.type3, inp.index)
|
||||
yield from expression(ctx, inp.varref, varref_phft)
|
||||
yield from expression(ctx, inp.index, index_phft)
|
||||
|
||||
if isinstance(inp.index, ourlang.ConstantPrimitive) and isinstance(inp.index.value, int):
|
||||
yield CanBeSubscriptedConstraint(phft, varref_phft, index_phft, inp.index.value)
|
||||
else:
|
||||
yield CanBeSubscriptedConstraint(phft, varref_phft, index_phft, None)
|
||||
return
|
||||
|
||||
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?
|
||||
assert isinstance(inp.struct_type3.application, TypeApplication_Struct) # FIXME: See test_struct.py::test_struct_not_accessible
|
||||
|
||||
yield from expression(ctx, inp.varref)
|
||||
yield SameTypeConstraint(st_args[inp.member], inp.type3,
|
||||
mem_typ = dict(inp.struct_type3.application.arguments)[inp.member]
|
||||
|
||||
yield from expression(ctx, inp.varref, PlaceholderForType([inp.varref])) # TODO
|
||||
yield SameTypeConstraint(mem_typ, phft,
|
||||
comment=f'The type of a struct member reference is the same as the type of struct member {inp.struct_type3.name}.{inp.member}')
|
||||
return
|
||||
|
||||
raise NotImplementedError(expression, inp)
|
||||
|
||||
def statement_return(ctx: Context, fun: ourlang.Function, inp: ourlang.StatementReturn) -> ConstraintGenerator:
|
||||
yield from expression(ctx, inp.value)
|
||||
phft = PlaceholderForType([inp.value])
|
||||
|
||||
yield SameTypeConstraint(fun.returns_type3, inp.value.type3,
|
||||
yield from expression(ctx, inp.value, phft)
|
||||
|
||||
yield SameTypeConstraint(fun.returns_type3, phft,
|
||||
comment=f'The type of the value returned from function {fun.name} should match its return type')
|
||||
|
||||
def statement_if(ctx: Context, fun: ourlang.Function, inp: ourlang.StatementIf) -> ConstraintGenerator:
|
||||
yield from expression(ctx, inp.test)
|
||||
test_phft = PlaceholderForType([inp.test])
|
||||
|
||||
yield SameTypeConstraint(inp.test.type3, prelude.bool_,
|
||||
yield from expression(ctx, inp.test, test_phft)
|
||||
|
||||
yield SameTypeConstraint(test_phft, prelude.bool_,
|
||||
comment='Must pass a boolean expression to if')
|
||||
|
||||
for stmt in inp.statements:
|
||||
@ -173,8 +254,10 @@ def function(ctx: Context, inp: ourlang.Function) -> ConstraintGenerator:
|
||||
yield from statement(ctx, inp, stmt)
|
||||
|
||||
def module_constant_def(ctx: Context, inp: ourlang.ModuleConstantDef) -> ConstraintGenerator:
|
||||
yield from constant(ctx, inp.constant)
|
||||
yield SameTypeConstraint(inp.type3, inp.constant.type3,
|
||||
phft = PlaceholderForType([inp.constant])
|
||||
|
||||
yield from constant(ctx, inp.constant, phft)
|
||||
yield SameTypeConstraint(inp.type3, phft,
|
||||
comment=f'The type of the value for module constant definition {inp.name} should match the type of that constant')
|
||||
|
||||
def module(ctx: Context, inp: ourlang.Module) -> ConstraintGenerator:
|
||||
|
||||
@ -115,8 +115,6 @@ def phasm_type3(inp: ourlang.Module, verbose: bool = False) -> None:
|
||||
# FIXME: This doesn't work with e.g. `:: [a] -> a`, as the placeholder is inside a type
|
||||
for plh, typ in placeholder_substitutes.items():
|
||||
for expr in plh.update_on_substitution:
|
||||
assert expr.type3 is plh
|
||||
|
||||
expr.type3 = typ
|
||||
|
||||
def print_constraint(placeholder_id_map: Dict[int, str], constraint: ConstraintBase) -> None:
|
||||
|
||||
@ -1,10 +1,36 @@
|
||||
from typing import TYPE_CHECKING, Any, Iterable, List, Union
|
||||
from typing import TYPE_CHECKING, Any, Hashable, Iterable, List, Union
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .typeclasses import Type3Class
|
||||
from .types import Type3
|
||||
|
||||
|
||||
class TypeVariableApplication_Base[T: Hashable, S: Hashable]:
|
||||
"""
|
||||
Records the constructor and arguments used to create this type.
|
||||
|
||||
Nullary types, or types of kind *, have both arguments set to None.
|
||||
"""
|
||||
constructor: T
|
||||
arguments: S
|
||||
|
||||
def __init__(self, constructor: T, arguments: S) -> None:
|
||||
self.constructor = constructor
|
||||
self.arguments = arguments
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash((self.constructor, self.arguments, ))
|
||||
|
||||
def __eq__(self, other: Any) -> bool:
|
||||
if not isinstance(other, TypeVariableApplication_Base):
|
||||
raise NotImplementedError
|
||||
|
||||
return (self.constructor == other.constructor # type: ignore[no-any-return]
|
||||
and self.arguments == other.arguments)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'{self.__class__.__name__}({self.constructor!r}, {self.arguments!r})'
|
||||
|
||||
class TypeVariable:
|
||||
"""
|
||||
Types variable are used in function definition.
|
||||
@ -14,47 +40,120 @@ class TypeVariable:
|
||||
during type checking. These type variables are used solely in the
|
||||
function's definition
|
||||
"""
|
||||
__slots__ = ('letter', )
|
||||
__slots__ = ('name', 'application', )
|
||||
|
||||
letter: str
|
||||
name: str
|
||||
application: TypeVariableApplication_Base[Any, Any]
|
||||
|
||||
def __init__(self, letter: str) -> None:
|
||||
assert len(letter) == 1, f'{letter} is not a valid type variable'
|
||||
self.letter = letter
|
||||
def __init__(self, name: str, application: TypeVariableApplication_Base[Any, Any]) -> None:
|
||||
self.name = name
|
||||
self.application = application
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.letter)
|
||||
return hash((self.name, self.application, ))
|
||||
|
||||
def __eq__(self, other: Any) -> bool:
|
||||
if not isinstance(other, TypeVariable):
|
||||
raise NotImplementedError
|
||||
|
||||
return self.letter == other.letter
|
||||
return (self.name == other.name
|
||||
and self.application == other.application)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'TypeVariable({repr(self.letter)})'
|
||||
return f'TypeVariable({repr(self.name)})'
|
||||
|
||||
class TypeVariableConstraintBase:
|
||||
class TypeVariableApplication_Nullary(TypeVariableApplication_Base[None, None]):
|
||||
"""
|
||||
For the type for this function argument it's not relevant if it was constructed.
|
||||
"""
|
||||
|
||||
class TypeConstructorVariable:
|
||||
"""
|
||||
Types constructor variable are used in function definition.
|
||||
|
||||
They are a lot like TypeVariable, except that they represent a
|
||||
type constructor rather than a type directly.
|
||||
|
||||
For now, we only have type constructor variables for kind
|
||||
* -> *.
|
||||
"""
|
||||
__slots__ = ('name', )
|
||||
|
||||
name: str
|
||||
|
||||
def __init__(self, name: str) -> None:
|
||||
self.name = name
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash((self.name, ))
|
||||
|
||||
def __eq__(self, other: Any) -> bool:
|
||||
if other is None:
|
||||
return False
|
||||
|
||||
if not isinstance(other, TypeConstructorVariable):
|
||||
raise NotImplementedError
|
||||
|
||||
return (self.name == other.name)
|
||||
|
||||
def __call__(self, tvar: TypeVariable) -> 'TypeVariable':
|
||||
return TypeVariable(
|
||||
self.name + ' ' + tvar.name,
|
||||
TypeVariableApplication_Unary(self, tvar)
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'TypeConstructorVariable({self.name!r})'
|
||||
|
||||
class TypeVariableApplication_Unary(TypeVariableApplication_Base[TypeConstructorVariable, TypeVariable]):
|
||||
"""
|
||||
The type for this function argument should be constructed from a type constructor.
|
||||
|
||||
And we need to know what construtor that was, since that's the one we support.
|
||||
"""
|
||||
|
||||
class ConstraintBase:
|
||||
__slots__ = ()
|
||||
|
||||
class TypeVariableConstraint_TypeHasTypeClass(TypeVariableConstraintBase):
|
||||
__slots__ = ('type_class3', )
|
||||
class Constraint_TypeClassInstanceExists(ConstraintBase):
|
||||
__slots__ = ('type_class3', 'types', )
|
||||
|
||||
def __init__(self, type_class3: 'Type3Class') -> None:
|
||||
type_class3: 'Type3Class'
|
||||
types: list[TypeVariable]
|
||||
|
||||
def __init__(self, type_class3: 'Type3Class', types: Iterable[TypeVariable]) -> None:
|
||||
self.type_class3 = type_class3
|
||||
self.types = list(types)
|
||||
|
||||
# Sanity check. AFAIK, if you have a multi-parameter type class,
|
||||
# you can only add a constraint by supplying types for all variables
|
||||
assert len(self.type_class3.args) == len(self.types)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.type_class3.name + ' ' + ' '.join(x.name for x in self.types)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'Constraint_TypeClassInstanceExists({self.type_class3.name}, {self.types!r})'
|
||||
|
||||
class TypeVariableContext:
|
||||
__slots__ = ('constraints', )
|
||||
|
||||
constraints: dict[TypeVariable, list[TypeVariableConstraintBase]]
|
||||
constraints: list[ConstraintBase]
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.constraints = {}
|
||||
def __init__(self, constraints: Iterable[ConstraintBase] = ()) -> None:
|
||||
self.constraints = list(constraints)
|
||||
|
||||
def __copy__(self) -> 'TypeVariableContext':
|
||||
result = TypeVariableContext()
|
||||
result.constraints.update(self.constraints)
|
||||
return result
|
||||
return TypeVariableContext(self.constraints)
|
||||
|
||||
def __str__(self) -> str:
|
||||
if not self.constraints:
|
||||
return ''
|
||||
|
||||
return '(' + ', '.join(str(x) for x in self.constraints) + ') => '
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'TypeVariableContext({self.constraints!r})'
|
||||
|
||||
class FunctionSignature:
|
||||
__slots__ = ('context', 'args', )
|
||||
@ -65,3 +164,9 @@ class FunctionSignature:
|
||||
def __init__(self, context: TypeVariableContext, args: Iterable[Union['Type3', TypeVariable]]) -> None:
|
||||
self.context = context.__copy__()
|
||||
self.args = list(args)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return str(self.context) + ' -> '.join(x.name for x in self.args)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'FunctionSignature({self.context!r}, {self.args!r})'
|
||||
|
||||
@ -7,14 +7,13 @@ 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'
|
||||
type3: Type3 | None
|
||||
"""
|
||||
The type to update
|
||||
"""
|
||||
|
||||
119
phasm/type3/routers.py
Normal file
119
phasm/type3/routers.py
Normal file
@ -0,0 +1,119 @@
|
||||
from typing import Any, Callable
|
||||
|
||||
from .functions import (
|
||||
TypeConstructorVariable,
|
||||
TypeVariable,
|
||||
TypeVariableApplication_Unary,
|
||||
)
|
||||
from .typeclasses import Type3ClassArgs
|
||||
from .types import KindArgument, Type3, TypeApplication_TypeInt, TypeConstructor_Base
|
||||
|
||||
|
||||
class NoRouteForTypeException(Exception):
|
||||
pass
|
||||
|
||||
class TypeApplicationRouter[S, R]:
|
||||
"""
|
||||
Helper class to find a method based on a constructed type
|
||||
"""
|
||||
__slots__ = ('by_constructor', 'by_type', )
|
||||
|
||||
by_constructor: dict[Any, Callable[[S, Any], R]]
|
||||
"""
|
||||
Contains all the added routing functions for constructed types
|
||||
"""
|
||||
|
||||
by_type: dict[Type3, Callable[[S], R]]
|
||||
"""
|
||||
Contains all the added routing functions for constructed types
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.by_constructor = {}
|
||||
self.by_type = {}
|
||||
|
||||
def add_n(self, typ: Type3, helper: Callable[[S], R]) -> None:
|
||||
"""
|
||||
Lets you route to types that were not constructed
|
||||
|
||||
Also known types of kind *
|
||||
"""
|
||||
self.by_type[typ] = helper
|
||||
|
||||
def add[T](self, constructor: TypeConstructor_Base[T], helper: Callable[[S, T], R]) -> None:
|
||||
self.by_constructor[constructor] = helper
|
||||
|
||||
def __call__(self, arg0: S, typ: Type3) -> R:
|
||||
t_helper = self.by_type.get(typ)
|
||||
if t_helper is not None:
|
||||
return t_helper(arg0)
|
||||
|
||||
c_helper = self.by_constructor.get(typ.application.constructor)
|
||||
if c_helper is not None:
|
||||
return c_helper(arg0, typ.application.arguments)
|
||||
|
||||
raise NoRouteForTypeException(arg0, typ)
|
||||
|
||||
TypeVariableLookup = dict[TypeVariable, tuple[KindArgument, ...]]
|
||||
|
||||
class TypeClassArgsRouter[S, R]:
|
||||
"""
|
||||
Helper class to find a method based on a type class argument list
|
||||
"""
|
||||
__slots__ = ('args', 'data', )
|
||||
|
||||
args: Type3ClassArgs
|
||||
|
||||
data: dict[tuple[Type3 | TypeConstructor_Base[Any], ...], Callable[[S, TypeVariableLookup], R]]
|
||||
|
||||
def __init__(self, args: Type3ClassArgs) -> None:
|
||||
self.args = args
|
||||
self.data = {}
|
||||
|
||||
def add(
|
||||
self,
|
||||
tv_map: dict[TypeVariable, Type3],
|
||||
tc_map: dict[TypeConstructorVariable, TypeConstructor_Base[Any]],
|
||||
helper: Callable[[S, TypeVariableLookup], R],
|
||||
) -> None:
|
||||
|
||||
key: list[Type3 | TypeConstructor_Base[Any]] = []
|
||||
|
||||
for tc_arg in self.args:
|
||||
if isinstance(tc_arg, TypeVariable):
|
||||
key.append(tv_map[tc_arg])
|
||||
else:
|
||||
key.append(tc_map[tc_arg])
|
||||
|
||||
self.data[tuple(key)] = helper
|
||||
|
||||
def __call__(self, arg0: S, tv_map: dict[TypeVariable, Type3]) -> R:
|
||||
key: list[Type3 | TypeConstructor_Base[Any]] = []
|
||||
arguments: TypeVariableLookup = {}
|
||||
|
||||
for tc_arg in self.args:
|
||||
if isinstance(tc_arg, TypeVariable):
|
||||
key.append(tv_map[tc_arg])
|
||||
continue
|
||||
|
||||
for tvar, typ in tv_map.items():
|
||||
tvar_constructor = tvar.application.constructor
|
||||
if tvar_constructor != tc_arg:
|
||||
continue
|
||||
|
||||
key.append(typ.application.constructor)
|
||||
|
||||
if isinstance(tvar.application, TypeVariableApplication_Unary):
|
||||
# FIXME: This feels sketchy. Shouldn't the type variable
|
||||
# have the exact same number as arguments?
|
||||
if isinstance(typ.application, TypeApplication_TypeInt):
|
||||
arguments[tvar.application.arguments] = typ.application.arguments
|
||||
continue
|
||||
|
||||
raise NotImplementedError(tvar.application, typ.application)
|
||||
|
||||
t_helper = self.data.get(tuple(key))
|
||||
if t_helper is not None:
|
||||
return t_helper(arg0, arguments)
|
||||
|
||||
raise NoRouteForTypeException(arg0, tv_map)
|
||||
@ -1,12 +1,14 @@
|
||||
from typing import Any, Dict, Iterable, List, Mapping, Optional, Union
|
||||
from typing import Dict, Iterable, List, Mapping, Optional, Union
|
||||
|
||||
from .functions import (
|
||||
Constraint_TypeClassInstanceExists,
|
||||
ConstraintBase,
|
||||
FunctionSignature,
|
||||
TypeConstructorVariable,
|
||||
TypeVariable,
|
||||
TypeVariableConstraint_TypeHasTypeClass,
|
||||
TypeVariableContext,
|
||||
)
|
||||
from .types import Type3, TypeConstructor, TypeConstructor_Struct
|
||||
from .types import Type3
|
||||
|
||||
|
||||
class Type3ClassMethod:
|
||||
@ -19,14 +21,19 @@ class Type3ClassMethod:
|
||||
self.name = name
|
||||
self.signature = signature
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f'{self.name} :: {self.signature}'
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'Type3ClassMethod({repr(self.name)}, {repr(self.signature)})'
|
||||
|
||||
Type3ClassArgs = tuple[TypeVariable] | tuple[TypeVariable, TypeVariable] | tuple[TypeConstructorVariable]
|
||||
|
||||
class Type3Class:
|
||||
__slots__ = ('name', 'args', 'methods', 'operators', 'inherited_classes', )
|
||||
|
||||
name: str
|
||||
args: List[TypeVariable]
|
||||
args: Type3ClassArgs
|
||||
methods: Dict[str, Type3ClassMethod]
|
||||
operators: Dict[str, Type3ClassMethod]
|
||||
inherited_classes: List['Type3Class']
|
||||
@ -34,42 +41,60 @@ class Type3Class:
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
args: Iterable[TypeVariable],
|
||||
args: Type3ClassArgs,
|
||||
methods: Mapping[str, Iterable[Union[Type3, TypeVariable]]],
|
||||
operators: Mapping[str, Iterable[Union[Type3, TypeVariable]]],
|
||||
inherited_classes: Optional[List['Type3Class']] = None,
|
||||
additional_context: Optional[Mapping[str, Iterable[ConstraintBase]]] = None,
|
||||
) -> None:
|
||||
self.name = name
|
||||
self.args = list(args)
|
||||
|
||||
context = TypeVariableContext()
|
||||
for arg in args:
|
||||
context.constraints[arg] = [
|
||||
TypeVariableConstraint_TypeHasTypeClass(self)
|
||||
]
|
||||
|
||||
# FIXME: Multi parameter class types
|
||||
# To fix this, realise that an instantiation of a multi paramater type class
|
||||
# means that the instantiation depends on the combination of type classes
|
||||
# and so we can't store the type classes on the types anymore
|
||||
# This also means constraints should store a tuple of types as its key
|
||||
assert len(context.constraints) <= 1
|
||||
self.args = args
|
||||
|
||||
self.methods = {
|
||||
k: Type3ClassMethod(k, FunctionSignature(context, v))
|
||||
k: Type3ClassMethod(k, _create_signature(v, self))
|
||||
for k, v in methods.items()
|
||||
}
|
||||
self.operators = {
|
||||
k: Type3ClassMethod(k, FunctionSignature(context, v))
|
||||
k: Type3ClassMethod(k, _create_signature(v, self))
|
||||
for k, v in operators.items()
|
||||
}
|
||||
self.inherited_classes = inherited_classes or []
|
||||
|
||||
if additional_context:
|
||||
for func_name, constraint_list in additional_context.items():
|
||||
func = self.methods.get(func_name) or self.operators.get(func_name)
|
||||
assert func is not None # type hint
|
||||
|
||||
func.signature.context.constraints.extend(constraint_list)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return self.name
|
||||
|
||||
def instance_type_class(cls: Type3Class, typ: Type3 | TypeConstructor[Any] | TypeConstructor_Struct) -> None:
|
||||
if isinstance(typ, Type3):
|
||||
typ.classes.add(cls)
|
||||
else:
|
||||
typ.type_classes.add(cls)
|
||||
def _create_signature(
|
||||
method_arg_list: Iterable[Type3 | TypeVariable],
|
||||
type_class3: Type3Class,
|
||||
) -> FunctionSignature:
|
||||
context = TypeVariableContext()
|
||||
if not isinstance(type_class3.args[0], TypeConstructorVariable):
|
||||
context.constraints.append(Constraint_TypeClassInstanceExists(type_class3, type_class3.args))
|
||||
|
||||
signature_args: list[Type3 | TypeVariable] = []
|
||||
for method_arg in method_arg_list:
|
||||
if isinstance(method_arg, Type3):
|
||||
signature_args.append(method_arg)
|
||||
continue
|
||||
|
||||
if isinstance(method_arg, TypeVariable):
|
||||
type_constructor = method_arg.application.constructor
|
||||
if type_constructor is None:
|
||||
signature_args.append(method_arg)
|
||||
continue
|
||||
|
||||
if (type_constructor, ) == type_class3.args:
|
||||
context.constraints.append(Constraint_TypeClassInstanceExists(type_class3, [method_arg]))
|
||||
signature_args.append(method_arg)
|
||||
continue
|
||||
|
||||
raise NotImplementedError(method_arg)
|
||||
|
||||
return FunctionSignature(context, signature_args)
|
||||
|
||||
@ -2,22 +2,46 @@
|
||||
Contains the final types for use in Phasm, as well as construtors.
|
||||
"""
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
Generic,
|
||||
Iterable,
|
||||
Set,
|
||||
Callable,
|
||||
Hashable,
|
||||
Self,
|
||||
Tuple,
|
||||
TypeVar,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .typeclasses import Type3Class
|
||||
|
||||
S = TypeVar('S')
|
||||
T = TypeVar('T')
|
||||
|
||||
class KindArgument:
|
||||
pass
|
||||
|
||||
class TypeApplication_Base[T: Hashable, S: Hashable]:
|
||||
"""
|
||||
Records the constructor and arguments used to create this type.
|
||||
|
||||
Nullary types, or types of kind *, have both arguments set to None.
|
||||
"""
|
||||
constructor: T
|
||||
arguments: S
|
||||
|
||||
def __init__(self, constructor: T, arguments: S) -> None:
|
||||
self.constructor = constructor
|
||||
self.arguments = arguments
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash((self.constructor, self.arguments, ))
|
||||
|
||||
def __eq__(self, other: Any) -> bool:
|
||||
if not isinstance(other, TypeApplication_Base):
|
||||
raise NotImplementedError
|
||||
|
||||
return (self.constructor == other.constructor # type: ignore[no-any-return]
|
||||
and self.arguments == other.arguments)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'{self.__class__.__name__}({self.constructor!r}, {self.arguments!r})'
|
||||
|
||||
class Type3(KindArgument):
|
||||
"""
|
||||
Base class for the type3 types
|
||||
@ -25,32 +49,25 @@ class Type3(KindArgument):
|
||||
(Having a separate name makes it easier to distinguish from
|
||||
Python's Type)
|
||||
"""
|
||||
__slots__ = ('name', 'classes', )
|
||||
__slots__ = ('name', 'application', )
|
||||
|
||||
name: str
|
||||
"""
|
||||
The name of the string, as parsed and outputted by codestyle.
|
||||
"""
|
||||
|
||||
classes: Set['Type3Class']
|
||||
application: TypeApplication_Base[Any, Any]
|
||||
"""
|
||||
The type classes that this type implements
|
||||
How the type was constructed; i.e. which constructor was used and which
|
||||
type level arguments were applied to the constructor.
|
||||
"""
|
||||
|
||||
def __init__(self, name: str, classes: Iterable['Type3Class']) -> None:
|
||||
def __init__(self, name: str, application: TypeApplication_Base[Any, Any]) -> None:
|
||||
self.name = name
|
||||
self.classes = set(classes)
|
||||
|
||||
for cls in self.classes:
|
||||
for inh_cls in cls.inherited_classes:
|
||||
if inh_cls not in self.classes:
|
||||
raise Exception(
|
||||
f'No instance for ({inh_cls} {self.name})'
|
||||
f'; required for ({cls} {self.name})'
|
||||
)
|
||||
self.application = application
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'Type3({repr(self.name)}, {repr(self.classes)})'
|
||||
return f'Type3({self.name!r}, {self.application!r})'
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.name
|
||||
@ -76,6 +93,11 @@ class Type3(KindArgument):
|
||||
def __bool__(self) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
class TypeApplication_Nullary(TypeApplication_Base[None, None]):
|
||||
"""
|
||||
There was no constructor used to create this type - it's a 'simple' type like u32
|
||||
"""
|
||||
|
||||
class IntType3(KindArgument):
|
||||
"""
|
||||
Sometimes you can have an int on the type level, e.g. when using static arrays
|
||||
@ -94,6 +116,9 @@ class IntType3(KindArgument):
|
||||
def __init__(self, value: int) -> None:
|
||||
self.value = value
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'IntType3({self.value!r})'
|
||||
|
||||
def __format__(self, format_spec: str) -> str:
|
||||
if format_spec != 's':
|
||||
raise TypeError(f'unsupported format string passed to Type3.__format__: {format_spec}')
|
||||
@ -112,27 +137,20 @@ class IntType3(KindArgument):
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.value)
|
||||
|
||||
T = TypeVar('T')
|
||||
|
||||
class TypeConstructor(Generic[T]):
|
||||
class TypeConstructor_Base[T]:
|
||||
"""
|
||||
Base class for type construtors
|
||||
"""
|
||||
__slots__ = ('name', 'classes', 'type_classes', '_cache', '_reverse_cache')
|
||||
__slots__ = ('name', 'on_create', '_cache', )
|
||||
|
||||
name: str
|
||||
"""
|
||||
The name of the type constructor
|
||||
"""
|
||||
|
||||
classes: Set['Type3Class']
|
||||
on_create: Callable[[T, Type3], None]
|
||||
"""
|
||||
The type classes that this constructor implements
|
||||
"""
|
||||
|
||||
type_classes: Set['Type3Class']
|
||||
"""
|
||||
The type classes that the constructed types implement
|
||||
Who to let know if a type is created
|
||||
"""
|
||||
|
||||
_cache: dict[T, Type3]
|
||||
@ -141,32 +159,26 @@ class TypeConstructor(Generic[T]):
|
||||
it should produce the exact same result.
|
||||
"""
|
||||
|
||||
_reverse_cache: dict[Type3, T]
|
||||
"""
|
||||
Sometimes we need to know the key that created a type.
|
||||
"""
|
||||
|
||||
def __init__(self, name: str, classes: Iterable['Type3Class'], type_classes: Iterable['Type3Class']) -> None:
|
||||
def __init__(self, name: str, on_create: Callable[[T, Type3], None]) -> None:
|
||||
self.name = name
|
||||
self.classes = set(classes)
|
||||
self.type_classes = set(type_classes)
|
||||
self.on_create = on_create
|
||||
|
||||
self._cache = {}
|
||||
self._reverse_cache = {}
|
||||
|
||||
def make_name(self, key: T) -> str:
|
||||
"""
|
||||
Renders the type's name based on the given arguments
|
||||
"""
|
||||
raise NotImplementedError
|
||||
raise NotImplementedError('make_name', self)
|
||||
|
||||
def did_construct(self, typ: Type3) -> T | None:
|
||||
def make_application(self, key: T) -> TypeApplication_Base[Self, T]:
|
||||
"""
|
||||
Was the given type constructed by this constructor?
|
||||
Records how the type was constructed into type.
|
||||
|
||||
If so, which arguments where used?
|
||||
The type checker and compiler will need to know what
|
||||
arguments where made to construct the type.
|
||||
"""
|
||||
return self._reverse_cache.get(typ)
|
||||
raise NotImplementedError('make_application', self)
|
||||
|
||||
def construct(self, key: T) -> Type3:
|
||||
"""
|
||||
@ -175,21 +187,15 @@ class TypeConstructor(Generic[T]):
|
||||
"""
|
||||
result = self._cache.get(key, None)
|
||||
if result is None:
|
||||
self._cache[key] = result = Type3(self.make_name(key), self.type_classes)
|
||||
self._reverse_cache[result] = key
|
||||
self._cache[key] = result = Type3(self.make_name(key), self.make_application(key))
|
||||
self.on_create(key, result)
|
||||
|
||||
return result
|
||||
|
||||
class TypeConstructor_Type(TypeConstructor[Type3]):
|
||||
"""
|
||||
Base class type constructors of kind: * -> *
|
||||
"""
|
||||
__slots__ = ()
|
||||
def __repr__(self) -> str:
|
||||
return f'{self.__class__.__name__}({self.name!r}, ...)'
|
||||
|
||||
def __call__(self, arg: Type3) -> Type3:
|
||||
raise NotImplementedError
|
||||
|
||||
class TypeConstructor_TypeInt(TypeConstructor[Tuple[Type3, IntType3]]):
|
||||
class TypeConstructor_TypeInt(TypeConstructor_Base[Tuple[Type3, IntType3]]):
|
||||
"""
|
||||
Base class type constructors of kind: * -> Int -> *
|
||||
|
||||
@ -197,22 +203,34 @@ class TypeConstructor_TypeInt(TypeConstructor[Tuple[Type3, IntType3]]):
|
||||
"""
|
||||
__slots__ = ()
|
||||
|
||||
def make_application(self, key: Tuple[Type3, IntType3]) -> 'TypeApplication_TypeInt':
|
||||
return TypeApplication_TypeInt(self, key)
|
||||
|
||||
def make_name(self, key: Tuple[Type3, IntType3]) -> str:
|
||||
return f'{self.name} {key[0].name} {key[1].value}'
|
||||
|
||||
def __call__(self, arg0: Type3, arg1: IntType3) -> Type3:
|
||||
return self.construct((arg0, arg1))
|
||||
|
||||
class TypeConstructor_TypeStar(TypeConstructor[Tuple[Type3, ...]]):
|
||||
class TypeApplication_TypeInt(TypeApplication_Base[TypeConstructor_TypeInt, Tuple[Type3, IntType3]]):
|
||||
pass
|
||||
|
||||
class TypeConstructor_TypeStar(TypeConstructor_Base[Tuple[Type3, ...]]):
|
||||
"""
|
||||
Base class type constructors of variadic kind
|
||||
|
||||
Notably, tuple.
|
||||
"""
|
||||
def make_application(self, key: Tuple[Type3, ...]) -> 'TypeApplication_TypeStar':
|
||||
return TypeApplication_TypeStar(self, key)
|
||||
|
||||
def __call__(self, *args: Type3) -> Type3:
|
||||
key: Tuple[Type3, ...] = tuple(args)
|
||||
return self.construct(key)
|
||||
|
||||
class TypeApplication_TypeStar(TypeApplication_Base[TypeConstructor_TypeStar, Tuple[Type3, ...]]):
|
||||
pass
|
||||
|
||||
class TypeConstructor_StaticArray(TypeConstructor_TypeInt):
|
||||
def make_name(self, key: Tuple[Type3, IntType3]) -> str:
|
||||
return f'{key[0].name}[{key[1].value}]'
|
||||
@ -221,57 +239,30 @@ class TypeConstructor_Tuple(TypeConstructor_TypeStar):
|
||||
def make_name(self, key: Tuple[Type3, ...]) -> str:
|
||||
return '(' + ', '.join(x.name for x in key) + ', )'
|
||||
|
||||
class TypeConstructor_Struct:
|
||||
class TypeConstructor_Struct(TypeConstructor_Base[tuple[tuple[str, Type3], ...]]):
|
||||
"""
|
||||
Base class for type construtors
|
||||
Constructs struct types
|
||||
"""
|
||||
__slots__ = ('name', 'classes', 'type_classes', '_cache', '_reverse_cache')
|
||||
def make_application(self, key: tuple[tuple[str, Type3], ...]) -> 'TypeApplication_Struct':
|
||||
return TypeApplication_Struct(self, key)
|
||||
|
||||
name: str
|
||||
"""
|
||||
The name of the type constructor
|
||||
"""
|
||||
def make_name(self, key: tuple[tuple[str, Type3], ...]) -> str:
|
||||
return f'{self.name}(' + ', '.join(
|
||||
f'{n}: {t.name}'
|
||||
for n, t in key
|
||||
) + ')'
|
||||
|
||||
classes: Set['Type3Class']
|
||||
def construct(self, key: T) -> Type3:
|
||||
"""
|
||||
The type classes that this constructor implements
|
||||
Constructs the type by applying the given arguments to this
|
||||
constructor.
|
||||
"""
|
||||
raise Exception('This does not work with the caching system')
|
||||
|
||||
type_classes: Set['Type3Class']
|
||||
"""
|
||||
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.classes = set(classes)
|
||||
self.type_classes = set(type_classes)
|
||||
|
||||
self._cache = {}
|
||||
self._reverse_cache = {}
|
||||
|
||||
def did_construct(self, typ: Type3) -> dict[str, Type3] | None:
|
||||
"""
|
||||
Was the given type constructed by this constructor?
|
||||
|
||||
If so, which arguments where used?
|
||||
"""
|
||||
return self._reverse_cache.get(typ)
|
||||
|
||||
def __call__(self, name: str, args: dict[str, Type3], classes: Set['Type3Class']) -> Type3:
|
||||
result = Type3(name, classes | self.type_classes)
|
||||
self._reverse_cache[result] = args
|
||||
|
||||
def __call__(self, name: str, args: tuple[tuple[str, Type3], ...]) -> Type3:
|
||||
result = Type3(name, self.make_application(args))
|
||||
self.on_create(args, result)
|
||||
return result
|
||||
|
||||
class TypeApplication_Struct(TypeApplication_Base[TypeConstructor_Struct, tuple[tuple[str, Type3], ...]]):
|
||||
pass
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
Helper functions to generate WASM code by writing Python functions
|
||||
"""
|
||||
import functools
|
||||
from typing import Any, Callable, Dict, List, Optional, Type
|
||||
from typing import Any, Callable, Dict, Iterable, List, Optional, Type
|
||||
|
||||
from . import wasm
|
||||
|
||||
@ -24,6 +24,12 @@ class VarType_i32(VarType_Base):
|
||||
class VarType_i64(VarType_Base):
|
||||
wasm_type = wasm.WasmTypeInt64
|
||||
|
||||
class VarType_f32(VarType_Base):
|
||||
wasm_type = wasm.WasmTypeFloat32
|
||||
|
||||
class VarType_f64(VarType_Base):
|
||||
wasm_type = wasm.WasmTypeFloat64
|
||||
|
||||
class Generator_i32i64:
|
||||
def __init__(self, prefix: str, generator: 'Generator') -> None:
|
||||
self.prefix = prefix
|
||||
@ -74,6 +80,9 @@ class Generator_i32(Generator_i32i64):
|
||||
def __init__(self, generator: 'Generator') -> None:
|
||||
super().__init__('i32', generator)
|
||||
|
||||
# 2.4.1. Numeric Instructions
|
||||
self.wrap_i64 = functools.partial(self.generator.add_statement, 'i32.wrap_i64')
|
||||
|
||||
class Generator_i64(Generator_i32i64):
|
||||
def __init__(self, generator: 'Generator') -> None:
|
||||
super().__init__('i64', generator)
|
||||
@ -132,10 +141,16 @@ class Generator_f32(Generator_f32f64):
|
||||
def __init__(self, generator: 'Generator') -> None:
|
||||
super().__init__('f32', generator)
|
||||
|
||||
# 2.4.1 Numeric Instructions
|
||||
self.demote_f64 = functools.partial(self.generator.add_statement, 'f32.demote_f64')
|
||||
|
||||
class Generator_f64(Generator_f32f64):
|
||||
def __init__(self, generator: 'Generator') -> None:
|
||||
super().__init__('f64', generator)
|
||||
|
||||
# 2.4.1 Numeric Instructions
|
||||
self.promote_f32 = functools.partial(self.generator.add_statement, 'f64.promote_f32')
|
||||
|
||||
class Generator_Local:
|
||||
def __init__(self, generator: 'Generator') -> None:
|
||||
self.generator = generator
|
||||
@ -155,12 +170,23 @@ class Generator_Local:
|
||||
self.generator.add_statement('local.tee', variable.name_ref, comment=comment)
|
||||
|
||||
class GeneratorBlock:
|
||||
def __init__(self, generator: 'Generator', name: str) -> None:
|
||||
def __init__(self, generator: 'Generator', name: str, params: Iterable[str] = (), result: str | None = None) -> None:
|
||||
self.generator = generator
|
||||
self.name = name
|
||||
self.params = params
|
||||
self.result = result
|
||||
|
||||
def __enter__(self) -> None:
|
||||
self.generator.add_statement(self.name)
|
||||
stmt = self.name
|
||||
if self.params:
|
||||
stmt = f'{stmt} ' + ' '.join(
|
||||
f'(param {typ})'
|
||||
for typ in self.params
|
||||
)
|
||||
if self.result:
|
||||
stmt = f'{stmt} (result {self.result})'
|
||||
|
||||
self.generator.add_statement(stmt)
|
||||
|
||||
def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
|
||||
if not exc_type:
|
||||
@ -201,19 +227,18 @@ class Generator:
|
||||
def add_statement(self, name: str, *args: str, comment: Optional[str] = None) -> None:
|
||||
self.statements.append(wasm.Statement(name, *args, comment=comment))
|
||||
|
||||
def temp_var_i32(self, infix: str) -> VarType_i32:
|
||||
def temp_var[T: VarType_Base](self, var: T) -> T:
|
||||
idx = 0
|
||||
while (varname := f'__{infix}_tmp_var_{idx}__') in self.locals:
|
||||
while (varname := f'__{var.name}_tmp_var_{idx}__') in self.locals:
|
||||
idx += 1
|
||||
|
||||
return VarType_i32(varname)
|
||||
return var.__class__(varname)
|
||||
|
||||
def temp_var_i32(self, infix: str) -> VarType_i32:
|
||||
return self.temp_var(VarType_i32(infix))
|
||||
|
||||
def temp_var_u8(self, infix: str) -> VarType_u8:
|
||||
idx = 0
|
||||
while (varname := f'__{infix}_tmp_var_{idx}__') in self.locals:
|
||||
idx += 1
|
||||
|
||||
return VarType_u8(varname)
|
||||
return self.temp_var(VarType_u8(infix))
|
||||
|
||||
def func_wrapper(exported: bool = True) -> Callable[[Any], wasm.Function]:
|
||||
"""
|
||||
|
||||
@ -4,14 +4,22 @@ from typing import Any, Generator, Iterable, List, TextIO, Union
|
||||
|
||||
from phasm import compiler, prelude
|
||||
from phasm.codestyle import phasm_render
|
||||
from phasm.runtime import calculate_alloc_size
|
||||
from phasm.type3 import placeholders as type3placeholders
|
||||
from phasm.runtime import (
|
||||
calculate_alloc_size,
|
||||
calculate_alloc_size_static_array,
|
||||
calculate_alloc_size_struct,
|
||||
calculate_alloc_size_tuple,
|
||||
)
|
||||
from phasm.type3 import types as type3types
|
||||
from phasm.type3.routers import NoRouteForTypeException, TypeApplicationRouter
|
||||
|
||||
from . import runners
|
||||
|
||||
DASHES = '-' * 16
|
||||
|
||||
class InvalidArgumentException(Exception):
|
||||
pass
|
||||
|
||||
class SuiteResult:
|
||||
def __init__(self) -> None:
|
||||
self.returned_value = None
|
||||
@ -65,9 +73,6 @@ class Suite:
|
||||
runner.interpreter_dump_memory(sys.stderr)
|
||||
|
||||
for arg, arg_typ in zip(args, func_args, strict=True):
|
||||
assert not isinstance(arg_typ, type3placeholders.PlaceholderForType), \
|
||||
'Cannot call polymorphic function from outside'
|
||||
|
||||
if arg_typ in (prelude.u8, prelude.u32, prelude.u64, ):
|
||||
assert isinstance(arg, int)
|
||||
wasm_args.append(arg)
|
||||
@ -83,30 +88,10 @@ class Suite:
|
||||
wasm_args.append(arg)
|
||||
continue
|
||||
|
||||
if arg_typ is prelude.bytes_:
|
||||
adr = _allocate_memory_stored_value(runner, arg_typ, arg)
|
||||
try:
|
||||
adr = ALLOCATE_MEMORY_STORED_ROUTER((runner, arg), arg_typ)
|
||||
wasm_args.append(adr)
|
||||
continue
|
||||
|
||||
assert isinstance(arg_typ, type3types.Type3)
|
||||
sa_args = prelude.static_array.did_construct(arg_typ)
|
||||
if sa_args is not None:
|
||||
adr = _allocate_memory_stored_value(runner, arg_typ, arg)
|
||||
wasm_args.append(adr)
|
||||
continue
|
||||
|
||||
tp_args = prelude.tuple_.did_construct(arg_typ)
|
||||
if tp_args is not None:
|
||||
adr = _allocate_memory_stored_value(runner, arg_typ, arg)
|
||||
wasm_args.append(adr)
|
||||
continue
|
||||
|
||||
st_args = prelude.struct.did_construct(arg_typ)
|
||||
if st_args is not None:
|
||||
adr = _allocate_memory_stored_value(runner, arg_typ, arg)
|
||||
wasm_args.append(adr)
|
||||
continue
|
||||
|
||||
except NoRouteForTypeException:
|
||||
raise NotImplementedError(arg_typ, arg)
|
||||
|
||||
write_header(sys.stderr, 'Memory (pre run)')
|
||||
@ -146,39 +131,18 @@ def _write_memory_stored_value(
|
||||
val_typ: type3types.Type3,
|
||||
val: Any,
|
||||
) -> int:
|
||||
if val_typ is prelude.bytes_:
|
||||
adr2 = _allocate_memory_stored_value(runner, val_typ, val)
|
||||
try:
|
||||
adr2 = ALLOCATE_MEMORY_STORED_ROUTER((runner, val), val_typ)
|
||||
runner.interpreter_write_memory(adr, compiler.module_data_u32(adr2))
|
||||
return 4
|
||||
|
||||
st_args = prelude.struct.did_construct(val_typ)
|
||||
if st_args is not None:
|
||||
adr2 = _allocate_memory_stored_value(runner, val_typ, val)
|
||||
runner.interpreter_write_memory(adr, compiler.module_data_u32(adr2))
|
||||
return 4
|
||||
|
||||
sa_args = prelude.static_array.did_construct(val_typ)
|
||||
if sa_args is not None:
|
||||
adr2 = _allocate_memory_stored_value(runner, val_typ, val)
|
||||
runner.interpreter_write_memory(adr, compiler.module_data_u32(adr2))
|
||||
return 4
|
||||
|
||||
tp_args = prelude.tuple_.did_construct(val_typ)
|
||||
if tp_args is not None:
|
||||
adr2 = _allocate_memory_stored_value(runner, val_typ, val)
|
||||
runner.interpreter_write_memory(adr, compiler.module_data_u32(adr2))
|
||||
return 4
|
||||
|
||||
except NoRouteForTypeException:
|
||||
to_write = WRITE_LOOKUP_MAP[val_typ.name](val)
|
||||
runner.interpreter_write_memory(adr, to_write)
|
||||
return len(to_write)
|
||||
|
||||
def _allocate_memory_stored_value(
|
||||
runner: runners.RunnerBase,
|
||||
val_typ: type3types.Type3,
|
||||
val: Any
|
||||
) -> int:
|
||||
if val_typ is prelude.bytes_:
|
||||
def _allocate_memory_stored_bytes(attrs: tuple[runners.RunnerBase, bytes]) -> int:
|
||||
runner, val = attrs
|
||||
|
||||
assert isinstance(val, bytes)
|
||||
|
||||
adr = runner.call('stdlib.types.__alloc_bytes__', len(val))
|
||||
@ -188,32 +152,52 @@ def _allocate_memory_stored_value(
|
||||
runner.interpreter_write_memory(adr + 4, val)
|
||||
return adr
|
||||
|
||||
sa_args = prelude.static_array.did_construct(val_typ)
|
||||
if sa_args is not None:
|
||||
assert isinstance(val, tuple)
|
||||
def _allocate_memory_stored_static_array(attrs: tuple[runners.RunnerBase, Any], sa_args: tuple[type3types.Type3, type3types.IntType3]) -> int:
|
||||
runner, val = attrs
|
||||
|
||||
sa_type, sa_len = sa_args
|
||||
|
||||
alloc_size = calculate_alloc_size(val_typ)
|
||||
adr = runner.call('stdlib.alloc.__alloc__', alloc_size)
|
||||
assert isinstance(adr, int)
|
||||
sys.stderr.write(f'Allocation 0x{adr:08x} {repr(val)}\n')
|
||||
if not isinstance(val, tuple):
|
||||
raise InvalidArgumentException(f'Expected tuple of length {sa_len.value}; got {val!r} instead')
|
||||
if sa_len.value != len(val):
|
||||
raise InvalidArgumentException(f'Expected tuple of length {sa_len.value}; got {val!r} instead')
|
||||
|
||||
tuple_len = sa_len.value
|
||||
assert tuple_len == len(val)
|
||||
alloc_size = calculate_alloc_size_static_array(False, sa_args)
|
||||
adr = runner.call('stdlib.alloc.__alloc__', alloc_size)
|
||||
assert isinstance(adr, int) # Type int
|
||||
sys.stderr.write(f'Allocation 0x{adr:08x} {repr(val)}\n')
|
||||
|
||||
offset = adr
|
||||
for val_el_val in val:
|
||||
offset += _write_memory_stored_value(runner, offset, sa_type, val_el_val)
|
||||
return adr
|
||||
|
||||
val_el_typ: type3types.Type3
|
||||
def _allocate_memory_stored_struct(attrs: tuple[runners.RunnerBase, Any], st_args: tuple[tuple[str, type3types.Type3], ...]) -> int:
|
||||
runner, val = attrs
|
||||
|
||||
assert isinstance(val, dict)
|
||||
|
||||
alloc_size = calculate_alloc_size_struct(False, st_args)
|
||||
adr = runner.call('stdlib.alloc.__alloc__', alloc_size)
|
||||
assert isinstance(adr, int)
|
||||
sys.stderr.write(f'Allocation 0x{adr:08x} {repr(val)}\n')
|
||||
|
||||
offset = adr
|
||||
for val_el_name, val_el_typ in st_args:
|
||||
assert val_el_name in val, f'Missing key value {val_el_name}'
|
||||
val_el_val = val.pop(val_el_name)
|
||||
offset += _write_memory_stored_value(runner, offset, val_el_typ, val_el_val)
|
||||
|
||||
assert not val, f'Additional values: {list(val)!r}'
|
||||
|
||||
return adr
|
||||
|
||||
def _allocate_memory_stored_tuple(attrs: tuple[runners.RunnerBase, Any], tp_args: tuple[type3types.Type3, ...]) -> int:
|
||||
runner, val = attrs
|
||||
|
||||
tp_args = prelude.tuple_.did_construct(val_typ)
|
||||
if tp_args is not None:
|
||||
assert isinstance(val, tuple)
|
||||
|
||||
alloc_size = calculate_alloc_size(val_typ)
|
||||
alloc_size = calculate_alloc_size_tuple(False, tp_args)
|
||||
adr = runner.call('stdlib.alloc.__alloc__', alloc_size)
|
||||
assert isinstance(adr, int)
|
||||
sys.stderr.write(f'Allocation 0x{adr:08x} {repr(val)}\n')
|
||||
@ -222,31 +206,14 @@ def _allocate_memory_stored_value(
|
||||
|
||||
offset = adr
|
||||
for val_el_val, val_el_typ in zip(val, tp_args, strict=True):
|
||||
assert not isinstance(val_el_typ, type3placeholders.PlaceholderForType)
|
||||
|
||||
offset += _write_memory_stored_value(runner, offset, val_el_typ, val_el_val)
|
||||
return adr
|
||||
|
||||
st_args = prelude.struct.did_construct(val_typ)
|
||||
if st_args is not None:
|
||||
assert isinstance(val, dict)
|
||||
|
||||
alloc_size = calculate_alloc_size(val_typ)
|
||||
adr = runner.call('stdlib.alloc.__alloc__', alloc_size)
|
||||
assert isinstance(adr, int)
|
||||
sys.stderr.write(f'Allocation 0x{adr:08x} {repr(val)}\n')
|
||||
|
||||
assert list(val.keys()) == list(st_args)
|
||||
|
||||
offset = adr
|
||||
for val_el_name, val_el_typ in st_args.items():
|
||||
assert not isinstance(val_el_typ, type3placeholders.PlaceholderForType)
|
||||
|
||||
val_el_val = val[val_el_name]
|
||||
offset += _write_memory_stored_value(runner, offset, val_el_typ, val_el_val)
|
||||
return adr
|
||||
|
||||
raise NotImplementedError(val_typ, val)
|
||||
ALLOCATE_MEMORY_STORED_ROUTER = TypeApplicationRouter[tuple[runners.RunnerBase, Any], Any]()
|
||||
ALLOCATE_MEMORY_STORED_ROUTER.add_n(prelude.bytes_, _allocate_memory_stored_bytes)
|
||||
ALLOCATE_MEMORY_STORED_ROUTER.add(prelude.static_array, _allocate_memory_stored_static_array)
|
||||
ALLOCATE_MEMORY_STORED_ROUTER.add(prelude.struct, _allocate_memory_stored_struct)
|
||||
ALLOCATE_MEMORY_STORED_ROUTER.add(prelude.tuple_, _allocate_memory_stored_tuple)
|
||||
|
||||
def _load_memory_stored_returned_value(
|
||||
runner: runners.RunnerBase,
|
||||
@ -264,6 +231,13 @@ def _load_memory_stored_returned_value(
|
||||
|
||||
if ret_type3 in (prelude.i8, prelude.i32, prelude.i64):
|
||||
assert isinstance(wasm_value, int), wasm_value
|
||||
|
||||
if ret_type3 is prelude.i8:
|
||||
# Values are actually i32
|
||||
# Have to reinterpret to load proper value
|
||||
data = struct.pack('<i', wasm_value)
|
||||
wasm_value, = struct.unpack('<bxxx', data)
|
||||
|
||||
return wasm_value
|
||||
|
||||
if ret_type3 in (prelude.u8, prelude.u32, prelude.u64):
|
||||
@ -287,30 +261,9 @@ def _load_memory_stored_returned_value(
|
||||
assert isinstance(wasm_value, float), wasm_value
|
||||
return wasm_value
|
||||
|
||||
if ret_type3 is prelude.bytes_:
|
||||
assert isinstance(wasm_value, int), wasm_value
|
||||
|
||||
return _load_bytes_from_address(runner, ret_type3, wasm_value)
|
||||
|
||||
assert isinstance(ret_type3, type3types.Type3) # Type hint
|
||||
|
||||
sa_args = prelude.static_array.did_construct(ret_type3)
|
||||
if sa_args is not None:
|
||||
assert isinstance(wasm_value, int), wasm_value
|
||||
|
||||
return _load_static_array_from_address(runner, sa_args[0], sa_args[1], wasm_value)
|
||||
|
||||
tp_args = prelude.tuple_.did_construct(ret_type3)
|
||||
if tp_args is not None:
|
||||
assert isinstance(wasm_value, int), wasm_value
|
||||
|
||||
return _load_tuple_from_address(runner, tp_args, wasm_value)
|
||||
|
||||
st_args = prelude.struct.did_construct(ret_type3)
|
||||
if st_args is not None:
|
||||
return _load_struct_from_address(runner, st_args, wasm_value)
|
||||
|
||||
raise NotImplementedError(ret_type3, wasm_value)
|
||||
return LOAD_FROM_ADDRESS_ROUTER((runner, wasm_value), ret_type3)
|
||||
|
||||
def _unpack(runner: runners.RunnerBase, typ: type3types.Type3, inp: bytes) -> Any:
|
||||
if typ is prelude.u8:
|
||||
@ -347,41 +300,19 @@ def _unpack(runner: runners.RunnerBase, typ: type3types.Type3, inp: bytes) -> An
|
||||
assert len(inp) == 8
|
||||
return struct.unpack('<d', inp)[0]
|
||||
|
||||
if typ is prelude.bytes_:
|
||||
# Note: For bytes, inp should contain a 4 byte pointer
|
||||
assert len(inp) == 4
|
||||
adr = struct.unpack('<I', inp)[0]
|
||||
|
||||
return _load_bytes_from_address(runner, typ, adr)
|
||||
|
||||
if prelude.InternalPassAsPointer in typ.classes:
|
||||
if (prelude.InternalPassAsPointer, (typ, )) in prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING:
|
||||
# Note: For applied types, inp should contain a 4 byte pointer
|
||||
assert len(inp) == 4
|
||||
adr = struct.unpack('<I', inp)[0]
|
||||
|
||||
assert isinstance(typ, type3types.Type3)
|
||||
|
||||
sa_args = prelude.static_array.did_construct(typ)
|
||||
if sa_args is not None:
|
||||
sa_type, sa_len = sa_args
|
||||
return _load_static_array_from_address(runner, sa_type, sa_len, adr)
|
||||
|
||||
tp_args = prelude.tuple_.did_construct(typ)
|
||||
if tp_args is not None:
|
||||
return _load_tuple_from_address(runner, tp_args, adr)
|
||||
|
||||
st_args = prelude.struct.did_construct(typ)
|
||||
if st_args is not None:
|
||||
# Note: For structs, inp should contain a 4 byte pointer
|
||||
assert len(inp) == 4
|
||||
adr = struct.unpack('<I', inp)[0]
|
||||
|
||||
return _load_struct_from_address(runner, st_args, adr)
|
||||
return LOAD_FROM_ADDRESS_ROUTER((runner, adr), typ)
|
||||
|
||||
raise NotImplementedError(typ, inp)
|
||||
|
||||
def _load_bytes_from_address(runner: runners.RunnerBase, typ: type3types.Type3, adr: int) -> bytes:
|
||||
sys.stderr.write(f'Reading 0x{adr:08x} {typ:s}\n')
|
||||
def _load_bytes_from_address(attrs: tuple[runners.RunnerBase, int]) -> bytes:
|
||||
runner, adr = attrs
|
||||
|
||||
sys.stderr.write(f'Reading 0x{adr:08x} bytes\n')
|
||||
read_bytes = runner.interpreter_read_memory(adr, 4)
|
||||
bytes_len, = struct.unpack('<I', read_bytes)
|
||||
|
||||
@ -394,11 +325,11 @@ def _split_read_bytes(all_bytes: bytes, split_sizes: Iterable[int]) -> Generator
|
||||
yield all_bytes[offset:offset + size]
|
||||
offset += size
|
||||
|
||||
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')
|
||||
def _load_static_array_from_address(attrs: tuple[runners.RunnerBase, int], sa_args: tuple[type3types.Type3, type3types.IntType3]) -> Any:
|
||||
runner, adr = attrs
|
||||
sub_typ, len_typ = sa_args
|
||||
|
||||
assert not isinstance(sub_typ, type3placeholders.PlaceholderForType)
|
||||
assert isinstance(len_typ, type3types.IntType3)
|
||||
sys.stderr.write(f'Reading 0x{adr:08x} {sub_typ:s} {len_typ:s}\n')
|
||||
|
||||
sa_len = len_typ.value
|
||||
|
||||
@ -412,37 +343,42 @@ def _load_static_array_from_address(runner: runners.RunnerBase, sub_typ: type3ty
|
||||
for arg_bytes in _split_read_bytes(read_bytes, arg_sizes)
|
||||
)
|
||||
|
||||
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')
|
||||
def _load_struct_from_address(attrs: tuple[runners.RunnerBase, int], st_args: tuple[tuple[str, type3types.Type3], ...]) -> dict[str, Any]:
|
||||
runner, adr = attrs
|
||||
|
||||
arg_sizes = [
|
||||
calculate_alloc_size(x, is_member=True)
|
||||
for x in typ_args
|
||||
]
|
||||
|
||||
read_bytes = runner.interpreter_read_memory(adr, sum(arg_sizes))
|
||||
|
||||
return tuple(
|
||||
_unpack(runner, arg_typ, arg_bytes)
|
||||
for arg_typ, arg_bytes in zip(typ_args, _split_read_bytes(read_bytes, arg_sizes), strict=True)
|
||||
)
|
||||
|
||||
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} struct {list(st_args)}\n')
|
||||
|
||||
name_list = list(st_args)
|
||||
|
||||
typ_list = list(st_args.values())
|
||||
assert len(typ_list) == len(st_args)
|
||||
|
||||
arg_sizes = [
|
||||
calculate_alloc_size(x, is_member=True)
|
||||
for x in typ_list
|
||||
for _, x in st_args
|
||||
]
|
||||
|
||||
read_bytes = runner.interpreter_read_memory(adr, sum(arg_sizes))
|
||||
|
||||
return {
|
||||
arg_name: _unpack(runner, arg_typ, arg_bytes)
|
||||
for arg_name, arg_typ, arg_bytes in zip(name_list, typ_list, _split_read_bytes(read_bytes, arg_sizes), strict=True)
|
||||
for (arg_name, arg_typ, ), arg_bytes in zip(st_args, _split_read_bytes(read_bytes, arg_sizes), strict=True)
|
||||
}
|
||||
|
||||
def _load_tuple_from_address(attrs: tuple[runners.RunnerBase, int], tp_args: tuple[type3types.Type3, ...]) -> Any:
|
||||
runner, adr = attrs
|
||||
|
||||
sys.stderr.write(f'Reading 0x{adr:08x} tuple {len(tp_args)}\n')
|
||||
|
||||
arg_sizes = [
|
||||
calculate_alloc_size(x, is_member=True)
|
||||
for x in tp_args
|
||||
]
|
||||
|
||||
read_bytes = runner.interpreter_read_memory(adr, sum(arg_sizes))
|
||||
|
||||
return tuple(
|
||||
_unpack(runner, arg_typ, arg_bytes)
|
||||
for arg_typ, arg_bytes in zip(tp_args, _split_read_bytes(read_bytes, arg_sizes), strict=True)
|
||||
)
|
||||
|
||||
LOAD_FROM_ADDRESS_ROUTER = TypeApplicationRouter[tuple[runners.RunnerBase, int], Any]()
|
||||
LOAD_FROM_ADDRESS_ROUTER.add_n(prelude.bytes_, _load_bytes_from_address)
|
||||
LOAD_FROM_ADDRESS_ROUTER.add(prelude.static_array, _load_static_array_from_address)
|
||||
LOAD_FROM_ADDRESS_ROUTER.add(prelude.struct, _load_struct_from_address)
|
||||
LOAD_FROM_ADDRESS_ROUTER.add(prelude.tuple_, _load_tuple_from_address)
|
||||
|
||||
@ -49,7 +49,7 @@ def testEntry(a: bytes, b: bytes) -> u8:
|
||||
def test_foldl_3():
|
||||
code_py = """
|
||||
def xor(l: u32, r: u8) -> u32:
|
||||
return l ^ u32(r)
|
||||
return l ^ extend(r)
|
||||
|
||||
@exported
|
||||
def testEntry(a: bytes) -> u32:
|
||||
|
||||
30
tests/integration/test_lang/test_bytes.py
Normal file
30
tests/integration/test_lang/test_bytes.py
Normal file
@ -0,0 +1,30 @@
|
||||
import pytest
|
||||
|
||||
from ..helpers import Suite
|
||||
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_bytes_export_constant():
|
||||
code_py = """
|
||||
CONSTANT: bytes = b'Hello'
|
||||
|
||||
@exported
|
||||
def testEntry() -> bytes:
|
||||
return CONSTANT
|
||||
"""
|
||||
|
||||
result = Suite(code_py).run_code()
|
||||
|
||||
assert b"Hello" == result.returned_value
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_bytes_export_instantiation():
|
||||
code_py = """
|
||||
@exported
|
||||
def testEntry() -> bytes:
|
||||
return b'Hello'
|
||||
"""
|
||||
|
||||
result = Suite(code_py).run_code()
|
||||
|
||||
assert b"Hello" == result.returned_value
|
||||
@ -29,7 +29,7 @@ def testEntry(x: Foo, y: Foo) -> Foo:
|
||||
return x == y
|
||||
"""
|
||||
|
||||
with pytest.raises(Type3Exception, match='Foo does not implement the Eq type class'):
|
||||
with pytest.raises(Type3Exception, match='Missing type class instantation: Eq Foo'):
|
||||
Suite(code_py).run_code()
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@ -111,7 +111,7 @@ def testEntry(x: Foo, y: Foo) -> Foo:
|
||||
return x != y
|
||||
"""
|
||||
|
||||
with pytest.raises(Type3Exception, match='Foo does not implement the Eq type class'):
|
||||
with pytest.raises(Type3Exception, match='Missing type class instantation: Eq Foo'):
|
||||
Suite(code_py).run_code()
|
||||
|
||||
@pytest.mark.integration_test
|
||||
|
||||
112
tests/integration/test_lang/test_extendable.py
Normal file
112
tests/integration/test_lang/test_extendable.py
Normal file
@ -0,0 +1,112 @@
|
||||
import pytest
|
||||
|
||||
from phasm.type3.entry import Type3Exception
|
||||
|
||||
from ..helpers import Suite
|
||||
|
||||
EXTENTABLE = [
|
||||
('u8', 'u32', ),
|
||||
('u8', 'u64', ),
|
||||
('u32', 'u64', ),
|
||||
|
||||
('i8', 'i32', ),
|
||||
('i8', 'i64', ),
|
||||
('i32', 'i64', ),
|
||||
]
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_extend_not_implemented():
|
||||
code_py = """
|
||||
class Foo:
|
||||
val: i32
|
||||
|
||||
class Baz:
|
||||
val: i32
|
||||
|
||||
@exported
|
||||
def testEntry(x: Foo) -> Baz:
|
||||
return extend(x)
|
||||
"""
|
||||
|
||||
with pytest.raises(Type3Exception, match='Missing type class instantation: Extendable Foo Baz'):
|
||||
Suite(code_py).run_code()
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@pytest.mark.parametrize('ext_from,ext_to', EXTENTABLE)
|
||||
def test_extend_ok(ext_from,ext_to):
|
||||
code_py = f"""
|
||||
CONSTANT: {ext_from} = 10
|
||||
|
||||
@exported
|
||||
def testEntry() -> {ext_to}:
|
||||
return extend(CONSTANT)
|
||||
"""
|
||||
|
||||
result = Suite(code_py).run_code()
|
||||
|
||||
assert 10 == result.returned_value
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@pytest.mark.parametrize('ext_from,in_put,ext_to,exp_out', [
|
||||
('u8', 241, 'u32', 241),
|
||||
('u32', 4059165169, 'u64', 4059165169),
|
||||
('u8', 241, 'u64', 241),
|
||||
|
||||
('i8', 113, 'i32', 113),
|
||||
('i32', 1911681521, 'i64', 1911681521),
|
||||
('i8', 113, 'i64', 113),
|
||||
|
||||
('i8', -15, 'i32', -15),
|
||||
('i32', -15, 'i64', -15),
|
||||
('i8', -15, 'i64', -15),
|
||||
|
||||
])
|
||||
def test_extend_results(ext_from, ext_to, in_put, exp_out):
|
||||
code_py = f"""
|
||||
@exported
|
||||
def testEntry(x: {ext_from}) -> {ext_to}:
|
||||
return extend(x)
|
||||
"""
|
||||
|
||||
result = Suite(code_py).run_code(in_put)
|
||||
|
||||
assert exp_out == result.returned_value
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@pytest.mark.parametrize('ext_from,ext_to', EXTENTABLE)
|
||||
def test_wrap_ok(ext_from,ext_to):
|
||||
code_py = f"""
|
||||
CONSTANT: {ext_to} = 10
|
||||
|
||||
@exported
|
||||
def testEntry() -> {ext_from}:
|
||||
return wrap(CONSTANT)
|
||||
"""
|
||||
|
||||
result = Suite(code_py).run_code()
|
||||
|
||||
assert 10 == result.returned_value
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@pytest.mark.parametrize('ext_to,in_put,ext_from,exp_out', [
|
||||
('u32', 0xF1F1F1F1, 'u8', 0xF1),
|
||||
('u64', 0xF1F1F1F1F1F1F1F1, 'u32', 0xF1F1F1F1),
|
||||
('u64', 0xF1F1F1F1F1F1F1F1, 'u8', 0xF1),
|
||||
|
||||
('i32', 0xF1F1F171, 'i8', 113),
|
||||
('i32', 0xF1F1F1F1, 'i8', -15),
|
||||
('i64', 0x71F1F1F171F1F1F1, 'i32', 1911681521),
|
||||
('i64', 0x71F1F1F1F1F1F1F1, 'i32', -235802127),
|
||||
('i64', 0xF1F1F1F1F1F1F171, 'i8', 113),
|
||||
('i64', 0xF1F1F1F1F1F1F1F1, 'i8', -15),
|
||||
])
|
||||
def test_wrap_results(ext_from, ext_to, in_put, exp_out):
|
||||
code_py = f"""
|
||||
@exported
|
||||
def testEntry(x: {ext_to}) -> {ext_from}:
|
||||
return wrap(x)
|
||||
"""
|
||||
|
||||
result = Suite(code_py).run_code(in_put)
|
||||
|
||||
assert exp_out == result.returned_value
|
||||
71
tests/integration/test_lang/test_foldable.py
Normal file
71
tests/integration/test_lang/test_foldable.py
Normal file
@ -0,0 +1,71 @@
|
||||
import pytest
|
||||
|
||||
from phasm.type3.entry import Type3Exception
|
||||
|
||||
from ..helpers import Suite
|
||||
from .test_natnum import FLOAT_TYPES, INT_TYPES
|
||||
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@pytest.mark.parametrize('length', [1, 5, 13])
|
||||
@pytest.mark.parametrize('a_type', INT_TYPES + FLOAT_TYPES)
|
||||
def test_foldable_sum(length, a_type):
|
||||
code_py = f"""
|
||||
@exported
|
||||
def testEntry(x: {a_type}[{length}]) -> {a_type}:
|
||||
return sum(x)
|
||||
"""
|
||||
|
||||
in_put = tuple(range(length))
|
||||
|
||||
result = Suite(code_py).run_code(in_put)
|
||||
|
||||
assert sum(in_put) == result.returned_value
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_foldable_sum_not_natnum():
|
||||
code_py = """
|
||||
class Foo:
|
||||
bar: i32
|
||||
|
||||
@exported
|
||||
def testEntry(x: Foo[4]) -> Foo:
|
||||
return sum(x)
|
||||
"""
|
||||
|
||||
with pytest.raises(Type3Exception, match='Missing type class instantation: NatNum Foo'):
|
||||
Suite(code_py).run_code()
|
||||
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_foldable_invalid_return_type():
|
||||
code_py = """
|
||||
@exported
|
||||
def testEntry(x: i32[5]) -> f64:
|
||||
return sum(x)
|
||||
"""
|
||||
|
||||
with pytest.raises(Type3Exception, match='i32 must be f64 instead'):
|
||||
Suite(code_py).run_code((4, 5, 6, 7, 8, ))
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_foldable_not_constructed():
|
||||
code_py = """
|
||||
@exported
|
||||
def testEntry(x: i32) -> i32:
|
||||
return sum(x)
|
||||
"""
|
||||
|
||||
with pytest.raises(Type3Exception, match='Missing type class instantation: Foldable i32.*i32 must be a constructed type instead'):
|
||||
Suite(code_py).run_code()
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_foldable_not_foldable():
|
||||
code_py = """
|
||||
@exported
|
||||
def testEntry(x: (i32, u32, )) -> i32:
|
||||
return sum(x)
|
||||
"""
|
||||
|
||||
with pytest.raises(Type3Exception, match='Missing type class instantation: Foldable tuple'):
|
||||
Suite(code_py).run_code()
|
||||
@ -6,7 +6,20 @@ from ..helpers import Suite
|
||||
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_expr_constant_literal_does_not_fit():
|
||||
def test_expr_constant_literal_does_not_fit_module_constant():
|
||||
code_py = """
|
||||
CONSTANT: u8 = 1000
|
||||
|
||||
@exported
|
||||
def testEntry() -> u8:
|
||||
return CONSTANT
|
||||
"""
|
||||
|
||||
with pytest.raises(Type3Exception, match=r'Must fit in 1 byte\(s\)'):
|
||||
Suite(code_py).run_code()
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_expr_constant_literal_does_not_fit_return():
|
||||
code_py = """
|
||||
@exported
|
||||
def testEntry() -> u8:
|
||||
|
||||
@ -27,7 +27,7 @@ def testEntry(x: Foo, y: Foo) -> Foo:
|
||||
return x + y
|
||||
"""
|
||||
|
||||
with pytest.raises(Type3Exception, match='Foo does not implement the NatNum type class'):
|
||||
with pytest.raises(Type3Exception, match='Missing type class instantation: NatNum Foo'):
|
||||
Suite(code_py).run_code()
|
||||
|
||||
@pytest.mark.integration_test
|
||||
|
||||
51
tests/integration/test_lang/test_promotable.py
Normal file
51
tests/integration/test_lang/test_promotable.py
Normal file
@ -0,0 +1,51 @@
|
||||
import pytest
|
||||
|
||||
from phasm.type3.entry import Type3Exception
|
||||
|
||||
from ..helpers import Suite
|
||||
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_promote_not_implemented():
|
||||
code_py = """
|
||||
class Foo:
|
||||
val: i32
|
||||
|
||||
class Baz:
|
||||
val: i32
|
||||
|
||||
@exported
|
||||
def testEntry(x: Foo) -> Baz:
|
||||
return promote(x)
|
||||
"""
|
||||
|
||||
with pytest.raises(Type3Exception, match='Missing type class instantation: Promotable Foo Baz'):
|
||||
Suite(code_py).run_code()
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_promote_ok():
|
||||
code_py = """
|
||||
CONSTANT: f32 = 10.5
|
||||
|
||||
@exported
|
||||
def testEntry() -> f64:
|
||||
return promote(CONSTANT)
|
||||
"""
|
||||
|
||||
result = Suite(code_py).run_code()
|
||||
|
||||
assert 10.5 == result.returned_value
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_demote_ok():
|
||||
code_py = """
|
||||
CONSTANT: f64 = 10.5
|
||||
|
||||
@exported
|
||||
def testEntry() -> f32:
|
||||
return demote(CONSTANT)
|
||||
"""
|
||||
|
||||
result = Suite(code_py).run_code()
|
||||
|
||||
assert 10.5 == result.returned_value
|
||||
@ -30,3 +30,29 @@ def testEntry() -> i32:
|
||||
|
||||
with pytest.raises(Type3Exception, match='Member count mismatch'):
|
||||
Suite(code_py).run_code()
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_static_array_export_constant():
|
||||
code_py = """
|
||||
CONSTANT: u8[3] = (1, 2, 3, )
|
||||
|
||||
@exported
|
||||
def testEntry() -> u8[3]:
|
||||
return CONSTANT
|
||||
"""
|
||||
|
||||
result = Suite(code_py).run_code()
|
||||
|
||||
assert (1, 2, 3) == result.returned_value
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_static_array_export_instantiation():
|
||||
code_py = """
|
||||
@exported
|
||||
def testEntry() -> u8[3]:
|
||||
return (1, 2, 3, )
|
||||
"""
|
||||
|
||||
result = Suite(code_py).run_code()
|
||||
|
||||
assert (1, 2, 3) == result.returned_value
|
||||
|
||||
@ -64,6 +64,36 @@ def helper(shape1: Rectangle, shape2: Rectangle) -> i32:
|
||||
|
||||
assert 545 == result.returned_value
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_type_mismatch_struct_call_root():
|
||||
code_py = """
|
||||
class CheckedValueBlue:
|
||||
value: i32
|
||||
|
||||
class CheckedValueRed:
|
||||
value: i32
|
||||
|
||||
CONST: CheckedValueBlue = CheckedValueRed(1)
|
||||
"""
|
||||
|
||||
with pytest.raises(Type3Exception, match='CheckedValueBlue must be CheckedValueRed instead'):
|
||||
Suite(code_py).run_code()
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_type_mismatch_struct_call_nested():
|
||||
code_py = """
|
||||
class CheckedValueBlue:
|
||||
value: i32
|
||||
|
||||
class CheckedValueRed:
|
||||
value: i32
|
||||
|
||||
CONST: (CheckedValueBlue, u32, ) = (CheckedValueRed(1), 16, )
|
||||
"""
|
||||
|
||||
with pytest.raises(Type3Exception, match='CheckedValueBlue must be CheckedValueRed instead'):
|
||||
Suite(code_py).run_code()
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@pytest.mark.parametrize('type_', ['i32', 'i64', 'f32', 'f64'])
|
||||
def test_type_mismatch_struct_member(type_):
|
||||
@ -100,3 +130,47 @@ class f32:
|
||||
|
||||
with pytest.raises(StaticError, match='f32 already defined as type'):
|
||||
Suite(code_py).run_code()
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@pytest.mark.skip(reason='FIXME: See constraintgenerator.py for AccessStructMember')
|
||||
def test_struct_not_accessible():
|
||||
code_py = """
|
||||
@exported
|
||||
def testEntry(x: u8) -> u8:
|
||||
return x.y
|
||||
"""
|
||||
|
||||
with pytest.raises(Type3Exception, match='u8 is not struct'):
|
||||
Suite(code_py).run_code()
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_struct_export_constant():
|
||||
code_py = """
|
||||
class CheckedValue:
|
||||
value: i32
|
||||
|
||||
CONSTANT: CheckedValue = CheckedValue(32)
|
||||
|
||||
@exported
|
||||
def testEntry() -> CheckedValue:
|
||||
return CONSTANT
|
||||
"""
|
||||
|
||||
result = Suite(code_py).run_code()
|
||||
|
||||
assert {"value": 32} == result.returned_value
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_struct_export_instantiation():
|
||||
code_py = """
|
||||
class CheckedValue:
|
||||
value: i32
|
||||
|
||||
@exported
|
||||
def testEntry() -> CheckedValue:
|
||||
return CheckedValue(32)
|
||||
"""
|
||||
|
||||
result = Suite(code_py).run_code()
|
||||
|
||||
assert {"value": 32} == result.returned_value
|
||||
|
||||
@ -23,6 +23,23 @@ def testEntry(f: {type_}) -> u8:
|
||||
|
||||
assert exp_result == result.returned_value
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@pytest.mark.parametrize('type_, in_put, exp_result', [
|
||||
('(u8, u8, u8, )', (45, 46, 47), 47, ),
|
||||
('u8[5]', (45, 46, 47, 48, 49), 47, ),
|
||||
('bytes', b'This is a test', 105)
|
||||
])
|
||||
def test_subscript_2(type_, in_put, exp_result):
|
||||
code_py = f"""
|
||||
@exported
|
||||
def testEntry(f: {type_}) -> u8:
|
||||
return f[2]
|
||||
"""
|
||||
|
||||
result = Suite(code_py).run_code(in_put)
|
||||
|
||||
assert exp_result == result.returned_value
|
||||
|
||||
@pytest.mark.integration_test
|
||||
@pytest.mark.parametrize('type_, in_put, exp_result', [
|
||||
('(u8, u8, )', (45, 46), 45, ),
|
||||
@ -47,7 +64,7 @@ def testEntry(x: (u8, u32, u64), y: u8) -> u64:
|
||||
return x[y]
|
||||
"""
|
||||
|
||||
with pytest.raises(Type3Exception, match='Must index with literal'):
|
||||
with pytest.raises(Type3Exception, match='Must index with integer literal'):
|
||||
Suite(code_py).run_code()
|
||||
|
||||
@pytest.mark.integration_test
|
||||
|
||||
@ -70,3 +70,29 @@ CONSTANT: (u32, u8, u8, ) = (24, 4000, 1, )
|
||||
|
||||
with pytest.raises(Type3Exception, match=r'Must fit in 1 byte\(s\)'):
|
||||
Suite(code_py).run_code()
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_tuple_export_constant():
|
||||
code_py = """
|
||||
CONSTANT: (u32, u8, u8, ) = (4000, 20, 20, )
|
||||
|
||||
@exported
|
||||
def testEntry() -> (u32, u8, u8, ):
|
||||
return CONSTANT
|
||||
"""
|
||||
|
||||
result = Suite(code_py).run_code()
|
||||
|
||||
assert (4000, 20, 20, ) == result.returned_value
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_tuple_export_instantiation():
|
||||
code_py = """
|
||||
@exported
|
||||
def testEntry() -> (u32, u8, u8, ):
|
||||
return (4000, 20, 20, )
|
||||
"""
|
||||
|
||||
result = Suite(code_py).run_code()
|
||||
|
||||
assert (4000, 20, 20, ) == result.returned_value
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user