Moves the prelude to runtime
Previously, it was hardcoded at 'compile' time (in as much Python has that). This would make it more difficult to add stuff to it. Also, in a lot of places we made assumptions about prelude instead of checking properly.
This commit is contained in:
parent
d97be81828
commit
8db541dc08
3
Makefile
3
Makefile
@ -1,3 +1,4 @@
|
|||||||
|
TEST_FILES := tests
|
||||||
WAT2WASM := venv/bin/python wat2wasm.py
|
WAT2WASM := venv/bin/python wat2wasm.py
|
||||||
|
|
||||||
%.wat: %.py $(shell find phasm -name '*.py') venv/.done
|
%.wat: %.py $(shell find phasm -name '*.py') venv/.done
|
||||||
@ -16,7 +17,7 @@ examples: venv/.done $(subst .py,.wasm,$(wildcard examples/*.py)) $(subst .py,.w
|
|||||||
venv/bin/python3 -m http.server --directory examples
|
venv/bin/python3 -m http.server --directory examples
|
||||||
|
|
||||||
test: venv/.done $(subst .json,.py,$(subst /generator_,/test_generated_,$(wildcard tests/integration/test_lang/generator_*.json)))
|
test: venv/.done $(subst .json,.py,$(subst /generator_,/test_generated_,$(wildcard tests/integration/test_lang/generator_*.json)))
|
||||||
venv/bin/pytest tests $(TEST_FLAGS)
|
venv/bin/pytest $(TEST_FILES) $(TEST_FLAGS)
|
||||||
|
|
||||||
lint: venv/.done
|
lint: venv/.done
|
||||||
venv/bin/ruff check phasm tests
|
venv/bin/ruff check phasm tests
|
||||||
|
|||||||
5
TODO.md
5
TODO.md
@ -15,17 +15,12 @@
|
|||||||
- Does Subscript do what we want? It's a language feature rather a normal typed thing. How would you implement your own Subscript-able type?
|
- Does Subscript do what we want? It's a language feature rather a normal typed thing. How would you implement your own Subscript-able type?
|
||||||
- Clean up Subscript implementation - it's half implemented in the compiler. Makes more sense to move more parts to stdlib_types.
|
- Clean up Subscript implementation - it's half implemented in the compiler. Makes more sense to move more parts to stdlib_types.
|
||||||
- Have a set of rules or guidelines for the constraint comments, they're messy.
|
- Have a set of rules or guidelines for the constraint comments, they're messy.
|
||||||
- Why is expression_subscript_bytes using a helper method but expression_subscript_static_array is not?
|
|
||||||
- calculate_alloc_size can be reworked; is_member isn't useful with TYPE_INFO_MAP
|
- calculate_alloc_size can be reworked; is_member isn't useful with TYPE_INFO_MAP
|
||||||
|
|
||||||
- Parser is putting stuff in ModuleDataBlock
|
- Parser is putting stuff in ModuleDataBlock
|
||||||
- Surely the compiler should build data blocks
|
- Surely the compiler should build data blocks
|
||||||
- Also put the struct.pack constants in TYPE_INFO_MAP
|
- Also put the struct.pack constants in TYPE_INFO_MAP
|
||||||
- Make prelude more an actual thing
|
|
||||||
- Make it less build in - have a environment class of some kind
|
|
||||||
- instance_type_class should probably be part of this environment
|
|
||||||
- Implemented Bounded: https://hackage.haskell.org/package/base-4.21.0.0/docs/Prelude.html#t:Bounded
|
- 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
|
- 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
|
|
||||||
|
|
||||||
- Read https://bytecodealliance.org/articles/multi-value-all-the-wasm
|
- Read https://bytecodealliance.org/articles/multi-value-all-the-wasm
|
||||||
|
|||||||
0
phasm/build/__init__.py
Normal file
0
phasm/build/__init__.py
Normal file
339
phasm/build/base.py
Normal file
339
phasm/build/base.py
Normal file
@ -0,0 +1,339 @@
|
|||||||
|
"""
|
||||||
|
The base class for build environments.
|
||||||
|
|
||||||
|
Contains nothing but the explicit compiler builtins.
|
||||||
|
"""
|
||||||
|
from typing import Any, Callable, NamedTuple, Type
|
||||||
|
from warnings import warn
|
||||||
|
|
||||||
|
from ..type3.functions import (
|
||||||
|
TypeConstructorVariable,
|
||||||
|
TypeVariable,
|
||||||
|
)
|
||||||
|
from ..type3.routers import (
|
||||||
|
NoRouteForTypeException,
|
||||||
|
TypeApplicationRouter,
|
||||||
|
TypeClassArgsRouter,
|
||||||
|
TypeVariableLookup,
|
||||||
|
)
|
||||||
|
from ..type3.typeclasses import Type3Class, Type3ClassMethod
|
||||||
|
from ..type3.types import (
|
||||||
|
IntType3,
|
||||||
|
Type3,
|
||||||
|
TypeConstructor_Base,
|
||||||
|
TypeConstructor_DynamicArray,
|
||||||
|
TypeConstructor_Function,
|
||||||
|
TypeConstructor_StaticArray,
|
||||||
|
TypeConstructor_Struct,
|
||||||
|
TypeConstructor_Tuple,
|
||||||
|
)
|
||||||
|
from ..wasm import WasmType, WasmTypeInt32, WasmTypeNone
|
||||||
|
from . import builtins
|
||||||
|
|
||||||
|
TypeInfo = NamedTuple('TypeInfo', [
|
||||||
|
# Name of the type
|
||||||
|
('typ', str, ),
|
||||||
|
# What WebAssembly type to use when passing this value around
|
||||||
|
# For example in function arguments
|
||||||
|
('wasm_type', Type[WasmType]),
|
||||||
|
# What WebAssembly function to use when loading a value from memory
|
||||||
|
('wasm_load_func', str),
|
||||||
|
# What WebAssembly function to use when storing a value to memory
|
||||||
|
('wasm_store_func', str),
|
||||||
|
# When storing this value in memory, how many bytes do we use?
|
||||||
|
# Only valid for non-constructed types, see calculate_alloc_size
|
||||||
|
# Should match wasm_load_func / wasm_store_func
|
||||||
|
('alloc_size', int),
|
||||||
|
# When storing integers, the values can be stored as natural number
|
||||||
|
# (False) or as integer number (True). For other types, this is None.
|
||||||
|
('signed', bool | None),
|
||||||
|
])
|
||||||
|
|
||||||
|
class MissingImplementationWarning(Warning):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class BuildBase[G]:
|
||||||
|
__slots__ = (
|
||||||
|
'dynamic_array',
|
||||||
|
'function',
|
||||||
|
'static_array',
|
||||||
|
'struct',
|
||||||
|
'tuple_',
|
||||||
|
|
||||||
|
'none_',
|
||||||
|
'bool_',
|
||||||
|
|
||||||
|
'type_info_map',
|
||||||
|
'type_info_constructed',
|
||||||
|
|
||||||
|
'types',
|
||||||
|
'type_classes',
|
||||||
|
'type_class_instances',
|
||||||
|
'type_class_instance_methods',
|
||||||
|
'methods',
|
||||||
|
'operators',
|
||||||
|
|
||||||
|
'alloc_size_router',
|
||||||
|
)
|
||||||
|
|
||||||
|
dynamic_array: TypeConstructor_DynamicArray
|
||||||
|
"""
|
||||||
|
This is a dynamic length piece of memory.
|
||||||
|
|
||||||
|
It should be applied with two arguments. It has a runtime
|
||||||
|
determined length, and each argument is the same.
|
||||||
|
"""
|
||||||
|
|
||||||
|
function: TypeConstructor_Function
|
||||||
|
"""
|
||||||
|
This is a function.
|
||||||
|
|
||||||
|
It should be applied with one or more arguments. The last argument is the 'return' type.
|
||||||
|
"""
|
||||||
|
|
||||||
|
static_array: TypeConstructor_StaticArray
|
||||||
|
"""
|
||||||
|
This is a fixed length piece of memory.
|
||||||
|
|
||||||
|
It should be applied with two arguments. It has a compile time
|
||||||
|
determined length, and each argument is the same.
|
||||||
|
"""
|
||||||
|
|
||||||
|
struct: TypeConstructor_Struct
|
||||||
|
"""
|
||||||
|
This is like a tuple, but each argument is named, so that developers
|
||||||
|
can get and set fields by name.
|
||||||
|
"""
|
||||||
|
|
||||||
|
tuple_: TypeConstructor_Tuple
|
||||||
|
"""
|
||||||
|
This is a fixed length piece of memory.
|
||||||
|
|
||||||
|
It should be applied with zero or more arguments. It has a compile time
|
||||||
|
determined length, and each argument can be different.
|
||||||
|
"""
|
||||||
|
|
||||||
|
none_: Type3
|
||||||
|
"""
|
||||||
|
The none type, for when functions simply don't return anything. e.g., IO().
|
||||||
|
"""
|
||||||
|
|
||||||
|
bool_: Type3
|
||||||
|
"""
|
||||||
|
The bool type, either True or False
|
||||||
|
"""
|
||||||
|
|
||||||
|
type_info_map: dict[str, TypeInfo]
|
||||||
|
"""
|
||||||
|
Map from type name to the info of that type
|
||||||
|
"""
|
||||||
|
|
||||||
|
type_info_constructed: TypeInfo
|
||||||
|
"""
|
||||||
|
By default, constructed types are passed as pointers
|
||||||
|
NOTE: ALLOC SIZE IN THIS STRUCT DOES NOT WORK FOR CONSTRUCTED TYPES
|
||||||
|
USE calculate_alloc_size FOR ACCURATE RESULTS
|
||||||
|
Functions count as constructed types - even though they are
|
||||||
|
not memory pointers but table addresses instead.
|
||||||
|
"""
|
||||||
|
|
||||||
|
types: dict[str, Type3]
|
||||||
|
"""
|
||||||
|
Types that are available without explicit import.
|
||||||
|
"""
|
||||||
|
|
||||||
|
type_classes: dict[str, Type3Class]
|
||||||
|
"""
|
||||||
|
Type classes that are available without explicit import.
|
||||||
|
"""
|
||||||
|
|
||||||
|
type_class_instances: set[tuple[Type3Class, tuple[Type3 | TypeConstructor_Base[Any], ...]]]
|
||||||
|
"""
|
||||||
|
Type class instances that are available without explicit import.
|
||||||
|
"""
|
||||||
|
|
||||||
|
type_class_instance_methods: dict[Type3ClassMethod, TypeClassArgsRouter[G, None]]
|
||||||
|
"""
|
||||||
|
Methods (and operators) for type class instances that are available without explicit import.
|
||||||
|
"""
|
||||||
|
|
||||||
|
methods: dict[str, Type3ClassMethod]
|
||||||
|
"""
|
||||||
|
Methods that are available without explicit import.
|
||||||
|
"""
|
||||||
|
|
||||||
|
operators: dict[str, Type3ClassMethod]
|
||||||
|
"""
|
||||||
|
Operators that are available without explicit import.
|
||||||
|
"""
|
||||||
|
|
||||||
|
alloc_size_router: TypeApplicationRouter['BuildBase[G]', int]
|
||||||
|
"""
|
||||||
|
Helper value for calculate_alloc_size.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.dynamic_array = builtins.dynamic_array
|
||||||
|
self.function = builtins.function
|
||||||
|
self.static_array = builtins.static_array
|
||||||
|
self.struct = builtins.struct
|
||||||
|
self.tuple_ = builtins.tuple_
|
||||||
|
|
||||||
|
self.bool_ = builtins.bool_
|
||||||
|
self.none_ = builtins.none_
|
||||||
|
|
||||||
|
self.type_info_map = {
|
||||||
|
'None': TypeInfo('ptr', WasmTypeNone, 'unreachable', 'unreachable', 0, None),
|
||||||
|
'bool': TypeInfo('bool', WasmTypeInt32, 'unreachable', 'unreachable', 0, None),
|
||||||
|
'ptr': TypeInfo('ptr', WasmTypeInt32, 'i32.load', 'i32.store', 4, False),
|
||||||
|
}
|
||||||
|
self.type_info_constructed = self.type_info_map['ptr']
|
||||||
|
|
||||||
|
self.types = {
|
||||||
|
'None': self.none_,
|
||||||
|
'bool': self.bool_,
|
||||||
|
}
|
||||||
|
self.type_classes = {}
|
||||||
|
self.type_class_instances = set()
|
||||||
|
self.type_class_instance_methods = {}
|
||||||
|
self.methods = {}
|
||||||
|
self.operators = {}
|
||||||
|
|
||||||
|
self.alloc_size_router = TypeApplicationRouter['BuildBase[G]', int]()
|
||||||
|
self.alloc_size_router.add(self.static_array, self.__class__.calculate_alloc_size_static_array)
|
||||||
|
self.alloc_size_router.add(self.struct, self.__class__.calculate_alloc_size_struct)
|
||||||
|
self.alloc_size_router.add(self.tuple_, self.__class__.calculate_alloc_size_tuple)
|
||||||
|
|
||||||
|
def register_type_class(self, cls: Type3Class) -> None:
|
||||||
|
"""
|
||||||
|
Register that the given type class exists
|
||||||
|
"""
|
||||||
|
old_len_methods = len(self.methods)
|
||||||
|
old_len_operators = len(self.operators)
|
||||||
|
|
||||||
|
self.type_classes[cls.name] = cls
|
||||||
|
self.methods.update(cls.methods)
|
||||||
|
self.operators.update(cls.operators)
|
||||||
|
|
||||||
|
assert len(self.methods) == old_len_methods + len(cls.methods), 'Duplicated method detected'
|
||||||
|
assert len(self.operators) == old_len_operators + len(cls.operators), 'Duplicated operator detected'
|
||||||
|
|
||||||
|
def instance_type_class(
|
||||||
|
self,
|
||||||
|
cls: Type3Class,
|
||||||
|
*typ: Type3 | TypeConstructor_Base[Any],
|
||||||
|
methods: dict[str, Callable[[G, TypeVariableLookup], None]] = {},
|
||||||
|
operators: dict[str, Callable[[G, TypeVariableLookup], None]] = {},
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Registered the given type class and its implementation
|
||||||
|
"""
|
||||||
|
assert len(cls.args) == len(typ)
|
||||||
|
|
||||||
|
for incls in cls.inherited_classes:
|
||||||
|
if (incls, tuple(typ), ) not in self.type_class_instances:
|
||||||
|
warn(MissingImplementationWarning(
|
||||||
|
incls.name + ' ' + ' '.join(x.name for x in typ) + ' - required for ' + cls.name
|
||||||
|
))
|
||||||
|
|
||||||
|
# First just register the type
|
||||||
|
self.type_class_instances.add((cls, tuple(typ), ))
|
||||||
|
|
||||||
|
# Then make the implementation findable
|
||||||
|
# We route based on the type class arguments.
|
||||||
|
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)
|
||||||
|
|
||||||
|
for method_name, method in cls.methods.items():
|
||||||
|
router = self.type_class_instance_methods.get(method)
|
||||||
|
if router is None:
|
||||||
|
router = TypeClassArgsRouter[G, None](cls.args)
|
||||||
|
self.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 = self.type_class_instance_methods.get(operator)
|
||||||
|
if router is None:
|
||||||
|
router = TypeClassArgsRouter[G, None](cls.args)
|
||||||
|
self.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)
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_alloc_size_static_array(self, args: tuple[Type3, IntType3]) -> int:
|
||||||
|
"""
|
||||||
|
Helper method for calculate_alloc_size - static_array
|
||||||
|
"""
|
||||||
|
sa_type, sa_len = args
|
||||||
|
|
||||||
|
return sa_len.value * self.calculate_alloc_size(sa_type, is_member=True)
|
||||||
|
|
||||||
|
def calculate_alloc_size_tuple(self, args: tuple[Type3, ...]) -> int:
|
||||||
|
"""
|
||||||
|
Helper method for calculate_alloc_size - tuple
|
||||||
|
"""
|
||||||
|
return sum(
|
||||||
|
self.calculate_alloc_size(x, is_member=True)
|
||||||
|
for x in args
|
||||||
|
)
|
||||||
|
|
||||||
|
def calculate_alloc_size_struct(self, args: tuple[tuple[str, Type3], ...]) -> int:
|
||||||
|
"""
|
||||||
|
Helper method for calculate_alloc_size - struct
|
||||||
|
"""
|
||||||
|
return sum(
|
||||||
|
self.calculate_alloc_size(x, is_member=True)
|
||||||
|
for _, x in args
|
||||||
|
)
|
||||||
|
|
||||||
|
def calculate_alloc_size(self, typ: Type3, is_member: bool = False) -> int:
|
||||||
|
"""
|
||||||
|
Calculates how much bytes you need to allocate when reserving memory for the given type.
|
||||||
|
"""
|
||||||
|
typ_info = self.type_info_map.get(typ.name)
|
||||||
|
if typ_info is not None:
|
||||||
|
return typ_info.alloc_size
|
||||||
|
|
||||||
|
if is_member:
|
||||||
|
return self.type_info_constructed.alloc_size
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.alloc_size_router(self, typ)
|
||||||
|
except NoRouteForTypeException:
|
||||||
|
raise NotImplementedError(typ)
|
||||||
|
|
||||||
|
def calculate_member_offset(self, st_name: str, st_args: tuple[tuple[str, Type3], ...], needle: str) -> int:
|
||||||
|
"""
|
||||||
|
Calculates the amount of bytes that should be skipped in memory befor reaching the struct's property with the given name.
|
||||||
|
"""
|
||||||
|
result = 0
|
||||||
|
|
||||||
|
for memnam, memtyp in st_args:
|
||||||
|
if needle == memnam:
|
||||||
|
return result
|
||||||
|
|
||||||
|
result += self.calculate_alloc_size(memtyp, is_member=True)
|
||||||
|
|
||||||
|
raise Exception(f'{needle} not in {st_name}')
|
||||||
26
phasm/build/builtins.py
Normal file
26
phasm/build/builtins.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
"""
|
||||||
|
Type (constructors) that are used integral to the compiler.
|
||||||
|
|
||||||
|
These cannot be changed as there is compiler logic depending on them.
|
||||||
|
|
||||||
|
For mode documentation, see base.py.
|
||||||
|
"""
|
||||||
|
from ..type3.types import (
|
||||||
|
Type3,
|
||||||
|
TypeApplication_Nullary,
|
||||||
|
TypeConstructor_DynamicArray,
|
||||||
|
TypeConstructor_Function,
|
||||||
|
TypeConstructor_StaticArray,
|
||||||
|
TypeConstructor_Struct,
|
||||||
|
TypeConstructor_Tuple,
|
||||||
|
)
|
||||||
|
|
||||||
|
dynamic_array = TypeConstructor_DynamicArray('dynamic_array')
|
||||||
|
function = TypeConstructor_Function('function')
|
||||||
|
static_array = TypeConstructor_StaticArray('static_array')
|
||||||
|
struct = TypeConstructor_Struct('struct')
|
||||||
|
tuple_ = TypeConstructor_Tuple('tuple')
|
||||||
|
|
||||||
|
bool_ = Type3('bool', TypeApplication_Nullary(None, None))
|
||||||
|
none_ = Type3('None', TypeApplication_Nullary(None, None))
|
||||||
|
|
||||||
99
phasm/build/default.py
Normal file
99
phasm/build/default.py
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
"""
|
||||||
|
The default class for build environments.
|
||||||
|
|
||||||
|
Contains the compiler builtins as well as some sane defaults.
|
||||||
|
|
||||||
|
# Added types
|
||||||
|
|
||||||
|
f32: A 32-bits IEEE 754 float, of 32 bits width.
|
||||||
|
"""
|
||||||
|
from ..type3.types import (
|
||||||
|
Type3,
|
||||||
|
TypeApplication_Nullary,
|
||||||
|
)
|
||||||
|
from ..wasm import (
|
||||||
|
WasmTypeFloat32,
|
||||||
|
WasmTypeFloat64,
|
||||||
|
WasmTypeInt32,
|
||||||
|
WasmTypeInt64,
|
||||||
|
)
|
||||||
|
from ..wasmgenerator import Generator
|
||||||
|
from .base import BuildBase, TypeInfo
|
||||||
|
from .typeclasses import (
|
||||||
|
bits,
|
||||||
|
convertable,
|
||||||
|
eq,
|
||||||
|
extendable,
|
||||||
|
floating,
|
||||||
|
foldable,
|
||||||
|
fractional,
|
||||||
|
integral,
|
||||||
|
intnum,
|
||||||
|
natnum,
|
||||||
|
ord,
|
||||||
|
promotable,
|
||||||
|
reinterpretable,
|
||||||
|
sized,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class BuildDefault(BuildBase[Generator]):
|
||||||
|
def __init__(self) -> None:
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
u8 = Type3('u8', TypeApplication_Nullary(None, None))
|
||||||
|
u16 = Type3('u16', TypeApplication_Nullary(None, None))
|
||||||
|
u32 = Type3('u32', TypeApplication_Nullary(None, None))
|
||||||
|
u64 = Type3('u64', TypeApplication_Nullary(None, None))
|
||||||
|
i8 = Type3('i8', TypeApplication_Nullary(None, None))
|
||||||
|
i16 = Type3('i16', TypeApplication_Nullary(None, None))
|
||||||
|
i32 = Type3('i32', TypeApplication_Nullary(None, None))
|
||||||
|
i64 = Type3('i64', TypeApplication_Nullary(None, None))
|
||||||
|
f32 = Type3('f32', TypeApplication_Nullary(None, None))
|
||||||
|
f64 = Type3('f64', TypeApplication_Nullary(None, None))
|
||||||
|
|
||||||
|
bytes_ = self.dynamic_array(u8)
|
||||||
|
|
||||||
|
self.type_info_map.update({
|
||||||
|
'u8': TypeInfo('u8', WasmTypeInt32, 'i32.load8_u', 'i32.store8', 1, False),
|
||||||
|
'u16': TypeInfo('u16', WasmTypeInt32, 'i32.load16_u', 'i32.store16', 2, False),
|
||||||
|
'u32': TypeInfo('u32', WasmTypeInt32, 'i32.load', 'i32.store', 4, False),
|
||||||
|
'u64': TypeInfo('u64', WasmTypeInt64, 'i64.load', 'i64.store', 8, False),
|
||||||
|
'i8': TypeInfo('i8', WasmTypeInt32, 'i32.load8_s', 'i32.store8', 1, True),
|
||||||
|
'i16': TypeInfo('i16', WasmTypeInt32, 'i32.load16_s', 'i32.store16', 2, True),
|
||||||
|
'i32': TypeInfo('i32', WasmTypeInt32, 'i32.load', 'i32.store', 4, True),
|
||||||
|
'i64': TypeInfo('i64', WasmTypeInt64, 'i64.load', 'i64.store', 8, True),
|
||||||
|
'f32': TypeInfo('f32', WasmTypeFloat32, 'f32.load', 'f32.store', 4, None),
|
||||||
|
'f64': TypeInfo('f64', WasmTypeFloat64, 'f64.load', 'f64.store', 8, None),
|
||||||
|
})
|
||||||
|
|
||||||
|
self.types.update({
|
||||||
|
'u8': u8,
|
||||||
|
'u16': u16,
|
||||||
|
'u32': u32,
|
||||||
|
'u64': u64,
|
||||||
|
'i8': i8,
|
||||||
|
'i16': i16,
|
||||||
|
'i32': i32,
|
||||||
|
'i64': i64,
|
||||||
|
'f32': f32,
|
||||||
|
'f64': f64,
|
||||||
|
'bytes': bytes_,
|
||||||
|
})
|
||||||
|
|
||||||
|
tc_list = [
|
||||||
|
bits,
|
||||||
|
eq, ord,
|
||||||
|
extendable, promotable,
|
||||||
|
convertable, reinterpretable,
|
||||||
|
natnum, intnum, fractional, floating,
|
||||||
|
integral,
|
||||||
|
foldable,
|
||||||
|
sized,
|
||||||
|
]
|
||||||
|
|
||||||
|
for tc in tc_list:
|
||||||
|
tc.load(self)
|
||||||
|
|
||||||
|
for tc in tc_list:
|
||||||
|
tc.wasm(self)
|
||||||
0
phasm/build/typeclasses/__init__.py
Normal file
0
phasm/build/typeclasses/__init__.py
Normal file
193
phasm/build/typeclasses/bits.py
Normal file
193
phasm/build/typeclasses/bits.py
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
"""
|
||||||
|
The Bits type class is defined for types that can be bit manipulated.
|
||||||
|
"""
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from ...type3.functions import make_typevar
|
||||||
|
from ...type3.routers import TypeVariableLookup
|
||||||
|
from ...type3.typeclasses import Type3Class
|
||||||
|
from ...wasmgenerator import Generator as WasmGenerator
|
||||||
|
from ..base import BuildBase
|
||||||
|
|
||||||
|
|
||||||
|
def load(build: BuildBase[Any]) -> None:
|
||||||
|
a = make_typevar('a')
|
||||||
|
u32 = build.types['u32']
|
||||||
|
|
||||||
|
Bits = Type3Class('Bits', (a, ), methods={
|
||||||
|
'shl': [a, u32, a], # Logical shift left
|
||||||
|
'shr': [a, u32, a], # Logical shift right
|
||||||
|
'rotl': [a, u32, a], # Rotate bits left
|
||||||
|
'rotr': [a, u32, a], # Rotate bits right
|
||||||
|
# FIXME: Do we want to expose clz, ctz, popcnt?
|
||||||
|
}, operators={
|
||||||
|
'&': [a, a, a], # Bit-wise and
|
||||||
|
'|': [a, a, a], # Bit-wise or
|
||||||
|
'^': [a, a, a], # Bit-wise xor
|
||||||
|
})
|
||||||
|
|
||||||
|
build.register_type_class(Bits)
|
||||||
|
|
||||||
|
def wasm_u8_logical_shift_left(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.shl()
|
||||||
|
g.i32.const(0xFF)
|
||||||
|
g.i32.and_()
|
||||||
|
|
||||||
|
def wasm_u16_logical_shift_left(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.shl()
|
||||||
|
g.i32.const(0xFFFF)
|
||||||
|
g.i32.and_()
|
||||||
|
|
||||||
|
def wasm_u32_logical_shift_left(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.shl()
|
||||||
|
|
||||||
|
def wasm_u64_logical_shift_left(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i64.extend_i32_u()
|
||||||
|
g.i64.shl()
|
||||||
|
|
||||||
|
def wasm_u8_logical_shift_right(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.shr_u()
|
||||||
|
|
||||||
|
def wasm_u16_logical_shift_right(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.shr_u()
|
||||||
|
|
||||||
|
def wasm_u32_logical_shift_right(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.shr_u()
|
||||||
|
|
||||||
|
def wasm_u64_logical_shift_right(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i64.extend_i32_u()
|
||||||
|
g.i64.shr_u()
|
||||||
|
|
||||||
|
def wasm_u8_rotate_left(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.call('stdlib.types.__u8_rotl__')
|
||||||
|
|
||||||
|
def wasm_u16_rotate_left(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.call('stdlib.types.__u16_rotl__')
|
||||||
|
|
||||||
|
def wasm_u32_rotate_left(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.rotl()
|
||||||
|
|
||||||
|
def wasm_u64_rotate_left(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i64.extend_i32_u()
|
||||||
|
g.i64.rotl()
|
||||||
|
|
||||||
|
def wasm_u8_rotate_right(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.call('stdlib.types.__u8_rotr__')
|
||||||
|
|
||||||
|
def wasm_u16_rotate_right(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.call('stdlib.types.__u16_rotr__')
|
||||||
|
|
||||||
|
def wasm_u32_rotate_right(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.rotr()
|
||||||
|
|
||||||
|
def wasm_u64_rotate_right(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i64.extend_i32_u()
|
||||||
|
g.i64.rotr()
|
||||||
|
|
||||||
|
def wasm_u8_bitwise_and(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.and_()
|
||||||
|
|
||||||
|
def wasm_u16_bitwise_and(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.and_()
|
||||||
|
|
||||||
|
def wasm_u32_bitwise_and(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.and_()
|
||||||
|
|
||||||
|
def wasm_u64_bitwise_and(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i64.and_()
|
||||||
|
|
||||||
|
def wasm_u8_bitwise_or(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.or_()
|
||||||
|
|
||||||
|
def wasm_u16_bitwise_or(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.or_()
|
||||||
|
|
||||||
|
def wasm_u32_bitwise_or(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.or_()
|
||||||
|
|
||||||
|
def wasm_u64_bitwise_or(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i64.or_()
|
||||||
|
|
||||||
|
def wasm_u8_bitwise_xor(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.xor()
|
||||||
|
|
||||||
|
def wasm_u16_bitwise_xor(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.xor()
|
||||||
|
|
||||||
|
def wasm_u32_bitwise_xor(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.xor()
|
||||||
|
|
||||||
|
def wasm_u64_bitwise_xor(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i64.xor()
|
||||||
|
|
||||||
|
def wasm(build: BuildBase[WasmGenerator]) -> None:
|
||||||
|
Bits = build.type_classes['Bits']
|
||||||
|
|
||||||
|
build.instance_type_class(Bits, build.types['u8'], methods={
|
||||||
|
'shl': wasm_u8_logical_shift_left,
|
||||||
|
'shr': wasm_u8_logical_shift_right,
|
||||||
|
'rotl': wasm_u8_rotate_left,
|
||||||
|
'rotr': wasm_u8_rotate_right,
|
||||||
|
}, operators={
|
||||||
|
'&': wasm_u8_bitwise_and,
|
||||||
|
'|': wasm_u8_bitwise_or,
|
||||||
|
'^': wasm_u8_bitwise_xor,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Bits, build.types['u16'], methods={
|
||||||
|
'shl': wasm_u16_logical_shift_left,
|
||||||
|
'shr': wasm_u16_logical_shift_right,
|
||||||
|
'rotl': wasm_u16_rotate_left,
|
||||||
|
'rotr': wasm_u16_rotate_right,
|
||||||
|
}, operators={
|
||||||
|
'&': wasm_u16_bitwise_and,
|
||||||
|
'|': wasm_u16_bitwise_or,
|
||||||
|
'^': wasm_u16_bitwise_xor,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Bits, build.types['u32'], methods={
|
||||||
|
'shl': wasm_u32_logical_shift_left,
|
||||||
|
'shr': wasm_u32_logical_shift_right,
|
||||||
|
'rotl': wasm_u32_rotate_left,
|
||||||
|
'rotr': wasm_u32_rotate_right,
|
||||||
|
}, operators={
|
||||||
|
'&': wasm_u32_bitwise_and,
|
||||||
|
'|': wasm_u32_bitwise_or,
|
||||||
|
'^': wasm_u32_bitwise_xor,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Bits, build.types['u64'], methods={
|
||||||
|
'shl': wasm_u64_logical_shift_left,
|
||||||
|
'shr': wasm_u64_logical_shift_right,
|
||||||
|
'rotl': wasm_u64_rotate_left,
|
||||||
|
'rotr': wasm_u64_rotate_right,
|
||||||
|
}, operators={
|
||||||
|
'&': wasm_u64_bitwise_and,
|
||||||
|
'|': wasm_u64_bitwise_or,
|
||||||
|
'^': wasm_u64_bitwise_xor,
|
||||||
|
})
|
||||||
124
phasm/build/typeclasses/convertable.py
Normal file
124
phasm/build/typeclasses/convertable.py
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
"""
|
||||||
|
The Convertable type class is defined for when a value from one type can be
|
||||||
|
converted to another type - but there's no real guarantee about precision or
|
||||||
|
value loss.
|
||||||
|
"""
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from ...type3.functions import make_typevar
|
||||||
|
from ...type3.routers import TypeVariableLookup
|
||||||
|
from ...type3.typeclasses import Type3Class
|
||||||
|
from ...wasmgenerator import Generator as WasmGenerator
|
||||||
|
from ..base import BuildBase
|
||||||
|
|
||||||
|
|
||||||
|
def load(build: BuildBase[Any]) -> None:
|
||||||
|
a = make_typevar('a')
|
||||||
|
b = make_typevar('b')
|
||||||
|
|
||||||
|
Convertable = Type3Class('Convertable', (a, b, ), methods={
|
||||||
|
'convert': [a, b],
|
||||||
|
'truncate': [b, a], # To prevent name clas with Fractional
|
||||||
|
}, operators={})
|
||||||
|
|
||||||
|
build.register_type_class(Convertable)
|
||||||
|
|
||||||
|
def wasm_u32_f32_convert(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f32.convert_i32_u()
|
||||||
|
|
||||||
|
def wasm_u32_f64_convert(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f64.convert_i32_u()
|
||||||
|
|
||||||
|
def wasm_u64_f32_convert(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f32.convert_i64_u()
|
||||||
|
|
||||||
|
def wasm_u64_f64_convert(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f64.convert_i64_u()
|
||||||
|
|
||||||
|
def wasm_i32_f32_convert(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f32.convert_i32_s()
|
||||||
|
|
||||||
|
def wasm_i32_f64_convert(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f64.convert_i32_s()
|
||||||
|
|
||||||
|
def wasm_i64_f32_convert(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f32.convert_i64_s()
|
||||||
|
|
||||||
|
def wasm_i64_f64_convert(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f64.convert_i64_s()
|
||||||
|
|
||||||
|
def wasm_u32_f32_truncate(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.trunc_f32_u()
|
||||||
|
|
||||||
|
def wasm_u32_f64_truncate(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.trunc_f64_u()
|
||||||
|
|
||||||
|
def wasm_u64_f32_truncate(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i64.trunc_f32_u()
|
||||||
|
|
||||||
|
def wasm_u64_f64_truncate(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i64.trunc_f64_u()
|
||||||
|
|
||||||
|
def wasm_i32_f32_truncate(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.trunc_f32_s()
|
||||||
|
|
||||||
|
def wasm_i32_f64_truncate(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.trunc_f64_s()
|
||||||
|
|
||||||
|
def wasm_i64_f32_truncate(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i64.trunc_f32_s()
|
||||||
|
|
||||||
|
def wasm_i64_f64_truncate(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i64.trunc_f64_s()
|
||||||
|
|
||||||
|
def wasm(build: BuildBase[WasmGenerator]) -> None:
|
||||||
|
Convertable = build.type_classes['Convertable']
|
||||||
|
|
||||||
|
build.instance_type_class(Convertable, build.types['u32'], build.types['f32'], methods={
|
||||||
|
'convert': wasm_u32_f32_convert,
|
||||||
|
'truncate': wasm_u32_f32_truncate,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Convertable, build.types['u32'], build.types['f64'], methods={
|
||||||
|
'convert': wasm_u32_f64_convert,
|
||||||
|
'truncate': wasm_u32_f64_truncate,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Convertable, build.types['u64'], build.types['f32'], methods={
|
||||||
|
'convert': wasm_u64_f32_convert,
|
||||||
|
'truncate': wasm_u64_f32_truncate,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Convertable, build.types['u64'], build.types['f64'], methods={
|
||||||
|
'convert': wasm_u64_f64_convert,
|
||||||
|
'truncate': wasm_u64_f64_truncate,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Convertable, build.types['i32'], build.types['f32'], methods={
|
||||||
|
'convert': wasm_i32_f32_convert,
|
||||||
|
'truncate': wasm_i32_f32_truncate,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Convertable, build.types['i32'], build.types['f64'], methods={
|
||||||
|
'convert': wasm_i32_f64_convert,
|
||||||
|
'truncate': wasm_i32_f64_truncate,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Convertable, build.types['i64'], build.types['f32'], methods={
|
||||||
|
'convert': wasm_i64_f32_convert,
|
||||||
|
'truncate': wasm_i64_f32_truncate,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Convertable, build.types['i64'], build.types['f64'], methods={
|
||||||
|
'convert': wasm_i64_f64_convert,
|
||||||
|
'truncate': wasm_i64_f64_truncate,
|
||||||
|
})
|
||||||
146
phasm/build/typeclasses/eq.py
Normal file
146
phasm/build/typeclasses/eq.py
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
"""
|
||||||
|
The Eq type class is defined for types that can be compered based on equality.
|
||||||
|
"""
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from ...type3.functions import make_typevar
|
||||||
|
from ...type3.routers import TypeVariableLookup
|
||||||
|
from ...type3.typeclasses import Type3Class
|
||||||
|
from ...wasmgenerator import Generator as WasmGenerator
|
||||||
|
from ..base import BuildBase
|
||||||
|
|
||||||
|
|
||||||
|
def load(build: BuildBase[Any]) -> None:
|
||||||
|
a = make_typevar('a')
|
||||||
|
|
||||||
|
Eq = Type3Class('Eq', (a, ), methods={}, operators={
|
||||||
|
'==': [a, a, build.bool_],
|
||||||
|
'!=': [a, a, build.bool_],
|
||||||
|
# FIXME: Do we want to expose 'eqz'? Or is that a compiler optimization?
|
||||||
|
})
|
||||||
|
|
||||||
|
build.register_type_class(Eq)
|
||||||
|
|
||||||
|
def wasm_u8_equals(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.eq()
|
||||||
|
|
||||||
|
def wasm_u16_equals(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.eq()
|
||||||
|
|
||||||
|
def wasm_u32_equals(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.eq()
|
||||||
|
|
||||||
|
def wasm_u64_equals(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i64.eq()
|
||||||
|
|
||||||
|
def wasm_i8_equals(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.eq()
|
||||||
|
|
||||||
|
def wasm_i16_equals(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.eq()
|
||||||
|
|
||||||
|
def wasm_i32_equals(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.eq()
|
||||||
|
|
||||||
|
def wasm_i64_equals(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i64.eq()
|
||||||
|
|
||||||
|
def wasm_f32_equals(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f32.eq()
|
||||||
|
|
||||||
|
def wasm_f64_equals(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f64.eq()
|
||||||
|
|
||||||
|
def wasm_u8_not_equals(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.ne()
|
||||||
|
|
||||||
|
def wasm_u16_not_equals(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.ne()
|
||||||
|
|
||||||
|
def wasm_u32_not_equals(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.ne()
|
||||||
|
|
||||||
|
def wasm_u64_not_equals(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i64.ne()
|
||||||
|
|
||||||
|
def wasm_i8_not_equals(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.ne()
|
||||||
|
|
||||||
|
def wasm_i16_not_equals(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.ne()
|
||||||
|
|
||||||
|
def wasm_i32_not_equals(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.ne()
|
||||||
|
|
||||||
|
def wasm_i64_not_equals(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i64.ne()
|
||||||
|
|
||||||
|
def wasm_f32_not_equals(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f32.ne()
|
||||||
|
|
||||||
|
def wasm_f64_not_equals(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f64.ne()
|
||||||
|
|
||||||
|
def wasm(build: BuildBase[WasmGenerator]) -> None:
|
||||||
|
Eq = build.type_classes['Eq']
|
||||||
|
|
||||||
|
build.instance_type_class(Eq, build.types['u8'], operators={
|
||||||
|
'==': wasm_u8_equals,
|
||||||
|
'!=': wasm_u8_not_equals,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Eq, build.types['u16'], operators={
|
||||||
|
'==': wasm_u16_equals,
|
||||||
|
'!=': wasm_u16_not_equals,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Eq, build.types['u32'], operators={
|
||||||
|
'==': wasm_u32_equals,
|
||||||
|
'!=': wasm_u32_not_equals,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Eq, build.types['u64'], operators={
|
||||||
|
'==': wasm_u64_equals,
|
||||||
|
'!=': wasm_u64_not_equals,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Eq, build.types['i8'], operators={
|
||||||
|
'==': wasm_i8_equals,
|
||||||
|
'!=': wasm_i8_not_equals,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Eq, build.types['i16'], operators={
|
||||||
|
'==': wasm_i16_equals,
|
||||||
|
'!=': wasm_i16_not_equals,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Eq, build.types['i32'], operators={
|
||||||
|
'==': wasm_i32_equals,
|
||||||
|
'!=': wasm_i32_not_equals,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Eq, build.types['i64'], operators={
|
||||||
|
'==': wasm_i64_equals,
|
||||||
|
'!=': wasm_i64_not_equals,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Eq, build.types['f32'], operators={
|
||||||
|
'==': wasm_f32_equals,
|
||||||
|
'!=': wasm_f32_not_equals,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Eq, build.types['f64'], operators={
|
||||||
|
'==': wasm_f64_equals,
|
||||||
|
'!=': wasm_f64_not_equals,
|
||||||
|
})
|
||||||
197
phasm/build/typeclasses/extendable.py
Normal file
197
phasm/build/typeclasses/extendable.py
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
"""
|
||||||
|
The Extendable type class is defined for types that can safely be extended to a type
|
||||||
|
that can hold strictly more values. Going back will result in some values being lost.
|
||||||
|
"""
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from ...type3.functions import make_typevar
|
||||||
|
from ...type3.routers import TypeVariableLookup
|
||||||
|
from ...type3.typeclasses import Type3Class
|
||||||
|
from ...wasmgenerator import Generator as WasmGenerator
|
||||||
|
from ..base import BuildBase
|
||||||
|
|
||||||
|
|
||||||
|
def load(build: BuildBase[Any]) -> None:
|
||||||
|
a = make_typevar('a')
|
||||||
|
b = make_typevar('b')
|
||||||
|
|
||||||
|
Extendable = Type3Class('Extendable', (a, b, ), methods={
|
||||||
|
'extend': [a, b],
|
||||||
|
'wrap': [b, a],
|
||||||
|
}, operators={})
|
||||||
|
|
||||||
|
build.register_type_class(Extendable)
|
||||||
|
|
||||||
|
def wasm_u8_u16_extend(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
# No-op
|
||||||
|
# u8 and u16 are both stored as u32
|
||||||
|
pass
|
||||||
|
|
||||||
|
def wasm_u8_u32_extend(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
# No-op
|
||||||
|
# u8 is already stored as u32
|
||||||
|
pass
|
||||||
|
|
||||||
|
def wasm_u8_u64_extend(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i64.extend_i32_u()
|
||||||
|
|
||||||
|
def wasm_u16_u32_extend(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
# No-op
|
||||||
|
# u16 is already stored as u32
|
||||||
|
pass
|
||||||
|
|
||||||
|
def wasm_u16_u64_extend(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i64.extend_i32_u()
|
||||||
|
|
||||||
|
def wasm_u32_u64_extend(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i64.extend_i32_u()
|
||||||
|
|
||||||
|
def wasm_i8_i16_extend(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
# No-op
|
||||||
|
# i8 is already stored as i32
|
||||||
|
pass
|
||||||
|
|
||||||
|
def wasm_i8_i32_extend(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
# No-op
|
||||||
|
# i8 is already stored as i32
|
||||||
|
pass
|
||||||
|
|
||||||
|
def wasm_i8_i64_extend(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i64.extend_i32_s()
|
||||||
|
|
||||||
|
def wasm_i16_i32_extend(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
# No-op
|
||||||
|
# i16 is already stored as i32
|
||||||
|
pass
|
||||||
|
|
||||||
|
def wasm_i16_i64_extend(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i64.extend_i32_s()
|
||||||
|
|
||||||
|
def wasm_i32_i64_extend(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i64.extend_i32_s()
|
||||||
|
|
||||||
|
def wasm_u8_u16_wrap(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.const(0xFF)
|
||||||
|
g.i32.and_()
|
||||||
|
|
||||||
|
def wasm_u8_u32_wrap(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.const(0xFF)
|
||||||
|
g.i32.and_()
|
||||||
|
|
||||||
|
def wasm_u8_u64_wrap(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.wrap_i64()
|
||||||
|
g.i32.const(0xFF)
|
||||||
|
g.i32.and_()
|
||||||
|
|
||||||
|
def wasm_u16_u32_wrap(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.const(0xFFFF)
|
||||||
|
g.i32.and_()
|
||||||
|
|
||||||
|
def wasm_u16_u64_wrap(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.wrap_i64()
|
||||||
|
g.i32.const(0xFFFF)
|
||||||
|
g.i32.and_()
|
||||||
|
|
||||||
|
def wasm_u32_u64_wrap(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.wrap_i64()
|
||||||
|
|
||||||
|
def wasm_i8_i16_wrap(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.const(0xFF)
|
||||||
|
g.i32.and_()
|
||||||
|
|
||||||
|
def wasm_i8_i32_wrap(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.const(0xFF)
|
||||||
|
g.i32.and_()
|
||||||
|
|
||||||
|
def wasm_i8_i64_wrap(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.wrap_i64()
|
||||||
|
g.i32.const(0xFF)
|
||||||
|
g.i32.and_()
|
||||||
|
|
||||||
|
def wasm_i16_i32_wrap(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.const(0xFFFF)
|
||||||
|
g.i32.and_()
|
||||||
|
|
||||||
|
def wasm_i16_i64_wrap(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.wrap_i64()
|
||||||
|
g.i32.const(0xFFFF)
|
||||||
|
g.i32.and_()
|
||||||
|
|
||||||
|
def wasm_i32_i64_wrap(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.wrap_i64()
|
||||||
|
|
||||||
|
def wasm(build: BuildBase[WasmGenerator]) -> None:
|
||||||
|
Extendable = build.type_classes['Extendable']
|
||||||
|
|
||||||
|
build.instance_type_class(Extendable, build.types['u8'], build.types['u16'], methods={
|
||||||
|
'extend': wasm_u8_u16_extend,
|
||||||
|
'wrap': wasm_u8_u16_wrap,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Extendable, build.types['u8'], build.types['u32'], methods={
|
||||||
|
'extend': wasm_u8_u32_extend,
|
||||||
|
'wrap': wasm_u8_u32_wrap,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Extendable, build.types['u8'], build.types['u64'], methods={
|
||||||
|
'extend': wasm_u8_u64_extend,
|
||||||
|
'wrap': wasm_u8_u64_wrap,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Extendable, build.types['u16'], build.types['u32'], methods={
|
||||||
|
'extend': wasm_u16_u32_extend,
|
||||||
|
'wrap': wasm_u16_u32_wrap,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Extendable, build.types['u16'], build.types['u64'], methods={
|
||||||
|
'extend': wasm_u16_u64_extend,
|
||||||
|
'wrap': wasm_u16_u64_wrap,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Extendable, build.types['u32'], build.types['u64'], methods={
|
||||||
|
'extend': wasm_u32_u64_extend,
|
||||||
|
'wrap': wasm_u32_u64_wrap,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Extendable, build.types['i8'], build.types['i16'], methods={
|
||||||
|
'extend': wasm_i8_i16_extend,
|
||||||
|
'wrap': wasm_i8_i16_wrap,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Extendable, build.types['i8'], build.types['i32'], methods={
|
||||||
|
'extend': wasm_i8_i32_extend,
|
||||||
|
'wrap': wasm_i8_i32_wrap,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Extendable, build.types['i8'], build.types['i64'], methods={
|
||||||
|
'extend': wasm_i8_i64_extend,
|
||||||
|
'wrap': wasm_i8_i64_wrap,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Extendable, build.types['i16'], build.types['i32'], methods={
|
||||||
|
'extend': wasm_i16_i32_extend,
|
||||||
|
'wrap': wasm_i16_i32_wrap,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Extendable, build.types['i16'], build.types['i64'], methods={
|
||||||
|
'extend': wasm_i16_i64_extend,
|
||||||
|
'wrap': wasm_i16_i64_wrap,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Extendable, build.types['i32'], build.types['i64'], methods={
|
||||||
|
'extend': wasm_i32_i64_extend,
|
||||||
|
'wrap': wasm_i32_i64_wrap,
|
||||||
|
})
|
||||||
40
phasm/build/typeclasses/floating.py
Normal file
40
phasm/build/typeclasses/floating.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
"""
|
||||||
|
The Floating type class is defined for Real numbers.
|
||||||
|
"""
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from ...type3.functions import make_typevar
|
||||||
|
from ...type3.routers import TypeVariableLookup
|
||||||
|
from ...type3.typeclasses import Type3Class
|
||||||
|
from ...wasmgenerator import Generator as WasmGenerator
|
||||||
|
from ..base import BuildBase
|
||||||
|
|
||||||
|
|
||||||
|
def load(build: BuildBase[Any]) -> None:
|
||||||
|
a = make_typevar('a')
|
||||||
|
Fractional = build.type_classes['Fractional']
|
||||||
|
|
||||||
|
Floating = Type3Class('Floating', (a, ), methods={
|
||||||
|
'sqrt': [a, a],
|
||||||
|
}, operators={}, inherited_classes=[Fractional])
|
||||||
|
# FIXME: Do we want to expose copysign?
|
||||||
|
|
||||||
|
build.register_type_class(Floating)
|
||||||
|
|
||||||
|
def wasm_f32_sqrt(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.add_statement('f32.sqrt')
|
||||||
|
|
||||||
|
def wasm_f64_sqrt(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.add_statement('f64.sqrt')
|
||||||
|
|
||||||
|
def wasm(build: BuildBase[WasmGenerator]) -> None:
|
||||||
|
Floating = build.type_classes['Floating']
|
||||||
|
|
||||||
|
build.instance_type_class(Floating, build.types['f32'], methods={
|
||||||
|
'sqrt': wasm_f32_sqrt,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Floating, build.types['f64'], methods={
|
||||||
|
'sqrt': wasm_f64_sqrt,
|
||||||
|
})
|
||||||
663
phasm/build/typeclasses/foldable.py
Normal file
663
phasm/build/typeclasses/foldable.py
Normal file
@ -0,0 +1,663 @@
|
|||||||
|
"""
|
||||||
|
The Foldable type class is defined for when a value iterated over.
|
||||||
|
"""
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from ...type3.functions import (
|
||||||
|
Constraint_TypeClassInstanceExists,
|
||||||
|
TypeConstructorVariable,
|
||||||
|
make_typevar,
|
||||||
|
)
|
||||||
|
from ...type3.routers import TypeVariableLookup
|
||||||
|
from ...type3.typeclasses import Type3Class
|
||||||
|
from ...type3.types import IntType3, Type3
|
||||||
|
from ...wasmgenerator import Generator as WasmGenerator
|
||||||
|
from ..base import BuildBase
|
||||||
|
|
||||||
|
|
||||||
|
def load(build: BuildBase[Any]) -> None:
|
||||||
|
a = make_typevar('a')
|
||||||
|
b = make_typevar('b')
|
||||||
|
t = TypeConstructorVariable('t')
|
||||||
|
|
||||||
|
NatNum = build.type_classes['NatNum']
|
||||||
|
|
||||||
|
Foldable = Type3Class('Foldable', (t, ), methods={
|
||||||
|
'sum': [t(a), a],
|
||||||
|
'foldl': [[b, a, b], b, t(a), b],
|
||||||
|
'foldr': [[a, b, b], b, t(a), b],
|
||||||
|
}, operators={}, additional_context={
|
||||||
|
'sum': [Constraint_TypeClassInstanceExists(NatNum, (a, ))],
|
||||||
|
})
|
||||||
|
|
||||||
|
build.register_type_class(Foldable)
|
||||||
|
|
||||||
|
class FoldableCodeGenerator:
|
||||||
|
def __init__(self, build: BuildBase[WasmGenerator]) -> None:
|
||||||
|
self.build = build
|
||||||
|
|
||||||
|
def wasm_dynamic_array_sum(self, g: WasmGenerator, tvl: TypeVariableLookup) -> None:
|
||||||
|
tv_map, tc_map = tvl
|
||||||
|
|
||||||
|
tvn_map = {
|
||||||
|
x.name: y
|
||||||
|
for x, y in tv_map.items()
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_type = tvn_map['a']
|
||||||
|
|
||||||
|
assert isinstance(sa_type, Type3)
|
||||||
|
|
||||||
|
ptr_type_info = self.build.type_info_map['ptr']
|
||||||
|
sa_type_info = self.build.type_info_map.get(sa_type.name)
|
||||||
|
if sa_type_info is None:
|
||||||
|
sa_type_info = ptr_type_info
|
||||||
|
|
||||||
|
NatNum = self.build.type_classes['NatNum']
|
||||||
|
natnum_router = self.build.type_class_instance_methods[NatNum.operators['+']]
|
||||||
|
|
||||||
|
tv_a = make_typevar('a')
|
||||||
|
|
||||||
|
# Definitions
|
||||||
|
sum_adr = g.temp_var_t(ptr_type_info.wasm_type, 'sum_adr')
|
||||||
|
sum_stop = g.temp_var_t(ptr_type_info.wasm_type, 'sum_stop')
|
||||||
|
|
||||||
|
with g.block(params=['i32'], result=sa_type_info.wasm_type):
|
||||||
|
# Stack: [adr] -> [] ; sum_adr=ard
|
||||||
|
g.local.set(sum_adr)
|
||||||
|
|
||||||
|
# Stack: [] ; sum_stop = adr + 4 + len(adr) * sa_type_info.alloc_size
|
||||||
|
g.nop(comment='Calculate address at which to stop looping')
|
||||||
|
g.local.get(sum_adr)
|
||||||
|
g.i32.load()
|
||||||
|
g.i32.const(sa_type_info.alloc_size)
|
||||||
|
g.i32.mul()
|
||||||
|
g.local.get(sum_adr)
|
||||||
|
g.i32.add()
|
||||||
|
|
||||||
|
g.i32.const(4)
|
||||||
|
g.i32.add()
|
||||||
|
g.local.set(sum_stop)
|
||||||
|
|
||||||
|
# Stack: [] -> [sum] ; sum_adr += 4
|
||||||
|
g.nop(comment='Get the first array value as starting point')
|
||||||
|
g.local.get(sum_adr)
|
||||||
|
g.i32.const(4)
|
||||||
|
g.i32.add()
|
||||||
|
g.local.tee(sum_adr)
|
||||||
|
g.add_statement(sa_type_info.wasm_load_func)
|
||||||
|
|
||||||
|
# Since we did the first one, increase adr
|
||||||
|
# Stack: [sum] -> [sum] ; sum_adr = sum_adr + sa_type_info.alloc_size
|
||||||
|
g.local.get(sum_adr)
|
||||||
|
g.i32.const(sa_type_info.alloc_size)
|
||||||
|
g.i32.add()
|
||||||
|
g.local.set(sum_adr)
|
||||||
|
|
||||||
|
g.local.get(sum_adr)
|
||||||
|
g.local.get(sum_stop)
|
||||||
|
g.i32.lt_u()
|
||||||
|
with g.if_(params=[sa_type_info.wasm_type], result=sa_type_info.wasm_type):
|
||||||
|
with g.loop(params=[sa_type_info.wasm_type], result=sa_type_info.wasm_type):
|
||||||
|
# sum = sum + *adr
|
||||||
|
# Stack: [sum] -> [sum + *adr]
|
||||||
|
g.nop(comment='Add array value')
|
||||||
|
g.local.get(sum_adr)
|
||||||
|
g.add_statement(sa_type_info.wasm_load_func)
|
||||||
|
natnum_router(g, {tv_a: sa_type})
|
||||||
|
|
||||||
|
# adr = adr + sa_type_info.alloc_size
|
||||||
|
# Stack: [sum] -> [sum]
|
||||||
|
g.nop(comment='Calculate address of the next value')
|
||||||
|
g.local.get(sum_adr)
|
||||||
|
g.i32.const(sa_type_info.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
|
||||||
|
|
||||||
|
# End result: [sum]
|
||||||
|
|
||||||
|
def wasm_static_array_sum(self, g: WasmGenerator, tvl: TypeVariableLookup) -> None:
|
||||||
|
tv_map, tc_map = tvl
|
||||||
|
|
||||||
|
tvn_map = {
|
||||||
|
x.name: y
|
||||||
|
for x, y in tv_map.items()
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_type = tvn_map['a']
|
||||||
|
sa_len = tvn_map['a*']
|
||||||
|
|
||||||
|
assert isinstance(sa_type, Type3)
|
||||||
|
assert isinstance(sa_len, IntType3)
|
||||||
|
|
||||||
|
if sa_len.value < 1:
|
||||||
|
raise NotImplementedError('Default value in case sum is empty')
|
||||||
|
|
||||||
|
ptr_type_info = self.build.type_info_map['ptr']
|
||||||
|
sa_type_info = self.build.type_info_map.get(sa_type.name)
|
||||||
|
if sa_type_info is None:
|
||||||
|
sa_type_info = ptr_type_info
|
||||||
|
|
||||||
|
NatNum = self.build.type_classes['NatNum']
|
||||||
|
natnum_router = self.build.type_class_instance_methods[NatNum.operators['+']]
|
||||||
|
|
||||||
|
tv_a = make_typevar('a')
|
||||||
|
|
||||||
|
# Definitions
|
||||||
|
sum_adr = g.temp_var_t(ptr_type_info.wasm_type, 'sum_adr')
|
||||||
|
sum_stop = g.temp_var_t(ptr_type_info.wasm_type, '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_info.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_info.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(sa_type_info.wasm_load_func)
|
||||||
|
|
||||||
|
# Since we did the first one, increase adr
|
||||||
|
# adr = adr + sa_type_info.alloc_size
|
||||||
|
# Stack: [sum] -> [sum]
|
||||||
|
g.local.get(sum_adr)
|
||||||
|
g.i32.const(sa_type_info.alloc_size)
|
||||||
|
g.i32.add()
|
||||||
|
g.local.set(sum_adr)
|
||||||
|
|
||||||
|
if sa_len.value > 1:
|
||||||
|
with g.loop(params=[sa_type_info.wasm_type], result=sa_type_info.wasm_type):
|
||||||
|
# sum = sum + *adr
|
||||||
|
# Stack: [sum] -> [sum + *adr]
|
||||||
|
g.nop(comment='Add array value')
|
||||||
|
g.local.get(sum_adr)
|
||||||
|
g.add_statement(sa_type_info.wasm_load_func)
|
||||||
|
natnum_router(g, {tv_a: sa_type})
|
||||||
|
|
||||||
|
# adr = adr + sa_type_info.alloc_size
|
||||||
|
# Stack: [sum] -> [sum]
|
||||||
|
g.nop(comment='Calculate address of the next value')
|
||||||
|
g.local.get(sum_adr)
|
||||||
|
g.i32.const(sa_type_info.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]
|
||||||
|
|
||||||
|
def wasm_dynamic_array_foldl(self, g: WasmGenerator, tvl: TypeVariableLookup) -> None:
|
||||||
|
tv_map, tc_map = tvl
|
||||||
|
|
||||||
|
tvn_map = {
|
||||||
|
x.name: y
|
||||||
|
for x, y in tv_map.items()
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_type = tvn_map['a']
|
||||||
|
res_type = tvn_map['b']
|
||||||
|
|
||||||
|
assert isinstance(sa_type, Type3)
|
||||||
|
assert isinstance(res_type, Type3)
|
||||||
|
|
||||||
|
ptr_type_info = self.build.type_info_map['ptr']
|
||||||
|
u32_type_info = self.build.type_info_map['u32']
|
||||||
|
|
||||||
|
sa_type_info = self.build.type_info_map.get(sa_type.name)
|
||||||
|
if sa_type_info is None:
|
||||||
|
sa_type_info = ptr_type_info
|
||||||
|
res_type_info = self.build.type_info_map.get(res_type.name)
|
||||||
|
if res_type_info is None:
|
||||||
|
res_type_info = ptr_type_info
|
||||||
|
|
||||||
|
# Definitions
|
||||||
|
fold_adr = g.temp_var_t(ptr_type_info.wasm_type, 'fold_adr')
|
||||||
|
fold_stop = g.temp_var_t(ptr_type_info.wasm_type, 'fold_stop')
|
||||||
|
fold_init = g.temp_var_t(res_type_info.wasm_type, 'fold_init')
|
||||||
|
fold_func = g.temp_var_t(ptr_type_info.wasm_type, 'fold_func')
|
||||||
|
fold_len = g.temp_var_t(u32_type_info.wasm_type, 'fold_len')
|
||||||
|
|
||||||
|
with g.block(params=['i32', res_type_info.wasm_type, 'i32'], result=res_type_info.wasm_type, comment=f'foldl a={sa_type.name} b={res_type.name}'):
|
||||||
|
# Stack: [fn*, b, sa*] -> [fn*, b]
|
||||||
|
g.local.tee(fold_adr) # Store address, but also keep it for loading the length
|
||||||
|
g.i32.load() # Load the length
|
||||||
|
g.local.set(fold_len) # Store the length
|
||||||
|
|
||||||
|
# Stack: [fn*, b] -> [fn*]
|
||||||
|
g.local.set(fold_init)
|
||||||
|
# Stack: [fn*] -> []
|
||||||
|
g.local.set(fold_func)
|
||||||
|
|
||||||
|
# Stack: [] -> [b]
|
||||||
|
g.nop(comment='No applications if array is empty')
|
||||||
|
g.local.get(fold_init)
|
||||||
|
g.local.get(fold_len)
|
||||||
|
g.i32.eqz() # If the array is empty
|
||||||
|
g.br_if(0) # Then the base value is the result
|
||||||
|
|
||||||
|
# Stack: [b] -> [b] ; fold_adr=fold_adr + 4
|
||||||
|
g.nop(comment='Skip the header')
|
||||||
|
g.local.get(fold_adr)
|
||||||
|
g.i32.const(4)
|
||||||
|
g.i32.add()
|
||||||
|
g.local.set(fold_adr)
|
||||||
|
|
||||||
|
# Stack: [b] -> [b]
|
||||||
|
g.nop(comment='Apply the first function call')
|
||||||
|
g.local.get(fold_adr)
|
||||||
|
g.add_statement(sa_type_info.wasm_load_func)
|
||||||
|
g.local.get(fold_func)
|
||||||
|
g.call_indirect([res_type_info.wasm_type, sa_type_info.wasm_type], res_type_info.wasm_type)
|
||||||
|
|
||||||
|
# Stack: [b] -> [b]
|
||||||
|
g.nop(comment='No loop if there is only one item')
|
||||||
|
g.local.get(fold_len)
|
||||||
|
g.i32.const(1)
|
||||||
|
g.i32.eq()
|
||||||
|
g.br_if(0) # just one value, don't need to loop
|
||||||
|
|
||||||
|
# Stack: [b] -> [b] ; fold_stop=fold_adr + (sa_len.value * sa_type_info.alloc_size)
|
||||||
|
g.nop(comment='Calculate address at which to stop looping')
|
||||||
|
g.local.get(fold_adr)
|
||||||
|
g.local.get(fold_len)
|
||||||
|
g.i32.const(sa_type_info.alloc_size)
|
||||||
|
g.i32.mul()
|
||||||
|
g.i32.add()
|
||||||
|
g.local.set(fold_stop)
|
||||||
|
|
||||||
|
# Stack: [b] -> [b] ; fold_adr = fold_adr + sa_type_info.alloc_size
|
||||||
|
g.nop(comment='Calculate address of the next value')
|
||||||
|
g.local.get(fold_adr)
|
||||||
|
g.i32.const(sa_type_info.alloc_size)
|
||||||
|
g.i32.add()
|
||||||
|
g.local.set(fold_adr)
|
||||||
|
|
||||||
|
with g.loop(params=[res_type_info.wasm_type], result=res_type_info.wasm_type):
|
||||||
|
# Stack: [b] -> [b]
|
||||||
|
g.nop(comment='Apply function call')
|
||||||
|
g.local.get(fold_adr)
|
||||||
|
g.add_statement(sa_type_info.wasm_load_func)
|
||||||
|
g.local.get(fold_func)
|
||||||
|
g.call_indirect([res_type_info.wasm_type, sa_type_info.wasm_type], res_type_info.wasm_type)
|
||||||
|
|
||||||
|
# Stack: [b] -> [b] ; fold_adr = fold_adr + sa_type_info.alloc_size
|
||||||
|
g.nop(comment='Calculate address of the next value')
|
||||||
|
g.local.get(fold_adr)
|
||||||
|
g.i32.const(sa_type_info.alloc_size)
|
||||||
|
g.i32.add()
|
||||||
|
g.local.tee(fold_adr)
|
||||||
|
|
||||||
|
# loop if adr > stop
|
||||||
|
# Stack: [b] -> [b]
|
||||||
|
g.nop(comment='Check if address exceeds array bounds')
|
||||||
|
g.local.get(fold_stop)
|
||||||
|
g.i32.lt_u()
|
||||||
|
g.br_if(0)
|
||||||
|
|
||||||
|
# Stack: [b]
|
||||||
|
|
||||||
|
def wasm_static_array_foldl(self, g: WasmGenerator, tvl: TypeVariableLookup) -> None:
|
||||||
|
tv_map, tc_map = tvl
|
||||||
|
|
||||||
|
tvn_map = {
|
||||||
|
x.name: y
|
||||||
|
for x, y in tv_map.items()
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_type = tvn_map['a']
|
||||||
|
sa_len = tvn_map['a*']
|
||||||
|
res_type = tvn_map['b']
|
||||||
|
|
||||||
|
assert isinstance(sa_type, Type3)
|
||||||
|
assert isinstance(sa_len, IntType3)
|
||||||
|
assert isinstance(res_type, Type3)
|
||||||
|
|
||||||
|
ptr_type_info = self.build.type_info_map['ptr']
|
||||||
|
|
||||||
|
sa_type_info = self.build.type_info_map.get(sa_type.name)
|
||||||
|
if sa_type_info is None:
|
||||||
|
sa_type_info = ptr_type_info
|
||||||
|
res_type_info = self.build.type_info_map.get(res_type.name)
|
||||||
|
if res_type_info is None:
|
||||||
|
res_type_info = ptr_type_info
|
||||||
|
|
||||||
|
# Definitions
|
||||||
|
fold_adr = g.temp_var_t(ptr_type_info.wasm_type, 'fold_adr')
|
||||||
|
fold_stop = g.temp_var_t(ptr_type_info.wasm_type, 'fold_stop')
|
||||||
|
fold_init = g.temp_var_t(res_type_info.wasm_type, 'fold_init')
|
||||||
|
fold_func = g.temp_var_t(ptr_type_info.wasm_type, 'fold_func')
|
||||||
|
|
||||||
|
with g.block(params=['i32', res_type_info.wasm_type, 'i32'], result=res_type_info.wasm_type, comment=f'foldl a={sa_type.name} a*={sa_len.value} b={res_type.name}'):
|
||||||
|
# Stack: [fn*, b, sa*] -> [fn*, b]
|
||||||
|
g.local.set(fold_adr)
|
||||||
|
# Stack: [fn*, b] -> [fn*]
|
||||||
|
g.local.set(fold_init)
|
||||||
|
# Stack: [fn*] -> []
|
||||||
|
g.local.set(fold_func)
|
||||||
|
|
||||||
|
if sa_len.value < 1:
|
||||||
|
g.local.get(fold_init)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Stack: [] -> [b]
|
||||||
|
g.nop(comment='Apply the first function call')
|
||||||
|
g.local.get(fold_init)
|
||||||
|
g.local.get(fold_adr)
|
||||||
|
g.add_statement(sa_type_info.wasm_load_func)
|
||||||
|
g.local.get(fold_func)
|
||||||
|
g.call_indirect([res_type_info.wasm_type, sa_type_info.wasm_type], res_type_info.wasm_type)
|
||||||
|
|
||||||
|
if sa_len.value > 1:
|
||||||
|
# Stack: [b] -> [b] ; fold_stop=fold_adr + (sa_len.value * sa_type_info.alloc_size)
|
||||||
|
g.nop(comment='Calculate address at which to stop looping')
|
||||||
|
g.local.get(fold_adr)
|
||||||
|
g.i32.const(sa_len.value * sa_type_info.alloc_size)
|
||||||
|
g.i32.add()
|
||||||
|
g.local.set(fold_stop)
|
||||||
|
|
||||||
|
# Stack: [b] -> [b] ; fold_adr = fold_adr + sa_type_info.alloc_size
|
||||||
|
g.nop(comment='Calculate address of the next value')
|
||||||
|
g.local.get(fold_adr)
|
||||||
|
g.i32.const(sa_type_info.alloc_size)
|
||||||
|
g.i32.add()
|
||||||
|
g.local.set(fold_adr)
|
||||||
|
|
||||||
|
with g.loop(params=[res_type_info.wasm_type], result=res_type_info.wasm_type):
|
||||||
|
# Stack: [b] -> [b]
|
||||||
|
g.nop(comment='Apply function call')
|
||||||
|
g.local.get(fold_adr)
|
||||||
|
g.add_statement(sa_type_info.wasm_load_func)
|
||||||
|
g.local.get(fold_func)
|
||||||
|
g.call_indirect([res_type_info.wasm_type, sa_type_info.wasm_type], res_type_info.wasm_type)
|
||||||
|
|
||||||
|
# Stack: [b] -> [b] ; fold_adr = fold_adr + sa_type_info.alloc_size
|
||||||
|
g.nop(comment='Calculate address of the next value')
|
||||||
|
g.local.get(fold_adr)
|
||||||
|
g.i32.const(sa_type_info.alloc_size)
|
||||||
|
g.i32.add()
|
||||||
|
g.local.tee(fold_adr)
|
||||||
|
|
||||||
|
# loop if adr > stop
|
||||||
|
# Stack: [b] -> [b]
|
||||||
|
g.nop(comment='Check if address exceeds array bounds')
|
||||||
|
g.local.get(fold_stop)
|
||||||
|
g.i32.lt_u()
|
||||||
|
g.br_if(0)
|
||||||
|
# else: just one value, don't need to loop
|
||||||
|
|
||||||
|
# Stack: [b]
|
||||||
|
|
||||||
|
def wasm_dynamic_array_foldr(self, g: WasmGenerator, tvl: TypeVariableLookup) -> None:
|
||||||
|
tv_map, tc_map = tvl
|
||||||
|
|
||||||
|
tvn_map = {
|
||||||
|
x.name: y
|
||||||
|
for x, y in tv_map.items()
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_type = tvn_map['a']
|
||||||
|
res_type = tvn_map['b']
|
||||||
|
|
||||||
|
assert isinstance(sa_type, Type3)
|
||||||
|
assert isinstance(res_type, Type3)
|
||||||
|
|
||||||
|
ptr_type_info = self.build.type_info_map['ptr']
|
||||||
|
u32_type_info = self.build.type_info_map['u32']
|
||||||
|
|
||||||
|
sa_type_info = self.build.type_info_map.get(sa_type.name)
|
||||||
|
if sa_type_info is None:
|
||||||
|
sa_type_info = ptr_type_info
|
||||||
|
res_type_info = self.build.type_info_map.get(res_type.name)
|
||||||
|
if res_type_info is None:
|
||||||
|
res_type_info = ptr_type_info
|
||||||
|
|
||||||
|
# Definitions
|
||||||
|
fold_adr = g.temp_var_t(ptr_type_info.wasm_type, 'fold_adr')
|
||||||
|
fold_stop = g.temp_var_t(ptr_type_info.wasm_type, 'fold_stop')
|
||||||
|
fold_tmp = g.temp_var_t(res_type_info.wasm_type, 'fold_tmp')
|
||||||
|
fold_func = g.temp_var_t(ptr_type_info.wasm_type, 'fold_func')
|
||||||
|
fold_len = g.temp_var_t(u32_type_info.wasm_type, 'fold_len')
|
||||||
|
|
||||||
|
with g.block(params=['i32', res_type_info.wasm_type, 'i32'], result=res_type_info.wasm_type, comment=f'foldr a={sa_type.name} b={res_type.name}'):
|
||||||
|
# Stack: [fn*, b, sa*] -> [fn*, b] ; fold_adr=fn*, fold_tmp=b, fold_func=fn*, fold_len=*sa
|
||||||
|
g.local.tee(fold_adr) # Store address, but also keep it for loading the length
|
||||||
|
g.i32.load() # Load the length
|
||||||
|
g.local.set(fold_len) # Store the length
|
||||||
|
# Stack: [fn*, b] -> [fn*]
|
||||||
|
g.local.set(fold_tmp)
|
||||||
|
# Stack: [fn*] -> []
|
||||||
|
g.local.set(fold_func)
|
||||||
|
|
||||||
|
# Stack: [] -> []
|
||||||
|
g.nop(comment='No applications if array is empty')
|
||||||
|
g.local.get(fold_tmp)
|
||||||
|
g.local.get(fold_len)
|
||||||
|
g.i32.eqz() # If the array is empty
|
||||||
|
g.br_if(0) # Then the base value is the result
|
||||||
|
g.drop() # Else drop the value for now
|
||||||
|
|
||||||
|
# Stack: [b] -> [b] ; fold_adr=fold_adr + 4
|
||||||
|
g.nop(comment='Skip the header')
|
||||||
|
g.local.get(fold_adr)
|
||||||
|
g.i32.const(4)
|
||||||
|
g.i32.add()
|
||||||
|
g.local.set(fold_adr)
|
||||||
|
|
||||||
|
# Stack: [] -> [] ; fold_stop=fold_adr
|
||||||
|
g.nop(comment='Calculate address at which to stop looping')
|
||||||
|
g.local.get(fold_adr)
|
||||||
|
g.local.set(fold_stop)
|
||||||
|
|
||||||
|
# Stack: [] -> [] ; fold_adr=fold_adr + (sa_len.value - 1) * sa_type_info.alloc_size
|
||||||
|
g.nop(comment='Calculate address at which to start looping')
|
||||||
|
g.local.get(fold_adr)
|
||||||
|
g.local.get(fold_len)
|
||||||
|
g.i32.const(1)
|
||||||
|
g.i32.sub()
|
||||||
|
g.i32.const(sa_type_info.alloc_size)
|
||||||
|
g.i32.mul()
|
||||||
|
g.i32.add()
|
||||||
|
g.local.set(fold_adr)
|
||||||
|
|
||||||
|
# Stack: [] -> [b]
|
||||||
|
g.nop(comment='Apply the first function call')
|
||||||
|
g.local.get(fold_adr)
|
||||||
|
g.add_statement(sa_type_info.wasm_load_func)
|
||||||
|
g.local.get(fold_tmp)
|
||||||
|
g.local.get(fold_func)
|
||||||
|
g.call_indirect([sa_type_info.wasm_type, res_type_info.wasm_type], res_type_info.wasm_type)
|
||||||
|
|
||||||
|
# Stack: [b] -> [b]
|
||||||
|
g.nop(comment='Check if more than one entry')
|
||||||
|
g.local.get(fold_len)
|
||||||
|
g.i32.const(1)
|
||||||
|
g.i32.eq() # If the array has only item
|
||||||
|
g.br_if(0) # Then the the first application is sufficient
|
||||||
|
|
||||||
|
# Stack: [b] -> [b] ; fold_adr = fold_adr - sa_type_info.alloc_size
|
||||||
|
g.nop(comment='Calculate address of the next value')
|
||||||
|
g.local.get(fold_adr)
|
||||||
|
g.i32.const(sa_type_info.alloc_size)
|
||||||
|
g.i32.sub()
|
||||||
|
g.local.set(fold_adr)
|
||||||
|
|
||||||
|
with g.loop(params=[res_type_info.wasm_type], result=res_type_info.wasm_type):
|
||||||
|
g.nop(comment='Apply function call')
|
||||||
|
|
||||||
|
# Stack [b] since we don't have proper stack switching opcodes
|
||||||
|
# Stack: [b] -> []
|
||||||
|
g.local.set(fold_tmp)
|
||||||
|
|
||||||
|
# Stack: [] -> [a]
|
||||||
|
g.local.get(fold_adr)
|
||||||
|
g.add_statement(sa_type_info.wasm_load_func)
|
||||||
|
|
||||||
|
# Stack [a] -> [a, b]
|
||||||
|
g.local.get(fold_tmp)
|
||||||
|
|
||||||
|
# Stack [a, b] -> [b]
|
||||||
|
g.local.get(fold_func)
|
||||||
|
g.call_indirect([sa_type_info.wasm_type, res_type_info.wasm_type], res_type_info.wasm_type)
|
||||||
|
|
||||||
|
# Stack: [b] -> [b] ; fold_adr = fold_adr - sa_type_info.alloc_size
|
||||||
|
g.nop(comment='Calculate address of the next value')
|
||||||
|
g.local.get(fold_adr)
|
||||||
|
g.i32.const(sa_type_info.alloc_size)
|
||||||
|
g.i32.sub()
|
||||||
|
g.local.tee(fold_adr)
|
||||||
|
|
||||||
|
# loop if adr >= stop
|
||||||
|
# Stack: [b] -> [b]
|
||||||
|
g.nop(comment='Check if address exceeds array bounds')
|
||||||
|
g.local.get(fold_stop)
|
||||||
|
g.i32.ge_u()
|
||||||
|
g.br_if(0)
|
||||||
|
|
||||||
|
# Stack: [b]
|
||||||
|
|
||||||
|
def wasm_static_array_foldr(self, g: WasmGenerator, tvl: TypeVariableLookup) -> None:
|
||||||
|
tv_map, tc_map = tvl
|
||||||
|
|
||||||
|
tvn_map = {
|
||||||
|
x.name: y
|
||||||
|
for x, y in tv_map.items()
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_type = tvn_map['a']
|
||||||
|
sa_len = tvn_map['a*']
|
||||||
|
res_type = tvn_map['b']
|
||||||
|
|
||||||
|
assert isinstance(sa_type, Type3)
|
||||||
|
assert isinstance(sa_len, IntType3)
|
||||||
|
assert isinstance(res_type, Type3)
|
||||||
|
|
||||||
|
ptr_type_info = self.build.type_info_map['ptr']
|
||||||
|
|
||||||
|
sa_type_info = self.build.type_info_map.get(sa_type.name)
|
||||||
|
if sa_type_info is None:
|
||||||
|
sa_type_info = ptr_type_info
|
||||||
|
res_type_info = self.build.type_info_map.get(res_type.name)
|
||||||
|
if res_type_info is None:
|
||||||
|
res_type_info = ptr_type_info
|
||||||
|
|
||||||
|
# Definitions
|
||||||
|
fold_adr = g.temp_var_t(ptr_type_info.wasm_type, 'fold_adr')
|
||||||
|
fold_stop = g.temp_var_t(ptr_type_info.wasm_type, 'fold_stop')
|
||||||
|
fold_tmp = g.temp_var_t(res_type_info.wasm_type, 'fold_tmp')
|
||||||
|
fold_func = g.temp_var_t(ptr_type_info.wasm_type, 'fold_func')
|
||||||
|
|
||||||
|
with g.block(params=['i32', res_type_info.wasm_type, 'i32'], result=res_type_info.wasm_type, comment=f'foldr a={sa_type.name} a*={sa_len.value} b={res_type.name}'):
|
||||||
|
# Stack: [fn*, b, sa*] -> [fn*, b] ; fold_adr=fn*, fold_tmp=b, fold_func=fn*
|
||||||
|
g.local.set(fold_adr)
|
||||||
|
# Stack: [fn*, b] -> [fn*]
|
||||||
|
g.local.set(fold_tmp)
|
||||||
|
# Stack: [fn*] -> []
|
||||||
|
g.local.set(fold_func)
|
||||||
|
|
||||||
|
if sa_len.value < 1:
|
||||||
|
g.local.get(fold_tmp)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Stack: [] -> [] ; fold_stop=fold_adr
|
||||||
|
g.nop(comment='Calculate address at which to stop looping')
|
||||||
|
g.local.get(fold_adr)
|
||||||
|
g.local.set(fold_stop)
|
||||||
|
|
||||||
|
# Stack: [] -> [] ; fold_adr=fold_adr + (sa_len.value - 1) * sa_type_info.alloc_size
|
||||||
|
g.nop(comment='Calculate address at which to start looping')
|
||||||
|
g.local.get(fold_adr)
|
||||||
|
g.i32.const((sa_len.value - 1) * sa_type_info.alloc_size)
|
||||||
|
g.i32.add()
|
||||||
|
g.local.set(fold_adr)
|
||||||
|
|
||||||
|
# Stack: [] -> [b]
|
||||||
|
g.nop(comment='Get the init value and first array value as starting point')
|
||||||
|
g.local.get(fold_adr)
|
||||||
|
g.add_statement(sa_type_info.wasm_load_func)
|
||||||
|
g.local.get(fold_tmp)
|
||||||
|
g.local.get(fold_func)
|
||||||
|
g.call_indirect([sa_type_info.wasm_type, res_type_info.wasm_type], res_type_info.wasm_type)
|
||||||
|
|
||||||
|
if sa_len.value > 1:
|
||||||
|
# Stack: [b] -> [b] ; fold_adr = fold_adr - sa_type_info.alloc_size
|
||||||
|
g.nop(comment='Calculate address of the next value')
|
||||||
|
g.local.get(fold_adr)
|
||||||
|
g.i32.const(sa_type_info.alloc_size)
|
||||||
|
g.i32.sub()
|
||||||
|
g.local.set(fold_adr)
|
||||||
|
|
||||||
|
with g.loop(params=[res_type_info.wasm_type], result=res_type_info.wasm_type):
|
||||||
|
g.nop(comment='Apply function call')
|
||||||
|
|
||||||
|
# Stack [b] since we don't have proper stack switching opcodes
|
||||||
|
# Stack: [b] -> []
|
||||||
|
g.local.set(fold_tmp)
|
||||||
|
|
||||||
|
# Stack: [] -> [a]
|
||||||
|
g.local.get(fold_adr)
|
||||||
|
g.add_statement(sa_type_info.wasm_load_func)
|
||||||
|
|
||||||
|
# Stack [a] -> [a, b]
|
||||||
|
g.local.get(fold_tmp)
|
||||||
|
|
||||||
|
# Stack [a, b] -> [b]
|
||||||
|
g.local.get(fold_func)
|
||||||
|
g.call_indirect([sa_type_info.wasm_type, res_type_info.wasm_type], res_type_info.wasm_type)
|
||||||
|
|
||||||
|
# Stack: [b] -> [b] ; fold_adr = fold_adr - sa_type_info.alloc_size
|
||||||
|
g.nop(comment='Calculate address of the next value')
|
||||||
|
g.local.get(fold_adr)
|
||||||
|
g.i32.const(sa_type_info.alloc_size)
|
||||||
|
g.i32.sub()
|
||||||
|
g.local.tee(fold_adr)
|
||||||
|
|
||||||
|
# loop if adr >= stop
|
||||||
|
# Stack: [b] -> [b]
|
||||||
|
g.nop(comment='Check if address exceeds array bounds')
|
||||||
|
g.local.get(fold_stop)
|
||||||
|
g.i32.ge_u()
|
||||||
|
g.br_if(0)
|
||||||
|
# else: just one value, don't need to loop
|
||||||
|
|
||||||
|
# Stack: [b]
|
||||||
|
|
||||||
|
def wasm(build: BuildBase[WasmGenerator]) -> None:
|
||||||
|
Foldable = build.type_classes['Foldable']
|
||||||
|
|
||||||
|
gen = FoldableCodeGenerator(build)
|
||||||
|
|
||||||
|
build.instance_type_class(Foldable, build.dynamic_array, methods={
|
||||||
|
'sum': gen.wasm_dynamic_array_sum,
|
||||||
|
'foldl': gen.wasm_dynamic_array_foldl,
|
||||||
|
'foldr': gen.wasm_dynamic_array_foldr,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Foldable, build.static_array, methods={
|
||||||
|
'sum': gen.wasm_static_array_sum,
|
||||||
|
'foldl': gen.wasm_static_array_foldl,
|
||||||
|
'foldr': gen.wasm_static_array_foldr,
|
||||||
|
})
|
||||||
86
phasm/build/typeclasses/fractional.py
Normal file
86
phasm/build/typeclasses/fractional.py
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
"""
|
||||||
|
The Fractional type class is defined for numeric types that can be (precisely) divided.
|
||||||
|
"""
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from ...type3.functions import make_typevar
|
||||||
|
from ...type3.routers import TypeVariableLookup
|
||||||
|
from ...type3.typeclasses import Type3Class
|
||||||
|
from ...wasmgenerator import Generator as WasmGenerator
|
||||||
|
from ..base import BuildBase
|
||||||
|
|
||||||
|
|
||||||
|
def load(build: BuildBase[Any]) -> None:
|
||||||
|
a = make_typevar('a')
|
||||||
|
NatNum = build.type_classes['NatNum']
|
||||||
|
|
||||||
|
Fractional = Type3Class('Fractional', (a, ), methods={
|
||||||
|
'ceil': [a, a],
|
||||||
|
'floor': [a, a],
|
||||||
|
'trunc': [a, a],
|
||||||
|
'nearest': [a, a],
|
||||||
|
}, operators={
|
||||||
|
'/': [a, a, a],
|
||||||
|
}, inherited_classes=[NatNum])
|
||||||
|
|
||||||
|
build.register_type_class(Fractional)
|
||||||
|
|
||||||
|
def wasm_f32_ceil(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f32.ceil()
|
||||||
|
|
||||||
|
def wasm_f64_ceil(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f64.ceil()
|
||||||
|
|
||||||
|
def wasm_f32_floor(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f32.floor()
|
||||||
|
|
||||||
|
def wasm_f64_floor(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f64.floor()
|
||||||
|
|
||||||
|
def wasm_f32_trunc(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f32.trunc()
|
||||||
|
|
||||||
|
def wasm_f64_trunc(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f64.trunc()
|
||||||
|
|
||||||
|
def wasm_f32_nearest(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f32.nearest()
|
||||||
|
|
||||||
|
def wasm_f64_nearest(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f64.nearest()
|
||||||
|
|
||||||
|
def wasm_f32_div(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f32.div()
|
||||||
|
|
||||||
|
def wasm_f64_div(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f64.div()
|
||||||
|
|
||||||
|
def wasm(build: BuildBase[WasmGenerator]) -> None:
|
||||||
|
Fractional = build.type_classes['Fractional']
|
||||||
|
|
||||||
|
build.instance_type_class(Fractional, build.types['f32'], methods={
|
||||||
|
'ceil': wasm_f32_ceil,
|
||||||
|
'floor': wasm_f32_floor,
|
||||||
|
'trunc': wasm_f32_trunc,
|
||||||
|
'nearest': wasm_f32_nearest,
|
||||||
|
}, operators={
|
||||||
|
'/': wasm_f32_div,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Fractional, build.types['f64'], methods={
|
||||||
|
'ceil': wasm_f64_ceil,
|
||||||
|
'floor': wasm_f64_floor,
|
||||||
|
'trunc': wasm_f64_trunc,
|
||||||
|
'nearest': wasm_f64_nearest,
|
||||||
|
}, operators={
|
||||||
|
'/': wasm_f64_div,
|
||||||
|
})
|
||||||
75
phasm/build/typeclasses/integral.py
Normal file
75
phasm/build/typeclasses/integral.py
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
"""
|
||||||
|
The Integral type class is defined for types that can only be approximately divided.
|
||||||
|
"""
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from ...type3.functions import make_typevar
|
||||||
|
from ...type3.routers import TypeVariableLookup
|
||||||
|
from ...type3.typeclasses import Type3Class
|
||||||
|
from ...wasmgenerator import Generator as WasmGenerator
|
||||||
|
from ..base import BuildBase
|
||||||
|
|
||||||
|
|
||||||
|
def load(build: BuildBase[Any]) -> None:
|
||||||
|
a = make_typevar('a')
|
||||||
|
NatNum = build.type_classes['NatNum']
|
||||||
|
|
||||||
|
Integral = Type3Class('Integral', (a, ), methods={
|
||||||
|
}, operators={
|
||||||
|
'//': [a, a, a],
|
||||||
|
'%': [a, a, a],
|
||||||
|
}, inherited_classes=[NatNum])
|
||||||
|
|
||||||
|
build.register_type_class(Integral)
|
||||||
|
|
||||||
|
def wasm_u32_div(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.add_statement('i32.div_u')
|
||||||
|
|
||||||
|
def wasm_u64_div(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.add_statement('i64.div_u')
|
||||||
|
|
||||||
|
def wasm_i32_div(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.add_statement('i32.div_s')
|
||||||
|
|
||||||
|
def wasm_i64_div(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.add_statement('i64.div_s')
|
||||||
|
|
||||||
|
def wasm_u32_rem(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.add_statement('i32.rem_u')
|
||||||
|
|
||||||
|
def wasm_u64_rem(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.add_statement('i64.rem_u')
|
||||||
|
|
||||||
|
def wasm_i32_rem(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.add_statement('i32.rem_s')
|
||||||
|
|
||||||
|
def wasm_i64_rem(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.add_statement('i64.rem_s')
|
||||||
|
|
||||||
|
def wasm(build: BuildBase[WasmGenerator]) -> None:
|
||||||
|
Integral = build.type_classes['Integral']
|
||||||
|
|
||||||
|
build.instance_type_class(Integral, build.types['u32'], operators={
|
||||||
|
'//': wasm_u32_div,
|
||||||
|
'%': wasm_u32_rem,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Integral, build.types['u64'], operators={
|
||||||
|
'//': wasm_u64_div,
|
||||||
|
'%': wasm_u64_rem,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Integral, build.types['i32'], operators={
|
||||||
|
'//': wasm_i32_div,
|
||||||
|
'%': wasm_i32_rem,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Integral, build.types['i64'], operators={
|
||||||
|
'//': wasm_i64_div,
|
||||||
|
'%': wasm_i64_rem,
|
||||||
|
})
|
||||||
76
phasm/build/typeclasses/intnum.py
Normal file
76
phasm/build/typeclasses/intnum.py
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
"""
|
||||||
|
The IntNum type class is defined for Integer Number types.
|
||||||
|
"""
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from ...type3.functions import make_typevar
|
||||||
|
from ...type3.routers import TypeVariableLookup
|
||||||
|
from ...type3.typeclasses import Type3Class
|
||||||
|
from ...wasmgenerator import Generator as WasmGenerator
|
||||||
|
from ..base import BuildBase
|
||||||
|
|
||||||
|
|
||||||
|
def load(build: BuildBase[Any]) -> None:
|
||||||
|
a = make_typevar('a')
|
||||||
|
NatNum = build.type_classes['NatNum']
|
||||||
|
|
||||||
|
IntNum = Type3Class('IntNum', (a, ), methods={
|
||||||
|
'abs': [a, a],
|
||||||
|
'neg': [a, a],
|
||||||
|
}, operators={}, inherited_classes=[NatNum])
|
||||||
|
|
||||||
|
build.register_type_class(IntNum)
|
||||||
|
|
||||||
|
def wasm_i32_abs(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.call('stdlib.types.__i32_abs__')
|
||||||
|
|
||||||
|
def wasm_i64_abs(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.call('stdlib.types.__i64_abs__')
|
||||||
|
|
||||||
|
def wasm_f32_abs(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f32.abs()
|
||||||
|
|
||||||
|
def wasm_f64_abs(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f64.abs()
|
||||||
|
|
||||||
|
def wasm_i32_neg(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.const(-1)
|
||||||
|
g.i32.mul()
|
||||||
|
|
||||||
|
def wasm_i64_neg(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i64.const(-1)
|
||||||
|
g.i64.mul()
|
||||||
|
|
||||||
|
def wasm_f32_neg(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f32.neg()
|
||||||
|
|
||||||
|
def wasm_f64_neg(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f64.neg()
|
||||||
|
|
||||||
|
def wasm(build: BuildBase[WasmGenerator]) -> None:
|
||||||
|
IntNum = build.type_classes['IntNum']
|
||||||
|
|
||||||
|
build.instance_type_class(IntNum, build.types['i32'], methods={
|
||||||
|
'abs': wasm_i32_abs,
|
||||||
|
'neg': wasm_i32_neg,
|
||||||
|
})
|
||||||
|
build.instance_type_class(IntNum, build.types['i64'], methods={
|
||||||
|
'abs': wasm_i64_abs,
|
||||||
|
'neg': wasm_i64_neg,
|
||||||
|
})
|
||||||
|
build.instance_type_class(IntNum, build.types['f32'], methods={
|
||||||
|
'abs': wasm_f32_abs,
|
||||||
|
'neg': wasm_f32_neg,
|
||||||
|
})
|
||||||
|
build.instance_type_class(IntNum, build.types['f64'], methods={
|
||||||
|
'abs': wasm_f64_abs,
|
||||||
|
'neg': wasm_f64_neg,
|
||||||
|
})
|
||||||
208
phasm/build/typeclasses/natnum.py
Normal file
208
phasm/build/typeclasses/natnum.py
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
"""
|
||||||
|
The NatNum type class is defined for Natural Number types.
|
||||||
|
|
||||||
|
These cannot be negative so functions like abs and neg make no sense.
|
||||||
|
"""
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from ...type3.functions import make_typevar
|
||||||
|
from ...type3.routers import TypeVariableLookup
|
||||||
|
from ...type3.typeclasses import Type3Class
|
||||||
|
from ...wasmgenerator import Generator as WasmGenerator
|
||||||
|
from ..base import BuildBase
|
||||||
|
|
||||||
|
|
||||||
|
def load(build: BuildBase[Any]) -> None:
|
||||||
|
a = make_typevar('a')
|
||||||
|
u32 = build.types['u32']
|
||||||
|
|
||||||
|
NatNum = Type3Class('NatNum', (a, ), methods={}, operators={
|
||||||
|
'+': [a, a, a],
|
||||||
|
'-': [a, a, a],
|
||||||
|
'*': [a, a, a],
|
||||||
|
'<<': [a, u32, a], # Arithmic shift left
|
||||||
|
'>>': [a, u32, a], # Arithmic shift right
|
||||||
|
})
|
||||||
|
|
||||||
|
build.register_type_class(NatNum)
|
||||||
|
|
||||||
|
## ###
|
||||||
|
## class NatNum
|
||||||
|
|
||||||
|
def wasm_u32_add(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.add_statement('i32.add')
|
||||||
|
|
||||||
|
def wasm_u64_add(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.add_statement('i64.add')
|
||||||
|
|
||||||
|
def wasm_i32_add(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.add_statement('i32.add')
|
||||||
|
|
||||||
|
def wasm_i64_add(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.add_statement('i64.add')
|
||||||
|
|
||||||
|
def wasm_f32_add(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.add_statement('f32.add')
|
||||||
|
|
||||||
|
def wasm_f64_add(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.add_statement('f64.add')
|
||||||
|
|
||||||
|
def wasm_u32_sub(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.add_statement('i32.sub')
|
||||||
|
|
||||||
|
def wasm_u64_sub(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.add_statement('i64.sub')
|
||||||
|
|
||||||
|
def wasm_i32_sub(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.add_statement('i32.sub')
|
||||||
|
|
||||||
|
def wasm_i64_sub(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.add_statement('i64.sub')
|
||||||
|
|
||||||
|
def wasm_f32_sub(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.add_statement('f32.sub')
|
||||||
|
|
||||||
|
def wasm_f64_sub(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.add_statement('f64.sub')
|
||||||
|
|
||||||
|
def wasm_u32_mul(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.add_statement('i32.mul')
|
||||||
|
|
||||||
|
def wasm_u64_mul(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.add_statement('i64.mul')
|
||||||
|
|
||||||
|
def wasm_i32_mul(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.add_statement('i32.mul')
|
||||||
|
|
||||||
|
def wasm_i64_mul(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.add_statement('i64.mul')
|
||||||
|
|
||||||
|
def wasm_f32_mul(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.add_statement('f32.mul')
|
||||||
|
|
||||||
|
def wasm_f64_mul(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.add_statement('f64.mul')
|
||||||
|
|
||||||
|
def wasm_u32_arithmic_shift_left(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.shl()
|
||||||
|
|
||||||
|
def wasm_u64_arithmic_shift_left(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i64.extend_i32_u()
|
||||||
|
g.i64.shl()
|
||||||
|
|
||||||
|
def wasm_i32_arithmic_shift_left(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.shl()
|
||||||
|
|
||||||
|
def wasm_i64_arithmic_shift_left(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i64.extend_i32_u()
|
||||||
|
g.i64.shl()
|
||||||
|
|
||||||
|
def wasm_f32_arithmic_shift_left(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.call('stdlib.types.__u32_pow2__')
|
||||||
|
g.f32.convert_i32_u()
|
||||||
|
g.f32.mul()
|
||||||
|
|
||||||
|
def wasm_f64_arithmic_shift_left(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.call('stdlib.types.__u32_pow2__')
|
||||||
|
g.f64.convert_i32_u()
|
||||||
|
g.f64.mul()
|
||||||
|
|
||||||
|
def wasm_u32_arithmic_shift_right(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.shr_u()
|
||||||
|
|
||||||
|
def wasm_u64_arithmic_shift_right(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i64.extend_i32_u()
|
||||||
|
g.i64.shr_u()
|
||||||
|
|
||||||
|
def wasm_i32_arithmic_shift_right(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.shr_s()
|
||||||
|
|
||||||
|
def wasm_i64_arithmic_shift_right(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i64.extend_i32_u()
|
||||||
|
g.i64.shr_s()
|
||||||
|
|
||||||
|
def wasm_f32_arithmic_shift_right(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.call('stdlib.types.__u32_pow2__')
|
||||||
|
g.f32.convert_i32_u()
|
||||||
|
g.f32.div()
|
||||||
|
|
||||||
|
def wasm_f64_arithmic_shift_right(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.call('stdlib.types.__u32_pow2__')
|
||||||
|
g.f64.convert_i32_u()
|
||||||
|
g.f64.div()
|
||||||
|
|
||||||
|
def wasm(build: BuildBase[WasmGenerator]) -> None:
|
||||||
|
NatNum = build.type_classes['NatNum']
|
||||||
|
|
||||||
|
build.instance_type_class(NatNum, build.types['u32'], operators={
|
||||||
|
'+': wasm_u32_add,
|
||||||
|
'-': wasm_u32_sub,
|
||||||
|
'*': wasm_u32_mul,
|
||||||
|
'<<': wasm_u32_arithmic_shift_left,
|
||||||
|
'>>': wasm_u32_arithmic_shift_right,
|
||||||
|
})
|
||||||
|
build.instance_type_class(NatNum, build.types['u64'], operators={
|
||||||
|
'+': wasm_u64_add,
|
||||||
|
'-': wasm_u64_sub,
|
||||||
|
'*': wasm_u64_mul,
|
||||||
|
'<<': wasm_u64_arithmic_shift_left,
|
||||||
|
'>>': wasm_u64_arithmic_shift_right,
|
||||||
|
})
|
||||||
|
build.instance_type_class(NatNum, build.types['i32'], operators={
|
||||||
|
'+': wasm_i32_add,
|
||||||
|
'-': wasm_i32_sub,
|
||||||
|
'*': wasm_i32_mul,
|
||||||
|
'<<': wasm_i32_arithmic_shift_left,
|
||||||
|
'>>': wasm_i32_arithmic_shift_right,
|
||||||
|
})
|
||||||
|
build.instance_type_class(NatNum, build.types['i64'], operators={
|
||||||
|
'+': wasm_i64_add,
|
||||||
|
'-': wasm_i64_sub,
|
||||||
|
'*': wasm_i64_mul,
|
||||||
|
'<<': wasm_i64_arithmic_shift_left,
|
||||||
|
'>>': wasm_i64_arithmic_shift_right,
|
||||||
|
})
|
||||||
|
build.instance_type_class(NatNum, build.types['f32'], operators={
|
||||||
|
'+': wasm_f32_add,
|
||||||
|
'-': wasm_f32_sub,
|
||||||
|
'*': wasm_f32_mul,
|
||||||
|
'<<': wasm_f32_arithmic_shift_left,
|
||||||
|
'>>': wasm_f32_arithmic_shift_right,
|
||||||
|
})
|
||||||
|
build.instance_type_class(NatNum, build.types['f64'], operators={
|
||||||
|
'+': wasm_f64_add,
|
||||||
|
'-': wasm_f64_sub,
|
||||||
|
'*': wasm_f64_mul,
|
||||||
|
'<<': wasm_f64_arithmic_shift_left,
|
||||||
|
'>>': wasm_f64_arithmic_shift_right,
|
||||||
|
})
|
||||||
363
phasm/build/typeclasses/ord.py
Normal file
363
phasm/build/typeclasses/ord.py
Normal file
@ -0,0 +1,363 @@
|
|||||||
|
"""
|
||||||
|
The Ord type class is defined for totally ordered datatypes.
|
||||||
|
"""
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from ...type3.functions import make_typevar
|
||||||
|
from ...type3.routers import TypeVariableLookup
|
||||||
|
from ...type3.typeclasses import Type3Class
|
||||||
|
from ...wasmgenerator import Generator as WasmGenerator
|
||||||
|
from ..base import BuildBase
|
||||||
|
|
||||||
|
|
||||||
|
def load(build: BuildBase[Any]) -> None:
|
||||||
|
a = make_typevar('a')
|
||||||
|
|
||||||
|
Eq = build.type_classes['Eq']
|
||||||
|
|
||||||
|
Ord = Type3Class('Ord', (a, ), methods={
|
||||||
|
'min': [a, a, a],
|
||||||
|
'max': [a, a, a],
|
||||||
|
}, operators={
|
||||||
|
'<': [a, a, build.bool_],
|
||||||
|
'<=': [a, a, build.bool_],
|
||||||
|
'>': [a, a, build.bool_],
|
||||||
|
'>=': [a, a, build.bool_],
|
||||||
|
}, inherited_classes=[Eq])
|
||||||
|
|
||||||
|
build.register_type_class(Ord)
|
||||||
|
|
||||||
|
def wasm_u8_min(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.call('stdlib.types.__u32_min__')
|
||||||
|
|
||||||
|
def wasm_u16_min(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.call('stdlib.types.__u32_min__')
|
||||||
|
|
||||||
|
def wasm_u32_min(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.call('stdlib.types.__u32_min__')
|
||||||
|
|
||||||
|
def wasm_u64_min(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.call('stdlib.types.__u64_min__')
|
||||||
|
|
||||||
|
def wasm_i8_min(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.call('stdlib.types.__i32_min__')
|
||||||
|
|
||||||
|
def wasm_i16_min(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.call('stdlib.types.__i32_min__')
|
||||||
|
|
||||||
|
def wasm_i32_min(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.call('stdlib.types.__i32_min__')
|
||||||
|
|
||||||
|
def wasm_i64_min(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.call('stdlib.types.__i64_min__')
|
||||||
|
|
||||||
|
def wasm_f32_min(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f32.min()
|
||||||
|
|
||||||
|
def wasm_f64_min(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f64.min()
|
||||||
|
|
||||||
|
def wasm_u8_max(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.call('stdlib.types.__u32_max__')
|
||||||
|
|
||||||
|
def wasm_u16_max(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.call('stdlib.types.__u32_max__')
|
||||||
|
|
||||||
|
def wasm_u32_max(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.call('stdlib.types.__u32_max__')
|
||||||
|
|
||||||
|
def wasm_u64_max(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.call('stdlib.types.__u64_max__')
|
||||||
|
|
||||||
|
def wasm_i8_max(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.call('stdlib.types.__i32_max__')
|
||||||
|
|
||||||
|
def wasm_i16_max(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.call('stdlib.types.__i32_max__')
|
||||||
|
|
||||||
|
def wasm_i32_max(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.call('stdlib.types.__i32_max__')
|
||||||
|
|
||||||
|
def wasm_i64_max(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.call('stdlib.types.__i64_max__')
|
||||||
|
|
||||||
|
def wasm_f32_max(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f32.max()
|
||||||
|
|
||||||
|
def wasm_f64_max(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f64.max()
|
||||||
|
|
||||||
|
|
||||||
|
def wasm_u8_less_than(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.lt_u()
|
||||||
|
|
||||||
|
def wasm_u16_less_than(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.lt_u()
|
||||||
|
|
||||||
|
def wasm_u32_less_than(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.lt_u()
|
||||||
|
|
||||||
|
def wasm_u64_less_than(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i64.lt_u()
|
||||||
|
|
||||||
|
def wasm_i8_less_than(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.lt_s()
|
||||||
|
|
||||||
|
def wasm_i16_less_than(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.lt_s()
|
||||||
|
|
||||||
|
def wasm_i32_less_than(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.lt_s()
|
||||||
|
|
||||||
|
def wasm_i64_less_than(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i64.lt_s()
|
||||||
|
|
||||||
|
def wasm_f32_less_than(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f32.lt()
|
||||||
|
|
||||||
|
def wasm_f64_less_than(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f64.lt()
|
||||||
|
|
||||||
|
def wasm_u8_less_than_or_equal(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.le_u()
|
||||||
|
|
||||||
|
def wasm_u16_less_than_or_equal(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.le_u()
|
||||||
|
|
||||||
|
def wasm_u32_less_than_or_equal(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.le_u()
|
||||||
|
|
||||||
|
def wasm_u64_less_than_or_equal(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i64.le_u()
|
||||||
|
|
||||||
|
def wasm_i8_less_than_or_equal(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.le_s()
|
||||||
|
|
||||||
|
def wasm_i16_less_than_or_equal(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.le_s()
|
||||||
|
|
||||||
|
def wasm_i32_less_than_or_equal(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.le_s()
|
||||||
|
|
||||||
|
def wasm_i64_less_than_or_equal(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i64.le_s()
|
||||||
|
|
||||||
|
def wasm_f32_less_than_or_equal(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f32.le()
|
||||||
|
|
||||||
|
def wasm_f64_less_than_or_equal(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f64.le()
|
||||||
|
|
||||||
|
def wasm_u8_greater_than(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.gt_u()
|
||||||
|
|
||||||
|
def wasm_u16_greater_than(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.gt_u()
|
||||||
|
|
||||||
|
def wasm_u32_greater_than(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.gt_u()
|
||||||
|
|
||||||
|
def wasm_u64_greater_than(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i64.gt_u()
|
||||||
|
|
||||||
|
def wasm_i8_greater_than(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.gt_s()
|
||||||
|
|
||||||
|
def wasm_i16_greater_than(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.gt_s()
|
||||||
|
|
||||||
|
def wasm_i32_greater_than(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.gt_s()
|
||||||
|
|
||||||
|
def wasm_i64_greater_than(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i64.gt_s()
|
||||||
|
|
||||||
|
def wasm_f32_greater_than(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f32.gt()
|
||||||
|
|
||||||
|
def wasm_f64_greater_than(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f64.gt()
|
||||||
|
|
||||||
|
def wasm_u8_greater_than_or_equal(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.ge_u()
|
||||||
|
|
||||||
|
def wasm_u16_greater_than_or_equal(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.ge_u()
|
||||||
|
|
||||||
|
def wasm_u32_greater_than_or_equal(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.ge_u()
|
||||||
|
|
||||||
|
def wasm_u64_greater_than_or_equal(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i64.ge_u()
|
||||||
|
|
||||||
|
def wasm_i8_greater_than_or_equal(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.ge_s()
|
||||||
|
|
||||||
|
def wasm_i16_greater_than_or_equal(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.ge_s()
|
||||||
|
|
||||||
|
def wasm_i32_greater_than_or_equal(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.ge_s()
|
||||||
|
|
||||||
|
def wasm_i64_greater_than_or_equal(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i64.ge_s()
|
||||||
|
|
||||||
|
def wasm_f32_greater_than_or_equal(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f32.ge()
|
||||||
|
|
||||||
|
def wasm_f64_greater_than_or_equal(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f64.ge()
|
||||||
|
|
||||||
|
def wasm(build: BuildBase[WasmGenerator]) -> None:
|
||||||
|
Ord = build.type_classes['Ord']
|
||||||
|
|
||||||
|
build.instance_type_class(Ord, build.types['u8'], methods={
|
||||||
|
'min': wasm_u8_min,
|
||||||
|
'max': wasm_u8_max,
|
||||||
|
}, operators={
|
||||||
|
'<': wasm_u8_less_than,
|
||||||
|
'<=': wasm_u8_less_than_or_equal,
|
||||||
|
'>': wasm_u8_greater_than,
|
||||||
|
'>=': wasm_u8_greater_than_or_equal,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Ord, build.types['u16'], methods={
|
||||||
|
'min': wasm_u16_min,
|
||||||
|
'max': wasm_u16_max,
|
||||||
|
}, operators={
|
||||||
|
'<': wasm_u16_less_than,
|
||||||
|
'<=': wasm_u16_less_than_or_equal,
|
||||||
|
'>': wasm_u16_greater_than,
|
||||||
|
'>=': wasm_u16_greater_than_or_equal,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Ord, build.types['u32'], methods={
|
||||||
|
'min': wasm_u32_min,
|
||||||
|
'max': wasm_u32_max,
|
||||||
|
}, operators={
|
||||||
|
'<': wasm_u32_less_than,
|
||||||
|
'<=': wasm_u32_less_than_or_equal,
|
||||||
|
'>': wasm_u32_greater_than,
|
||||||
|
'>=': wasm_u32_greater_than_or_equal,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Ord, build.types['u64'], methods={
|
||||||
|
'min': wasm_u64_min,
|
||||||
|
'max': wasm_u64_max,
|
||||||
|
}, operators={
|
||||||
|
'<': wasm_u64_less_than,
|
||||||
|
'<=': wasm_u64_less_than_or_equal,
|
||||||
|
'>': wasm_u64_greater_than,
|
||||||
|
'>=': wasm_u64_greater_than_or_equal,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Ord, build.types['i8'], methods={
|
||||||
|
'min': wasm_i8_min,
|
||||||
|
'max': wasm_i8_max,
|
||||||
|
}, operators={
|
||||||
|
'<': wasm_i8_less_than,
|
||||||
|
'<=': wasm_i8_less_than_or_equal,
|
||||||
|
'>': wasm_i8_greater_than,
|
||||||
|
'>=': wasm_i8_greater_than_or_equal,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Ord, build.types['i16'], methods={
|
||||||
|
'min': wasm_i16_min,
|
||||||
|
'max': wasm_i16_max,
|
||||||
|
}, operators={
|
||||||
|
'<': wasm_i16_less_than,
|
||||||
|
'<=': wasm_i16_less_than_or_equal,
|
||||||
|
'>': wasm_i16_greater_than,
|
||||||
|
'>=': wasm_i16_greater_than_or_equal,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Ord, build.types['i32'], methods={
|
||||||
|
'min': wasm_i32_min,
|
||||||
|
'max': wasm_i32_max,
|
||||||
|
}, operators={
|
||||||
|
'<': wasm_i32_less_than,
|
||||||
|
'<=': wasm_i32_less_than_or_equal,
|
||||||
|
'>': wasm_i32_greater_than,
|
||||||
|
'>=': wasm_i32_greater_than_or_equal,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Ord, build.types['i64'], methods={
|
||||||
|
'min': wasm_i64_min,
|
||||||
|
'max': wasm_i64_max,
|
||||||
|
}, operators={
|
||||||
|
'<': wasm_i64_less_than,
|
||||||
|
'<=': wasm_i64_less_than_or_equal,
|
||||||
|
'>': wasm_i64_greater_than,
|
||||||
|
'>=': wasm_i64_greater_than_or_equal,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Ord, build.types['f32'], methods={
|
||||||
|
'min': wasm_f32_min,
|
||||||
|
'max': wasm_f32_max,
|
||||||
|
}, operators={
|
||||||
|
'<': wasm_f32_less_than,
|
||||||
|
'<=': wasm_f32_less_than_or_equal,
|
||||||
|
'>': wasm_f32_greater_than,
|
||||||
|
'>=': wasm_f32_greater_than_or_equal,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Ord, build.types['f64'], methods={
|
||||||
|
'min': wasm_f64_min,
|
||||||
|
'max': wasm_f64_max,
|
||||||
|
}, operators={
|
||||||
|
'<': wasm_f64_less_than,
|
||||||
|
'<=': wasm_f64_less_than_or_equal,
|
||||||
|
'>': wasm_f64_greater_than,
|
||||||
|
'>=': wasm_f64_greater_than_or_equal,
|
||||||
|
})
|
||||||
39
phasm/build/typeclasses/promotable.py
Normal file
39
phasm/build/typeclasses/promotable.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
"""
|
||||||
|
The Promotable type class is defined for types that can safely be promoted to a type
|
||||||
|
that can hold strictly more values. Going back will result in some precision being lost.
|
||||||
|
"""
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from ...type3.functions import make_typevar
|
||||||
|
from ...type3.routers import TypeVariableLookup
|
||||||
|
from ...type3.typeclasses import Type3Class
|
||||||
|
from ...wasmgenerator import Generator as WasmGenerator
|
||||||
|
from ..base import BuildBase
|
||||||
|
|
||||||
|
|
||||||
|
def load(build: BuildBase[Any]) -> None:
|
||||||
|
a = make_typevar('a')
|
||||||
|
b = make_typevar('b')
|
||||||
|
|
||||||
|
Promotable = Type3Class('Promotable', (a, b, ), methods={
|
||||||
|
'promote': [a, b],
|
||||||
|
'demote': [b, a],
|
||||||
|
}, operators={})
|
||||||
|
|
||||||
|
build.register_type_class(Promotable)
|
||||||
|
|
||||||
|
def wasm_f32_f64_promote(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f64.promote_f32()
|
||||||
|
|
||||||
|
def wasm_f32_f64_demote(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f32.demote_f64()
|
||||||
|
|
||||||
|
def wasm(build: BuildBase[WasmGenerator]) -> None:
|
||||||
|
Promotable = build.type_classes['Promotable']
|
||||||
|
|
||||||
|
build.instance_type_class(Promotable, build.types['f32'], build.types['f64'], methods={
|
||||||
|
'promote': wasm_f32_f64_promote,
|
||||||
|
'demote': wasm_f32_f64_demote,
|
||||||
|
})
|
||||||
82
phasm/build/typeclasses/reinterpretable.py
Normal file
82
phasm/build/typeclasses/reinterpretable.py
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
"""
|
||||||
|
The Reinterpretable type class is defined for when the data for a type can be reinterpreted
|
||||||
|
to hold a value for another type.
|
||||||
|
"""
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from ...type3.functions import make_typevar
|
||||||
|
from ...type3.routers import TypeVariableLookup
|
||||||
|
from ...type3.typeclasses import Type3Class
|
||||||
|
from ...wasmgenerator import Generator as WasmGenerator
|
||||||
|
from ..base import BuildBase
|
||||||
|
|
||||||
|
|
||||||
|
def load(build: BuildBase[Any]) -> None:
|
||||||
|
a = make_typevar('a')
|
||||||
|
b = make_typevar('b')
|
||||||
|
|
||||||
|
Reinterpretable = Type3Class('Reinterpretable', (a, b, ), methods={
|
||||||
|
'reinterpret': [a, b]
|
||||||
|
}, operators={})
|
||||||
|
|
||||||
|
build.register_type_class(Reinterpretable)
|
||||||
|
|
||||||
|
def wasm_i32_f32_reinterpret(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f32.reinterpret_i32()
|
||||||
|
|
||||||
|
def wasm_u32_f32_reinterpret(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f32.reinterpret_i32()
|
||||||
|
|
||||||
|
def wasm_i64_f64_reinterpret(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f64.reinterpret_i64()
|
||||||
|
|
||||||
|
def wasm_u64_f64_reinterpret(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.f64.reinterpret_i64()
|
||||||
|
|
||||||
|
def wasm_f32_i32_reinterpret(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.reinterpret_f32()
|
||||||
|
|
||||||
|
def wasm_f32_u32_reinterpret(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i32.reinterpret_f32()
|
||||||
|
|
||||||
|
def wasm_f64_i64_reinterpret(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i64.reinterpret_f64()
|
||||||
|
|
||||||
|
def wasm_f64_u64_reinterpret(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
g.i64.reinterpret_f64()
|
||||||
|
|
||||||
|
def wasm(build: BuildBase[WasmGenerator]) -> None:
|
||||||
|
Reinterpretable = build.type_classes['Reinterpretable']
|
||||||
|
|
||||||
|
build.instance_type_class(Reinterpretable, build.types['u32'], build.types['f32'], methods={
|
||||||
|
'reinterpret': wasm_u32_f32_reinterpret,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Reinterpretable, build.types['u64'], build.types['f64'], methods={
|
||||||
|
'reinterpret': wasm_u64_f64_reinterpret,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Reinterpretable, build.types['i32'], build.types['f32'], methods={
|
||||||
|
'reinterpret': wasm_i32_f32_reinterpret,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Reinterpretable, build.types['i64'], build.types['f64'], methods={
|
||||||
|
'reinterpret': wasm_i64_f64_reinterpret,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Reinterpretable, build.types['f32'], build.types['u32'], methods={
|
||||||
|
'reinterpret': wasm_f32_u32_reinterpret,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Reinterpretable, build.types['f64'], build.types['u64'], methods={
|
||||||
|
'reinterpret': wasm_f64_u64_reinterpret,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Reinterpretable, build.types['f32'], build.types['i32'], methods={
|
||||||
|
'reinterpret': wasm_f32_i32_reinterpret,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Reinterpretable, build.types['f64'], build.types['i64'], methods={
|
||||||
|
'reinterpret': wasm_f64_i64_reinterpret,
|
||||||
|
})
|
||||||
53
phasm/build/typeclasses/sized.py
Normal file
53
phasm/build/typeclasses/sized.py
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
"""
|
||||||
|
The Sized type class is defined for when a value can be considered to have a length.
|
||||||
|
|
||||||
|
The length is always in number of items, and never in number of bytes (unless an item is a byte).
|
||||||
|
"""
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from ...type3.functions import TypeConstructorVariable, make_typevar
|
||||||
|
from ...type3.routers import TypeVariableLookup
|
||||||
|
from ...type3.typeclasses import Type3Class
|
||||||
|
from ...type3.types import IntType3
|
||||||
|
from ...wasmgenerator import Generator as WasmGenerator
|
||||||
|
from ..base import BuildBase
|
||||||
|
|
||||||
|
|
||||||
|
def load(build: BuildBase[Any]) -> None:
|
||||||
|
a = make_typevar('a')
|
||||||
|
t = TypeConstructorVariable('t')
|
||||||
|
|
||||||
|
u32 = build.types['u32']
|
||||||
|
|
||||||
|
Sized = Type3Class('Sized', (t, ), methods={
|
||||||
|
'len': [t(a), u32],
|
||||||
|
}, operators={}) # FIXME: Once we get type class families, add [] here
|
||||||
|
|
||||||
|
build.register_type_class(Sized)
|
||||||
|
|
||||||
|
def wasm_dynamic_array_len(g: WasmGenerator, tv_map: TypeVariableLookup) -> None:
|
||||||
|
del tv_map
|
||||||
|
# The length is stored in the first 4 bytes
|
||||||
|
g.i32.load()
|
||||||
|
|
||||||
|
def wasm_static_array_len(g: WasmGenerator, tvl: TypeVariableLookup) -> None:
|
||||||
|
tv_map, tc_map = tvl
|
||||||
|
|
||||||
|
tvn_map = {
|
||||||
|
x.name: y
|
||||||
|
for x, y in tv_map.items()
|
||||||
|
}
|
||||||
|
|
||||||
|
sa_len = tvn_map['a*']
|
||||||
|
assert isinstance(sa_len, IntType3)
|
||||||
|
g.i32.const(sa_len.value)
|
||||||
|
|
||||||
|
def wasm(build: BuildBase[WasmGenerator]) -> None:
|
||||||
|
Sized = build.type_classes['Sized']
|
||||||
|
|
||||||
|
build.instance_type_class(Sized, build.dynamic_array, methods={
|
||||||
|
'len': wasm_dynamic_array_len,
|
||||||
|
})
|
||||||
|
build.instance_type_class(Sized, build.static_array, methods={
|
||||||
|
'len': wasm_static_array_len,
|
||||||
|
})
|
||||||
@ -3,13 +3,13 @@ This module generates source code based on the parsed AST
|
|||||||
|
|
||||||
It's intented to be a "any color, as long as it's black" kind of renderer
|
It's intented to be a "any color, as long as it's black" kind of renderer
|
||||||
"""
|
"""
|
||||||
from typing import Generator
|
from typing import Any, Generator
|
||||||
|
|
||||||
from . import ourlang, prelude
|
from . import ourlang
|
||||||
from .type3.types import Type3, TypeApplication_Struct
|
from .type3.types import Type3, TypeApplication_Struct
|
||||||
|
|
||||||
|
|
||||||
def phasm_render(inp: ourlang.Module) -> str:
|
def phasm_render(inp: ourlang.Module[Any]) -> str:
|
||||||
"""
|
"""
|
||||||
Public method for rendering a Phasm module into Phasm code
|
Public method for rendering a Phasm module into Phasm code
|
||||||
"""
|
"""
|
||||||
@ -21,9 +21,6 @@ def type3(inp: Type3) -> str:
|
|||||||
"""
|
"""
|
||||||
Render: type's name
|
Render: type's name
|
||||||
"""
|
"""
|
||||||
if inp is prelude.none:
|
|
||||||
return 'None'
|
|
||||||
|
|
||||||
return inp.name
|
return inp.name
|
||||||
|
|
||||||
def struct_definition(inp: ourlang.StructDefinition) -> str:
|
def struct_definition(inp: ourlang.StructDefinition) -> str:
|
||||||
@ -161,7 +158,7 @@ def function(inp: ourlang.Function) -> str:
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def module(inp: ourlang.Module) -> str:
|
def module(inp: ourlang.Module[Any]) -> str:
|
||||||
"""
|
"""
|
||||||
Render: Module
|
Render: Module
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -4,11 +4,11 @@ This module contains the code to convert parsed Ourlang into WebAssembly code
|
|||||||
import struct
|
import struct
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from . import ourlang, prelude, wasm
|
from . import ourlang, wasm
|
||||||
from .runtime import calculate_alloc_size, calculate_member_offset
|
from .build import builtins
|
||||||
|
from .build.base import TypeInfo
|
||||||
from .stdlib import alloc as stdlib_alloc
|
from .stdlib import alloc as stdlib_alloc
|
||||||
from .stdlib import types as stdlib_types
|
from .stdlib import types as stdlib_types
|
||||||
from .stdlib.types import TYPE_INFO_CONSTRUCTED, TYPE_INFO_MAP
|
|
||||||
from .type3.functions import FunctionArgument, TypeVariable
|
from .type3.functions import FunctionArgument, TypeVariable
|
||||||
from .type3.routers import NoRouteForTypeException, TypeApplicationRouter
|
from .type3.routers import NoRouteForTypeException, TypeApplicationRouter
|
||||||
from .type3.typeclasses import Type3ClassMethod
|
from .type3.typeclasses import Type3ClassMethod
|
||||||
@ -24,28 +24,37 @@ from .type3.types import (
|
|||||||
TypeConstructor_StaticArray,
|
TypeConstructor_StaticArray,
|
||||||
TypeConstructor_Tuple,
|
TypeConstructor_Tuple,
|
||||||
)
|
)
|
||||||
|
from .wasm import (
|
||||||
|
WasmTypeFloat32,
|
||||||
|
WasmTypeFloat64,
|
||||||
|
WasmTypeInt32,
|
||||||
|
WasmTypeInt64,
|
||||||
|
)
|
||||||
from .wasmgenerator import Generator as WasmGenerator
|
from .wasmgenerator import Generator as WasmGenerator
|
||||||
|
|
||||||
TYPE3_ASSERTION_ERROR = 'You must call phasm_type3 after calling phasm_parse before your program can be compiled'
|
TYPE3_ASSERTION_ERROR = 'You must call phasm_type3 after calling phasm_parse before your program can be compiled'
|
||||||
|
|
||||||
def phasm_compile(inp: ourlang.Module) -> wasm.Module:
|
def phasm_compile(inp: ourlang.Module[WasmGenerator]) -> wasm.Module:
|
||||||
"""
|
"""
|
||||||
Public method for compiling a parsed Phasm module into
|
Public method for compiling a parsed Phasm module into
|
||||||
a WebAssembly module
|
a WebAssembly module
|
||||||
"""
|
"""
|
||||||
return module(inp)
|
return module(inp)
|
||||||
|
|
||||||
def type3(inp: Type3) -> wasm.WasmType:
|
def type3(mod: ourlang.Module[WasmGenerator], inp: Type3) -> wasm.WasmType:
|
||||||
"""
|
"""
|
||||||
Compile: type
|
Compile: type
|
||||||
|
|
||||||
Types are used for example in WebAssembly function parameters
|
Types are used for example in WebAssembly function parameters
|
||||||
and return types.
|
and return types.
|
||||||
"""
|
"""
|
||||||
typ_info = TYPE_INFO_MAP.get(inp.name, TYPE_INFO_CONSTRUCTED)
|
typ_info = mod.build.type_info_map.get(inp.name, )
|
||||||
|
if typ_info is None:
|
||||||
|
typ_info = mod.build.type_info_map['ptr']
|
||||||
|
|
||||||
return typ_info.wasm_type()
|
return typ_info.wasm_type()
|
||||||
|
|
||||||
def tuple_instantiation(wgn: WasmGenerator, mod: ourlang.Module, inp: ourlang.TupleInstantiation) -> None:
|
def tuple_instantiation(wgn: WasmGenerator, mod: ourlang.Module[WasmGenerator], inp: ourlang.TupleInstantiation) -> None:
|
||||||
"""
|
"""
|
||||||
Compile: Instantiation (allocation) of a tuple
|
Compile: Instantiation (allocation) of a tuple
|
||||||
"""
|
"""
|
||||||
@ -65,7 +74,7 @@ def tuple_instantiation(wgn: WasmGenerator, mod: ourlang.Module, inp: ourlang.Tu
|
|||||||
args = tuple(sa_type for _ in inp.elements)
|
args = tuple(sa_type for _ in inp.elements)
|
||||||
# Can't use calculate_alloc_size directly since that doesn't
|
# Can't use calculate_alloc_size directly since that doesn't
|
||||||
# know the dynamic array's length
|
# know the dynamic array's length
|
||||||
alloc_size = 4 + calculate_alloc_size(sa_type, is_member=True) * len(inp.elements)
|
alloc_size = 4 + mod.build.calculate_alloc_size(sa_type, is_member=True) * len(inp.elements)
|
||||||
alloc_size_header = len(inp.elements)
|
alloc_size_header = len(inp.elements)
|
||||||
elif isinstance(inp.type3.application, TypeApplication_TypeStar):
|
elif isinstance(inp.type3.application, TypeApplication_TypeStar):
|
||||||
# Possibly paranoid assert. If we have a future variadic type,
|
# Possibly paranoid assert. If we have a future variadic type,
|
||||||
@ -73,7 +82,7 @@ def tuple_instantiation(wgn: WasmGenerator, mod: ourlang.Module, inp: ourlang.Tu
|
|||||||
assert isinstance(inp.type3.application.constructor, TypeConstructor_Tuple)
|
assert isinstance(inp.type3.application.constructor, TypeConstructor_Tuple)
|
||||||
|
|
||||||
args = inp.type3.application.arguments
|
args = inp.type3.application.arguments
|
||||||
alloc_size = calculate_alloc_size(inp.type3, is_member=False)
|
alloc_size = mod.build.calculate_alloc_size(inp.type3, is_member=False)
|
||||||
elif isinstance(inp.type3.application, TypeApplication_TypeInt):
|
elif isinstance(inp.type3.application, TypeApplication_TypeInt):
|
||||||
# Possibly paranoid assert. If we have a future type of kind * -> Int -> *,
|
# Possibly paranoid assert. If we have a future type of kind * -> Int -> *,
|
||||||
# does it also do this tuple instantation like this?
|
# does it also do this tuple instantation like this?
|
||||||
@ -82,7 +91,7 @@ def tuple_instantiation(wgn: WasmGenerator, mod: ourlang.Module, inp: ourlang.Tu
|
|||||||
sa_type, sa_len = inp.type3.application.arguments
|
sa_type, sa_len = inp.type3.application.arguments
|
||||||
|
|
||||||
args = tuple(sa_type for _ in range(sa_len.value))
|
args = tuple(sa_type for _ in range(sa_len.value))
|
||||||
alloc_size = calculate_alloc_size(inp.type3, is_member=False)
|
alloc_size = mod.build.calculate_alloc_size(inp.type3, is_member=False)
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError('tuple_instantiation', inp.type3)
|
raise NotImplementedError('tuple_instantiation', inp.type3)
|
||||||
|
|
||||||
@ -109,7 +118,9 @@ def tuple_instantiation(wgn: WasmGenerator, mod: ourlang.Module, inp: ourlang.Tu
|
|||||||
for element, exp_type3 in zip(inp.elements, args, strict=True):
|
for element, exp_type3 in zip(inp.elements, args, strict=True):
|
||||||
assert element.type3 == exp_type3
|
assert element.type3 == exp_type3
|
||||||
|
|
||||||
exp_type_info = TYPE_INFO_MAP.get(exp_type3.name, TYPE_INFO_CONSTRUCTED)
|
exp_type_info = mod.build.type_info_map.get(exp_type3.name)
|
||||||
|
if exp_type_info is None:
|
||||||
|
exp_type_info = mod.build.type_info_map['ptr']
|
||||||
|
|
||||||
wgn.add_statement('nop', comment='PRE')
|
wgn.add_statement('nop', comment='PRE')
|
||||||
wgn.local.get(tmp_var)
|
wgn.local.get(tmp_var)
|
||||||
@ -117,22 +128,52 @@ def tuple_instantiation(wgn: WasmGenerator, mod: ourlang.Module, inp: ourlang.Tu
|
|||||||
wgn.add_statement(exp_type_info.wasm_store_func, 'offset=' + str(offset))
|
wgn.add_statement(exp_type_info.wasm_store_func, 'offset=' + str(offset))
|
||||||
wgn.add_statement('nop', comment='POST')
|
wgn.add_statement('nop', comment='POST')
|
||||||
|
|
||||||
offset += calculate_alloc_size(exp_type3, is_member=True)
|
offset += mod.build.calculate_alloc_size(exp_type3, is_member=True)
|
||||||
|
|
||||||
# Return the allocated address
|
# Return the allocated address
|
||||||
wgn.local.get(tmp_var)
|
wgn.local.get(tmp_var)
|
||||||
|
|
||||||
def expression_subscript_bytes(
|
def expression_subscript_dynamic_array(
|
||||||
attrs: tuple[WasmGenerator, ourlang.Module, ourlang.Subscript],
|
attrs: tuple[WasmGenerator, ourlang.Module[WasmGenerator], ourlang.Subscript],
|
||||||
|
args: tuple[Type3],
|
||||||
) -> None:
|
) -> None:
|
||||||
wgn, mod, inp = attrs
|
wgn, mod, inp = attrs
|
||||||
|
el_type, = args
|
||||||
|
|
||||||
|
el_type_info = mod.build.type_info_map.get(el_type.name)
|
||||||
|
if el_type_info is None:
|
||||||
|
el_type_info = mod.build.type_info_map['ptr']
|
||||||
|
|
||||||
|
tmp_idx = wgn.temp_var_i32('tmp_idx')
|
||||||
|
tmp_adr = wgn.temp_var_i32('tmp_adr')
|
||||||
|
|
||||||
expression(wgn, mod, inp.varref)
|
expression(wgn, mod, inp.varref)
|
||||||
expression(wgn, mod, inp.index)
|
expression(wgn, mod, inp.index)
|
||||||
wgn.call(stdlib_types.__subscript_bytes__)
|
|
||||||
|
wgn.local.set(tmp_idx)
|
||||||
|
wgn.local.set(tmp_adr)
|
||||||
|
|
||||||
|
# Out of bounds check based on size stored in memory
|
||||||
|
wgn.local.get(tmp_idx)
|
||||||
|
wgn.local.get(tmp_adr)
|
||||||
|
wgn.i32.load()
|
||||||
|
wgn.i32.ge_u()
|
||||||
|
with wgn.if_():
|
||||||
|
wgn.unreachable(comment='Out of bounds')
|
||||||
|
|
||||||
|
# tmp_ard + 4 + (tmp_idx * alloc_size)
|
||||||
|
wgn.local.get(tmp_adr)
|
||||||
|
wgn.i32.const(4)
|
||||||
|
wgn.i32.add()
|
||||||
|
wgn.local.get(tmp_idx)
|
||||||
|
wgn.i32.const(el_type_info.alloc_size)
|
||||||
|
wgn.i32.mul()
|
||||||
|
wgn.i32.add()
|
||||||
|
|
||||||
|
wgn.add_statement(el_type_info.wasm_load_func)
|
||||||
|
|
||||||
def expression_subscript_static_array(
|
def expression_subscript_static_array(
|
||||||
attrs: tuple[WasmGenerator, ourlang.Module, ourlang.Subscript],
|
attrs: tuple[WasmGenerator, ourlang.Module[WasmGenerator], ourlang.Subscript],
|
||||||
args: tuple[Type3, IntType3],
|
args: tuple[Type3, IntType3],
|
||||||
) -> None:
|
) -> None:
|
||||||
wgn, mod, inp = attrs
|
wgn, mod, inp = attrs
|
||||||
@ -141,11 +182,11 @@ def expression_subscript_static_array(
|
|||||||
|
|
||||||
# OPTIMIZE: If index is a constant, we can use offset instead of multiply
|
# 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
|
# and we don't need to do the out of bounds check
|
||||||
|
tmp_var = wgn.temp_var_i32('index')
|
||||||
|
|
||||||
expression(wgn, mod, inp.varref)
|
expression(wgn, mod, inp.varref)
|
||||||
|
|
||||||
tmp_var = wgn.temp_var_i32('index')
|
|
||||||
expression(wgn, mod, inp.index)
|
expression(wgn, mod, inp.index)
|
||||||
|
|
||||||
wgn.local.tee(tmp_var)
|
wgn.local.tee(tmp_var)
|
||||||
|
|
||||||
# Out of bounds check based on el_len.value
|
# Out of bounds check based on el_len.value
|
||||||
@ -154,7 +195,9 @@ def expression_subscript_static_array(
|
|||||||
with wgn.if_():
|
with wgn.if_():
|
||||||
wgn.unreachable(comment='Out of bounds')
|
wgn.unreachable(comment='Out of bounds')
|
||||||
|
|
||||||
el_type_info = TYPE_INFO_MAP.get(el_type.name, TYPE_INFO_CONSTRUCTED)
|
el_type_info = mod.build.type_info_map.get(el_type.name)
|
||||||
|
if el_type_info is None:
|
||||||
|
el_type_info = mod.build.type_info_map['ptr']
|
||||||
|
|
||||||
wgn.local.get(tmp_var)
|
wgn.local.get(tmp_var)
|
||||||
wgn.i32.const(el_type_info.alloc_size)
|
wgn.i32.const(el_type_info.alloc_size)
|
||||||
@ -164,7 +207,7 @@ def expression_subscript_static_array(
|
|||||||
wgn.add_statement(el_type_info.wasm_load_func)
|
wgn.add_statement(el_type_info.wasm_load_func)
|
||||||
|
|
||||||
def expression_subscript_tuple(
|
def expression_subscript_tuple(
|
||||||
attrs: tuple[WasmGenerator, ourlang.Module, ourlang.Subscript],
|
attrs: tuple[WasmGenerator, ourlang.Module[WasmGenerator], ourlang.Subscript],
|
||||||
args: tuple[Type3, ...],
|
args: tuple[Type3, ...],
|
||||||
) -> None:
|
) -> None:
|
||||||
wgn, mod, inp = attrs
|
wgn, mod, inp = attrs
|
||||||
@ -175,7 +218,9 @@ def expression_subscript_tuple(
|
|||||||
offset = 0
|
offset = 0
|
||||||
for el_type in args[0:inp.index.value]:
|
for el_type in args[0:inp.index.value]:
|
||||||
assert el_type is not None, TYPE3_ASSERTION_ERROR
|
assert el_type is not None, TYPE3_ASSERTION_ERROR
|
||||||
el_type_info = TYPE_INFO_MAP.get(el_type.name, TYPE_INFO_CONSTRUCTED)
|
el_type_info = mod.build.type_info_map.get(el_type.name)
|
||||||
|
if el_type_info is None:
|
||||||
|
el_type_info = mod.build.type_info_map['ptr']
|
||||||
offset += el_type_info.alloc_size
|
offset += el_type_info.alloc_size
|
||||||
|
|
||||||
el_type = args[inp.index.value]
|
el_type = args[inp.index.value]
|
||||||
@ -183,15 +228,17 @@ def expression_subscript_tuple(
|
|||||||
|
|
||||||
expression(wgn, mod, inp.varref)
|
expression(wgn, mod, inp.varref)
|
||||||
|
|
||||||
el_type_info = TYPE_INFO_MAP.get(el_type.name, TYPE_INFO_CONSTRUCTED)
|
el_type_info = mod.build.type_info_map.get(el_type.name)
|
||||||
|
if el_type_info is None:
|
||||||
|
el_type_info = mod.build.type_info_map['ptr']
|
||||||
wgn.add_statement(el_type_info.wasm_load_func, f'offset={offset}')
|
wgn.add_statement(el_type_info.wasm_load_func, f'offset={offset}')
|
||||||
|
|
||||||
SUBSCRIPT_ROUTER = TypeApplicationRouter[tuple[WasmGenerator, ourlang.Module, ourlang.Subscript], None]()
|
SUBSCRIPT_ROUTER = TypeApplicationRouter[tuple[WasmGenerator, ourlang.Module[WasmGenerator], ourlang.Subscript], None]()
|
||||||
SUBSCRIPT_ROUTER.add_n(prelude.bytes_, expression_subscript_bytes)
|
SUBSCRIPT_ROUTER.add(builtins.dynamic_array, expression_subscript_dynamic_array)
|
||||||
SUBSCRIPT_ROUTER.add(prelude.static_array, expression_subscript_static_array)
|
SUBSCRIPT_ROUTER.add(builtins.static_array, expression_subscript_static_array)
|
||||||
SUBSCRIPT_ROUTER.add(prelude.tuple_, expression_subscript_tuple)
|
SUBSCRIPT_ROUTER.add(builtins.tuple_, expression_subscript_tuple)
|
||||||
|
|
||||||
def expression(wgn: WasmGenerator, mod: ourlang.Module, inp: ourlang.Expression) -> None:
|
def expression(wgn: WasmGenerator, mod: ourlang.Module[WasmGenerator], inp: ourlang.Expression) -> None:
|
||||||
"""
|
"""
|
||||||
Compile: Any expression
|
Compile: Any expression
|
||||||
"""
|
"""
|
||||||
@ -202,34 +249,23 @@ def expression(wgn: WasmGenerator, mod: ourlang.Module, inp: ourlang.Expression)
|
|||||||
if isinstance(inp, ourlang.ConstantPrimitive):
|
if isinstance(inp, ourlang.ConstantPrimitive):
|
||||||
assert inp.type3 is not None, TYPE3_ASSERTION_ERROR
|
assert inp.type3 is not None, TYPE3_ASSERTION_ERROR
|
||||||
|
|
||||||
if inp.type3 in (prelude.i8, prelude.u8, ):
|
type_info = mod.build.type_info_map[inp.type3.name]
|
||||||
# No native u8 type - treat as i32, with caution
|
if type_info.wasm_type is WasmTypeInt32:
|
||||||
assert isinstance(inp.value, int)
|
assert isinstance(inp.value, int)
|
||||||
wgn.i32.const(inp.value)
|
wgn.i32.const(inp.value)
|
||||||
return
|
return
|
||||||
|
|
||||||
if inp.type3 in (prelude.i16, prelude.u16, ):
|
if type_info.wasm_type is WasmTypeInt64:
|
||||||
# No native u16 type - treat as i32, with caution
|
|
||||||
assert isinstance(inp.value, int)
|
|
||||||
wgn.i32.const(inp.value)
|
|
||||||
return
|
|
||||||
|
|
||||||
if inp.type3 in (prelude.i32, prelude.u32, ):
|
|
||||||
assert isinstance(inp.value, int)
|
|
||||||
wgn.i32.const(inp.value)
|
|
||||||
return
|
|
||||||
|
|
||||||
if inp.type3 in (prelude.i64, prelude.u64, ):
|
|
||||||
assert isinstance(inp.value, int)
|
assert isinstance(inp.value, int)
|
||||||
wgn.i64.const(inp.value)
|
wgn.i64.const(inp.value)
|
||||||
return
|
return
|
||||||
|
|
||||||
if inp.type3 == prelude.f32:
|
if type_info.wasm_type is WasmTypeFloat32:
|
||||||
assert isinstance(inp.value, float)
|
assert isinstance(inp.value, float)
|
||||||
wgn.f32.const(inp.value)
|
wgn.f32.const(inp.value)
|
||||||
return
|
return
|
||||||
|
|
||||||
if inp.type3 == prelude.f64:
|
if type_info.wasm_type is WasmTypeFloat64:
|
||||||
assert isinstance(inp.value, float)
|
assert isinstance(inp.value, float)
|
||||||
wgn.f64.const(inp.value)
|
wgn.f64.const(inp.value)
|
||||||
return
|
return
|
||||||
@ -249,7 +285,7 @@ def expression(wgn: WasmGenerator, mod: ourlang.Module, inp: ourlang.Expression)
|
|||||||
if isinstance(inp.variable, ourlang.ModuleConstantDef):
|
if isinstance(inp.variable, ourlang.ModuleConstantDef):
|
||||||
assert inp.type3 is not None, TYPE3_ASSERTION_ERROR
|
assert inp.type3 is not None, TYPE3_ASSERTION_ERROR
|
||||||
|
|
||||||
if inp.type3.name not in TYPE_INFO_MAP:
|
if inp.type3.name not in mod.build.type_info_map:
|
||||||
assert isinstance(inp.variable.constant, (ourlang.ConstantBytes, ourlang.ConstantStruct, ourlang.ConstantTuple, ))
|
assert isinstance(inp.variable.constant, (ourlang.ConstantBytes, ourlang.ConstantStruct, ourlang.ConstantTuple, ))
|
||||||
|
|
||||||
address = inp.variable.constant.data_block.address
|
address = inp.variable.constant.data_block.address
|
||||||
@ -285,7 +321,7 @@ def expression(wgn: WasmGenerator, mod: ourlang.Module, inp: ourlang.Expression)
|
|||||||
|
|
||||||
raise NotImplementedError(type_var, arg_expr.type3)
|
raise NotImplementedError(type_var, arg_expr.type3)
|
||||||
|
|
||||||
router = prelude.PRELUDE_TYPE_CLASS_INSTANCE_METHODS[inp.operator]
|
router = mod.build.type_class_instance_methods[inp.operator]
|
||||||
router(wgn, type_var_map)
|
router(wgn, type_var_map)
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -314,7 +350,7 @@ def expression(wgn: WasmGenerator, mod: ourlang.Module, inp: ourlang.Expression)
|
|||||||
|
|
||||||
raise NotImplementedError(type_var, arg_expr.type3)
|
raise NotImplementedError(type_var, arg_expr.type3)
|
||||||
|
|
||||||
router = prelude.PRELUDE_TYPE_CLASS_INSTANCE_METHODS[inp.function]
|
router = mod.build.type_class_instance_methods[inp.function]
|
||||||
try:
|
try:
|
||||||
router(wgn, type_var_map)
|
router(wgn, type_var_map)
|
||||||
except NoRouteForTypeException:
|
except NoRouteForTypeException:
|
||||||
@ -325,7 +361,7 @@ def expression(wgn: WasmGenerator, mod: ourlang.Module, inp: ourlang.Expression)
|
|||||||
assert isinstance(inp.function.type3.application.constructor, TypeConstructor_Function)
|
assert isinstance(inp.function.type3.application.constructor, TypeConstructor_Function)
|
||||||
|
|
||||||
params = [
|
params = [
|
||||||
type3(x)
|
type3(mod, x)
|
||||||
for x in inp.function.type3.application.arguments
|
for x in inp.function.type3.application.arguments
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -364,17 +400,19 @@ def expression(wgn: WasmGenerator, mod: ourlang.Module, inp: ourlang.Expression)
|
|||||||
assert isinstance(inp.struct_type3.application, TypeApplication_Struct)
|
assert isinstance(inp.struct_type3.application, TypeApplication_Struct)
|
||||||
|
|
||||||
member_type = dict(inp.struct_type3.application.arguments)[inp.member]
|
member_type = dict(inp.struct_type3.application.arguments)[inp.member]
|
||||||
member_type_info = TYPE_INFO_MAP.get(member_type.name, TYPE_INFO_CONSTRUCTED)
|
member_type_info = mod.build.type_info_map.get(member_type.name)
|
||||||
|
if member_type_info is None:
|
||||||
|
member_type_info = mod.build.type_info_map['ptr']
|
||||||
|
|
||||||
expression(wgn, mod, inp.varref)
|
expression(wgn, mod, inp.varref)
|
||||||
wgn.add_statement(member_type_info.wasm_load_func, 'offset=' + str(calculate_member_offset(
|
wgn.add_statement(member_type_info.wasm_load_func, 'offset=' + str(mod.build.calculate_member_offset(
|
||||||
inp.struct_type3.name, inp.struct_type3.application.arguments, inp.member
|
inp.struct_type3.name, inp.struct_type3.application.arguments, inp.member
|
||||||
)))
|
)))
|
||||||
return
|
return
|
||||||
|
|
||||||
raise NotImplementedError(expression, inp)
|
raise NotImplementedError(expression, inp)
|
||||||
|
|
||||||
def statement_return(wgn: WasmGenerator, mod: ourlang.Module, fun: ourlang.Function, inp: ourlang.StatementReturn) -> None:
|
def statement_return(wgn: WasmGenerator, mod: ourlang.Module[WasmGenerator], fun: ourlang.Function, inp: ourlang.StatementReturn) -> None:
|
||||||
"""
|
"""
|
||||||
Compile: Return statement
|
Compile: Return statement
|
||||||
"""
|
"""
|
||||||
@ -391,7 +429,7 @@ def statement_return(wgn: WasmGenerator, mod: ourlang.Module, fun: ourlang.Funct
|
|||||||
expression(wgn, mod, inp.value)
|
expression(wgn, mod, inp.value)
|
||||||
wgn.return_()
|
wgn.return_()
|
||||||
|
|
||||||
def statement_if(wgn: WasmGenerator, mod: ourlang.Module, fun: ourlang.Function, inp: ourlang.StatementIf) -> None:
|
def statement_if(wgn: WasmGenerator, mod: ourlang.Module[WasmGenerator], fun: ourlang.Function, inp: ourlang.StatementIf) -> None:
|
||||||
"""
|
"""
|
||||||
Compile: If statement
|
Compile: If statement
|
||||||
"""
|
"""
|
||||||
@ -406,7 +444,7 @@ def statement_if(wgn: WasmGenerator, mod: ourlang.Module, fun: ourlang.Function,
|
|||||||
# for stat in inp.else_statements:
|
# for stat in inp.else_statements:
|
||||||
# statement(wgn, stat)
|
# statement(wgn, stat)
|
||||||
|
|
||||||
def statement(wgn: WasmGenerator, mod: ourlang.Module, fun: ourlang.Function, inp: ourlang.Statement) -> None:
|
def statement(wgn: WasmGenerator, mod: ourlang.Module[WasmGenerator], fun: ourlang.Function, inp: ourlang.Statement) -> None:
|
||||||
"""
|
"""
|
||||||
Compile: any statement
|
Compile: any statement
|
||||||
"""
|
"""
|
||||||
@ -423,13 +461,13 @@ def statement(wgn: WasmGenerator, mod: ourlang.Module, fun: ourlang.Function, in
|
|||||||
|
|
||||||
raise NotImplementedError(statement, inp)
|
raise NotImplementedError(statement, inp)
|
||||||
|
|
||||||
def function_argument(inp: ourlang.FunctionParam) -> wasm.Param:
|
def function_argument(mod: ourlang.Module[WasmGenerator], inp: ourlang.FunctionParam) -> wasm.Param:
|
||||||
"""
|
"""
|
||||||
Compile: function argument
|
Compile: function argument
|
||||||
"""
|
"""
|
||||||
return (inp.name, type3(inp.type3), )
|
return (inp.name, type3(mod, inp.type3), )
|
||||||
|
|
||||||
def import_(inp: ourlang.Function) -> wasm.Import:
|
def import_(mod: ourlang.Module[WasmGenerator], inp: ourlang.Function) -> wasm.Import:
|
||||||
"""
|
"""
|
||||||
Compile: imported function
|
Compile: imported function
|
||||||
"""
|
"""
|
||||||
@ -440,13 +478,13 @@ def import_(inp: ourlang.Function) -> wasm.Import:
|
|||||||
inp.name,
|
inp.name,
|
||||||
inp.name,
|
inp.name,
|
||||||
[
|
[
|
||||||
function_argument(x)
|
function_argument(mod, x)
|
||||||
for x in inp.posonlyargs
|
for x in inp.posonlyargs
|
||||||
],
|
],
|
||||||
type3(inp.returns_type3)
|
type3(mod, inp.returns_type3)
|
||||||
)
|
)
|
||||||
|
|
||||||
def function(mod: ourlang.Module, inp: ourlang.Function) -> wasm.Function:
|
def function(mod: ourlang.Module[WasmGenerator], inp: ourlang.Function) -> wasm.Function:
|
||||||
"""
|
"""
|
||||||
Compile: function
|
Compile: function
|
||||||
"""
|
"""
|
||||||
@ -455,7 +493,7 @@ def function(mod: ourlang.Module, inp: ourlang.Function) -> wasm.Function:
|
|||||||
wgn = WasmGenerator()
|
wgn = WasmGenerator()
|
||||||
|
|
||||||
if isinstance(inp, ourlang.StructConstructor):
|
if isinstance(inp, ourlang.StructConstructor):
|
||||||
_generate_struct_constructor(wgn, inp)
|
_generate_struct_constructor(wgn, mod, inp)
|
||||||
else:
|
else:
|
||||||
for stat in inp.statements:
|
for stat in inp.statements:
|
||||||
statement(wgn, mod, inp, stat)
|
statement(wgn, mod, inp, stat)
|
||||||
@ -464,82 +502,41 @@ def function(mod: ourlang.Module, inp: ourlang.Function) -> wasm.Function:
|
|||||||
inp.name,
|
inp.name,
|
||||||
inp.name if inp.exported else None,
|
inp.name if inp.exported else None,
|
||||||
[
|
[
|
||||||
function_argument(x)
|
function_argument(mod, x)
|
||||||
for x in inp.posonlyargs
|
for x in inp.posonlyargs
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
(k, v.wasm_type(), )
|
(k, v.wasm_type(), )
|
||||||
for k, v in wgn.locals.items()
|
for k, v in wgn.locals.items()
|
||||||
],
|
],
|
||||||
type3(inp.returns_type3),
|
type3(mod, inp.returns_type3),
|
||||||
wgn.statements
|
wgn.statements
|
||||||
)
|
)
|
||||||
|
|
||||||
def module_data_u8(inp: int) -> bytes:
|
def module_data_primitive(type_info: TypeInfo, inp: int | float) -> bytes:
|
||||||
"""
|
letter_map = {
|
||||||
Compile: module data, u8 value
|
(WasmTypeInt32, 1, False): 'B',
|
||||||
"""
|
(WasmTypeInt32, 1, True): 'b',
|
||||||
return struct.pack('<B', inp)
|
(WasmTypeInt32, 2, False): 'H',
|
||||||
|
(WasmTypeInt32, 2, True): 'h',
|
||||||
|
(WasmTypeInt32, 4, False): 'I',
|
||||||
|
(WasmTypeInt32, 4, True): 'i',
|
||||||
|
(WasmTypeInt64, 8, False): 'Q',
|
||||||
|
(WasmTypeInt64, 8, True): 'q',
|
||||||
|
(WasmTypeFloat32, 4, None): 'f',
|
||||||
|
(WasmTypeFloat64, 8, None): 'd',
|
||||||
|
}
|
||||||
|
|
||||||
def module_data_u16(inp: int) -> bytes:
|
letter = letter_map[(type_info.wasm_type, type_info.alloc_size, type_info.signed, )]
|
||||||
"""
|
return struct.pack(f'<{letter}', inp)
|
||||||
Compile: module data, u16 value
|
|
||||||
"""
|
|
||||||
return struct.pack('<H', inp)
|
|
||||||
|
|
||||||
def module_data_u32(inp: int) -> bytes:
|
def module_data(mod: ourlang.Module[WasmGenerator], inp: ourlang.ModuleData) -> bytes:
|
||||||
"""
|
|
||||||
Compile: module data, u32 value
|
|
||||||
"""
|
|
||||||
return struct.pack('<I', inp)
|
|
||||||
|
|
||||||
def module_data_u64(inp: int) -> bytes:
|
|
||||||
"""
|
|
||||||
Compile: module data, u64 value
|
|
||||||
"""
|
|
||||||
return struct.pack('<Q', inp)
|
|
||||||
|
|
||||||
def module_data_i8(inp: int) -> bytes:
|
|
||||||
"""
|
|
||||||
Compile: module data, i8 value
|
|
||||||
"""
|
|
||||||
return struct.pack('<b', inp)
|
|
||||||
|
|
||||||
def module_data_i16(inp: int) -> bytes:
|
|
||||||
"""
|
|
||||||
Compile: module data, i16 value
|
|
||||||
"""
|
|
||||||
return struct.pack('<h', inp)
|
|
||||||
|
|
||||||
def module_data_i32(inp: int) -> bytes:
|
|
||||||
"""
|
|
||||||
Compile: module data, i32 value
|
|
||||||
"""
|
|
||||||
return struct.pack('<i', inp)
|
|
||||||
|
|
||||||
def module_data_i64(inp: int) -> bytes:
|
|
||||||
"""
|
|
||||||
Compile: module data, i64 value
|
|
||||||
"""
|
|
||||||
return struct.pack('<q', inp)
|
|
||||||
|
|
||||||
def module_data_f32(inp: float) -> bytes:
|
|
||||||
"""
|
|
||||||
Compile: module data, f32 value
|
|
||||||
"""
|
|
||||||
return struct.pack('<f', inp)
|
|
||||||
|
|
||||||
def module_data_f64(inp: float) -> bytes:
|
|
||||||
"""
|
|
||||||
Compile: module data, f64 value
|
|
||||||
"""
|
|
||||||
return struct.pack('<d', inp)
|
|
||||||
|
|
||||||
def module_data(inp: ourlang.ModuleData) -> bytes:
|
|
||||||
"""
|
"""
|
||||||
Compile: module data
|
Compile: module data
|
||||||
"""
|
"""
|
||||||
unalloc_ptr = stdlib_alloc.UNALLOC_PTR
|
unalloc_ptr = stdlib_alloc.UNALLOC_PTR
|
||||||
|
u32_type_info = mod.build.type_info_map['u32']
|
||||||
|
ptr_type_info = mod.build.type_info_map['ptr']
|
||||||
|
|
||||||
allocated_data = b''
|
allocated_data = b''
|
||||||
|
|
||||||
@ -551,115 +548,57 @@ def module_data(inp: ourlang.ModuleData) -> bytes:
|
|||||||
for constant in block.data:
|
for constant in block.data:
|
||||||
assert constant.type3 is not None, TYPE3_ASSERTION_ERROR
|
assert constant.type3 is not None, TYPE3_ASSERTION_ERROR
|
||||||
|
|
||||||
if isinstance(constant, ourlang.ConstantMemoryStored) and block is not constant.data_block:
|
if isinstance(constant, ourlang.ConstantBytes):
|
||||||
|
data_list.append(module_data_primitive(u32_type_info, len(constant.value)))
|
||||||
|
data_list.append(constant.value)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if isinstance(constant, ourlang.ConstantMemoryStored):
|
||||||
|
if block is constant.data_block:
|
||||||
|
raise NotImplementedError(block, constant)
|
||||||
|
|
||||||
# It's stored in a different block
|
# It's stored in a different block
|
||||||
# We only need to store its address
|
# We only need to store its address
|
||||||
# This happens for example when a tuple refers
|
# This happens for example when a tuple refers
|
||||||
# to a bytes constant
|
# to a bytes constant
|
||||||
assert constant.data_block.address is not None, 'Referred memory not yet stored'
|
assert constant.data_block.address is not None, 'Referred memory not yet stored'
|
||||||
data_list.append(module_data_u32(constant.data_block.address))
|
data_list.append(module_data_primitive(ptr_type_info, constant.data_block.address))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if constant.type3 == prelude.u8:
|
type_info = mod.build.type_info_map[constant.type3.name]
|
||||||
assert isinstance(constant, ourlang.ConstantPrimitive)
|
data_list.append(module_data_primitive(type_info, constant.value))
|
||||||
assert isinstance(constant.value, int)
|
|
||||||
data_list.append(module_data_u8(constant.value))
|
|
||||||
continue
|
|
||||||
|
|
||||||
if constant.type3 == prelude.u16:
|
|
||||||
assert isinstance(constant, ourlang.ConstantPrimitive)
|
|
||||||
assert isinstance(constant.value, int)
|
|
||||||
data_list.append(module_data_u16(constant.value))
|
|
||||||
continue
|
|
||||||
|
|
||||||
if constant.type3 == prelude.u32:
|
|
||||||
assert isinstance(constant, ourlang.ConstantPrimitive)
|
|
||||||
assert isinstance(constant.value, int)
|
|
||||||
data_list.append(module_data_u32(constant.value))
|
|
||||||
continue
|
|
||||||
|
|
||||||
if constant.type3 == prelude.u64:
|
|
||||||
assert isinstance(constant, ourlang.ConstantPrimitive)
|
|
||||||
assert isinstance(constant.value, int)
|
|
||||||
data_list.append(module_data_u64(constant.value))
|
|
||||||
continue
|
|
||||||
|
|
||||||
if constant.type3 == prelude.i8:
|
|
||||||
assert isinstance(constant, ourlang.ConstantPrimitive)
|
|
||||||
assert isinstance(constant.value, int)
|
|
||||||
data_list.append(module_data_i8(constant.value))
|
|
||||||
continue
|
|
||||||
|
|
||||||
if constant.type3 == prelude.i16:
|
|
||||||
assert isinstance(constant, ourlang.ConstantPrimitive)
|
|
||||||
assert isinstance(constant.value, int)
|
|
||||||
data_list.append(module_data_i16(constant.value))
|
|
||||||
continue
|
|
||||||
|
|
||||||
if constant.type3 == prelude.i32:
|
|
||||||
assert isinstance(constant, ourlang.ConstantPrimitive)
|
|
||||||
assert isinstance(constant.value, int)
|
|
||||||
data_list.append(module_data_i32(constant.value))
|
|
||||||
continue
|
|
||||||
|
|
||||||
if constant.type3 == prelude.i64:
|
|
||||||
assert isinstance(constant, ourlang.ConstantPrimitive)
|
|
||||||
assert isinstance(constant.value, int)
|
|
||||||
data_list.append(module_data_i64(constant.value))
|
|
||||||
continue
|
|
||||||
|
|
||||||
if constant.type3 == prelude.f32:
|
|
||||||
assert isinstance(constant, ourlang.ConstantPrimitive)
|
|
||||||
assert isinstance(constant.value, float)
|
|
||||||
data_list.append(module_data_f32(constant.value))
|
|
||||||
continue
|
|
||||||
|
|
||||||
if constant.type3 == prelude.f64:
|
|
||||||
assert isinstance(constant, ourlang.ConstantPrimitive)
|
|
||||||
assert isinstance(constant.value, float)
|
|
||||||
data_list.append(module_data_f64(constant.value))
|
|
||||||
continue
|
|
||||||
|
|
||||||
if constant.type3 == prelude.bytes_:
|
|
||||||
assert isinstance(constant, ourlang.ConstantBytes)
|
|
||||||
assert isinstance(constant.value, bytes)
|
|
||||||
data_list.append(module_data_u32(len(constant.value)))
|
|
||||||
data_list.append(constant.value)
|
|
||||||
continue
|
|
||||||
|
|
||||||
raise NotImplementedError(constant, constant.type3)
|
|
||||||
|
|
||||||
block_data = b''.join(data_list)
|
block_data = b''.join(data_list)
|
||||||
|
|
||||||
allocated_data += module_data_u32(len(block_data)) + block_data
|
allocated_data += module_data_primitive(u32_type_info, len(block_data)) + block_data
|
||||||
|
|
||||||
unalloc_ptr += 4 + len(block_data)
|
unalloc_ptr += 4 + len(block_data)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
# Store that we've initialized the memory
|
# Store that we've initialized the memory
|
||||||
module_data_u32(stdlib_alloc.IDENTIFIER)
|
module_data_primitive(u32_type_info, stdlib_alloc.IDENTIFIER)
|
||||||
# Store the first reserved i32
|
# Store the first reserved i32
|
||||||
+ module_data_u32(0)
|
+ module_data_primitive(u32_type_info, 0)
|
||||||
# Store the pointer towards the first free block
|
# Store the pointer towards the first free block
|
||||||
# In this case, 0 since we haven't freed any blocks yet
|
# In this case, 0 since we haven't freed any blocks yet
|
||||||
+ module_data_u32(0)
|
+ module_data_primitive(u32_type_info, 0)
|
||||||
# Store the pointer towards the first unallocated block
|
# Store the pointer towards the first unallocated block
|
||||||
# In this case the end of the stdlib.alloc header at the start
|
# In this case the end of the stdlib.alloc header at the start
|
||||||
+ module_data_u32(unalloc_ptr)
|
+ module_data_primitive(u32_type_info, unalloc_ptr)
|
||||||
# Store the actual data
|
# Store the actual data
|
||||||
+ allocated_data
|
+ allocated_data
|
||||||
)
|
)
|
||||||
|
|
||||||
def module(inp: ourlang.Module) -> wasm.Module:
|
def module(inp: ourlang.Module[WasmGenerator]) -> wasm.Module:
|
||||||
"""
|
"""
|
||||||
Compile: module
|
Compile: module
|
||||||
"""
|
"""
|
||||||
result = wasm.Module()
|
result = wasm.Module()
|
||||||
|
|
||||||
result.memory.data = module_data(inp.data)
|
result.memory.data = module_data(inp, inp.data)
|
||||||
|
|
||||||
result.imports = [
|
result.imports = [
|
||||||
import_(x)
|
import_(inp, x)
|
||||||
for x in inp.functions.values()
|
for x in inp.functions.values()
|
||||||
if x.imported
|
if x.imported
|
||||||
]
|
]
|
||||||
@ -668,17 +607,16 @@ def module(inp: ourlang.Module) -> wasm.Module:
|
|||||||
stdlib_alloc.__find_free_block__,
|
stdlib_alloc.__find_free_block__,
|
||||||
stdlib_alloc.__alloc__,
|
stdlib_alloc.__alloc__,
|
||||||
stdlib_types.__alloc_bytes__,
|
stdlib_types.__alloc_bytes__,
|
||||||
stdlib_types.__subscript_bytes__,
|
stdlib_types.__u32_min__,
|
||||||
stdlib_types.__u32_ord_min__,
|
stdlib_types.__u64_min__,
|
||||||
stdlib_types.__u64_ord_min__,
|
stdlib_types.__i32_min__,
|
||||||
stdlib_types.__i32_ord_min__,
|
stdlib_types.__i64_min__,
|
||||||
stdlib_types.__i64_ord_min__,
|
stdlib_types.__u32_max__,
|
||||||
stdlib_types.__u32_ord_max__,
|
stdlib_types.__u64_max__,
|
||||||
stdlib_types.__u64_ord_max__,
|
stdlib_types.__i32_max__,
|
||||||
stdlib_types.__i32_ord_max__,
|
stdlib_types.__i64_max__,
|
||||||
stdlib_types.__i64_ord_max__,
|
stdlib_types.__i32_abs__,
|
||||||
stdlib_types.__i32_intnum_abs__,
|
stdlib_types.__i64_abs__,
|
||||||
stdlib_types.__i64_intnum_abs__,
|
|
||||||
stdlib_types.__u32_pow2__,
|
stdlib_types.__u32_pow2__,
|
||||||
stdlib_types.__u8_rotl__,
|
stdlib_types.__u8_rotl__,
|
||||||
stdlib_types.__u8_rotr__,
|
stdlib_types.__u8_rotr__,
|
||||||
@ -698,7 +636,7 @@ def module(inp: ourlang.Module) -> wasm.Module:
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def _generate_struct_constructor(wgn: WasmGenerator, inp: ourlang.StructConstructor) -> None:
|
def _generate_struct_constructor(wgn: WasmGenerator, mod: ourlang.Module[WasmGenerator], inp: ourlang.StructConstructor) -> None:
|
||||||
assert isinstance(inp.struct_type3.application, TypeApplication_Struct)
|
assert isinstance(inp.struct_type3.application, TypeApplication_Struct)
|
||||||
|
|
||||||
st_args = inp.struct_type3.application.arguments
|
st_args = inp.struct_type3.application.arguments
|
||||||
@ -706,17 +644,19 @@ def _generate_struct_constructor(wgn: WasmGenerator, inp: ourlang.StructConstruc
|
|||||||
tmp_var = wgn.temp_var_i32('struct_adr')
|
tmp_var = wgn.temp_var_i32('struct_adr')
|
||||||
|
|
||||||
# Allocated the required amounts of bytes in memory
|
# Allocated the required amounts of bytes in memory
|
||||||
wgn.i32.const(calculate_alloc_size(inp.struct_type3))
|
wgn.i32.const(mod.build.calculate_alloc_size(inp.struct_type3))
|
||||||
wgn.call(stdlib_alloc.__alloc__)
|
wgn.call(stdlib_alloc.__alloc__)
|
||||||
wgn.local.set(tmp_var)
|
wgn.local.set(tmp_var)
|
||||||
|
|
||||||
# Store each member individually
|
# Store each member individually
|
||||||
for memname, mtyp3 in st_args:
|
for memname, mtyp3 in st_args:
|
||||||
mtyp3_info = TYPE_INFO_MAP.get(mtyp3.name, TYPE_INFO_CONSTRUCTED)
|
mtyp3_info = mod.build.type_info_map.get(mtyp3.name)
|
||||||
|
if mtyp3_info is None:
|
||||||
|
mtyp3_info = mod.build.type_info_map['ptr']
|
||||||
|
|
||||||
wgn.local.get(tmp_var)
|
wgn.local.get(tmp_var)
|
||||||
wgn.add_statement('local.get', f'${memname}')
|
wgn.add_statement('local.get', f'${memname}')
|
||||||
wgn.add_statement(mtyp3_info.wasm_store_func, 'offset=' + str(calculate_member_offset(
|
wgn.add_statement(mtyp3_info.wasm_store_func, 'offset=' + str(mod.build.calculate_member_offset(
|
||||||
inp.struct_type3.name, st_args, memname
|
inp.struct_type3.name, st_args, memname
|
||||||
)))
|
)))
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,7 @@ Contains the syntax tree for ourlang
|
|||||||
"""
|
"""
|
||||||
from typing import Dict, Iterable, List, Optional, Union
|
from typing import Dict, Iterable, List, Optional, Union
|
||||||
|
|
||||||
from . import prelude
|
from .build.base import BuildBase
|
||||||
from .type3.functions import FunctionSignature, TypeVariableContext
|
from .type3.functions import FunctionSignature, TypeVariableContext
|
||||||
from .type3.typeclasses import Type3ClassMethod
|
from .type3.typeclasses import Type3ClassMethod
|
||||||
from .type3.types import Type3, TypeApplication_Struct
|
from .type3.types import Type3, TypeApplication_Struct
|
||||||
@ -288,14 +288,14 @@ class Function:
|
|||||||
returns_type3: Type3
|
returns_type3: Type3
|
||||||
posonlyargs: List[FunctionParam]
|
posonlyargs: List[FunctionParam]
|
||||||
|
|
||||||
def __init__(self, name: str, lineno: int) -> None:
|
def __init__(self, name: str, lineno: int, returns_type3: Type3) -> None:
|
||||||
self.name = name
|
self.name = name
|
||||||
self.lineno = lineno
|
self.lineno = lineno
|
||||||
self.exported = False
|
self.exported = False
|
||||||
self.imported = None
|
self.imported = None
|
||||||
self.statements = []
|
self.statements = []
|
||||||
self.signature = FunctionSignature(TypeVariableContext(), [])
|
self.signature = FunctionSignature(TypeVariableContext(), [])
|
||||||
self.returns_type3 = prelude.none # FIXME: This could be a placeholder
|
self.returns_type3 = returns_type3
|
||||||
self.posonlyargs = []
|
self.posonlyargs = []
|
||||||
|
|
||||||
class StructDefinition:
|
class StructDefinition:
|
||||||
@ -323,7 +323,7 @@ class StructConstructor(Function):
|
|||||||
struct_type3: Type3
|
struct_type3: Type3
|
||||||
|
|
||||||
def __init__(self, struct_type3: Type3) -> None:
|
def __init__(self, struct_type3: Type3) -> None:
|
||||||
super().__init__(f'@{struct_type3.name}@__init___@', -1)
|
super().__init__(f'@{struct_type3.name}@__init___@', -1, struct_type3)
|
||||||
|
|
||||||
assert isinstance(struct_type3.application, TypeApplication_Struct)
|
assert isinstance(struct_type3.application, TypeApplication_Struct)
|
||||||
|
|
||||||
@ -331,7 +331,6 @@ class StructConstructor(Function):
|
|||||||
self.posonlyargs.append(FunctionParam(mem, typ, ))
|
self.posonlyargs.append(FunctionParam(mem, typ, ))
|
||||||
self.signature.args.append(typ)
|
self.signature.args.append(typ)
|
||||||
|
|
||||||
self.returns_type3 = struct_type3
|
|
||||||
self.signature.args.append(struct_type3)
|
self.signature.args.append(struct_type3)
|
||||||
|
|
||||||
self.struct_type3 = struct_type3
|
self.struct_type3 = struct_type3
|
||||||
@ -366,6 +365,9 @@ class ModuleDataBlock:
|
|||||||
self.data = [*data]
|
self.data = [*data]
|
||||||
self.address = None
|
self.address = None
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f'ModuleDataBlock({self.data!r}, {self.address!r})'
|
||||||
|
|
||||||
class ModuleData:
|
class ModuleData:
|
||||||
"""
|
"""
|
||||||
The data for when a module is loaded into memory
|
The data for when a module is loaded into memory
|
||||||
@ -377,25 +379,30 @@ class ModuleData:
|
|||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.blocks = []
|
self.blocks = []
|
||||||
|
|
||||||
class Module:
|
class Module[G]:
|
||||||
"""
|
"""
|
||||||
A module is a file and consists of functions
|
A module is a file and consists of functions
|
||||||
"""
|
"""
|
||||||
__slots__ = ('data', 'types', 'struct_definitions', 'constant_defs', 'functions', 'operators', 'functions_table', )
|
__slots__ = ('build', 'data', 'types', 'struct_definitions', 'constant_defs', 'functions', 'methods', 'operators', 'functions_table', )
|
||||||
|
|
||||||
|
build: BuildBase[G]
|
||||||
data: ModuleData
|
data: ModuleData
|
||||||
types: dict[str, Type3]
|
types: dict[str, Type3]
|
||||||
struct_definitions: Dict[str, StructDefinition]
|
struct_definitions: Dict[str, StructDefinition]
|
||||||
constant_defs: Dict[str, ModuleConstantDef]
|
constant_defs: Dict[str, ModuleConstantDef]
|
||||||
functions: Dict[str, Function]
|
functions: Dict[str, Function]
|
||||||
|
methods: Dict[str, Type3ClassMethod]
|
||||||
operators: Dict[str, Type3ClassMethod]
|
operators: Dict[str, Type3ClassMethod]
|
||||||
functions_table: dict[Function, int]
|
functions_table: dict[Function, int]
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self, build: BuildBase[G]) -> None:
|
||||||
|
self.build = build
|
||||||
|
|
||||||
self.data = ModuleData()
|
self.data = ModuleData()
|
||||||
self.types = {}
|
self.types = {}
|
||||||
self.struct_definitions = {}
|
self.struct_definitions = {}
|
||||||
self.constant_defs = {}
|
self.constant_defs = {}
|
||||||
self.functions = {}
|
self.functions = {}
|
||||||
|
self.methods = {}
|
||||||
self.operators = {}
|
self.operators = {}
|
||||||
self.functions_table = {}
|
self.functions_table = {}
|
||||||
|
|||||||
@ -4,7 +4,8 @@ Parses the source code from the plain text into a syntax tree
|
|||||||
import ast
|
import ast
|
||||||
from typing import Any, Dict, NoReturn, Union
|
from typing import Any, Dict, NoReturn, Union
|
||||||
|
|
||||||
from . import prelude
|
from .build.base import BuildBase
|
||||||
|
from .build.default import BuildDefault
|
||||||
from .exceptions import StaticError
|
from .exceptions import StaticError
|
||||||
from .ourlang import (
|
from .ourlang import (
|
||||||
AccessStructMember,
|
AccessStructMember,
|
||||||
@ -31,12 +32,12 @@ from .ourlang import (
|
|||||||
TupleInstantiation,
|
TupleInstantiation,
|
||||||
VariableReference,
|
VariableReference,
|
||||||
)
|
)
|
||||||
from .prelude import PRELUDE_METHODS, PRELUDE_OPERATORS, PRELUDE_TYPES
|
|
||||||
from .type3.typeclasses import Type3ClassMethod
|
from .type3.typeclasses import Type3ClassMethod
|
||||||
from .type3.types import IntType3, Type3
|
from .type3.types import IntType3, Type3
|
||||||
|
from .wasmgenerator import Generator
|
||||||
|
|
||||||
|
|
||||||
def phasm_parse(source: str) -> Module:
|
def phasm_parse(source: str) -> Module[Generator]:
|
||||||
"""
|
"""
|
||||||
Public method for parsing Phasm code into a Phasm Module
|
Public method for parsing Phasm code into a Phasm Module
|
||||||
"""
|
"""
|
||||||
@ -44,7 +45,8 @@ def phasm_parse(source: str) -> Module:
|
|||||||
|
|
||||||
res = OptimizerTransformer().visit(res)
|
res = OptimizerTransformer().visit(res)
|
||||||
|
|
||||||
our_visitor = OurVisitor()
|
build = BuildDefault()
|
||||||
|
our_visitor = OurVisitor(build)
|
||||||
return our_visitor.visit_Module(res)
|
return our_visitor.visit_Module(res)
|
||||||
|
|
||||||
OurLocals = Dict[str, Union[FunctionParam]] # FIXME: Does it become easier if we add ModuleConstantDef to this dict?
|
OurLocals = Dict[str, Union[FunctionParam]] # FIXME: Does it become easier if we add ModuleConstantDef to this dict?
|
||||||
@ -75,7 +77,7 @@ class OptimizerTransformer(ast.NodeTransformer):
|
|||||||
return node.operand
|
return node.operand
|
||||||
return node
|
return node
|
||||||
|
|
||||||
class OurVisitor:
|
class OurVisitor[G]:
|
||||||
"""
|
"""
|
||||||
Class to visit a Python syntax tree and create an ourlang syntax tree
|
Class to visit a Python syntax tree and create an ourlang syntax tree
|
||||||
|
|
||||||
@ -90,14 +92,15 @@ class OurVisitor:
|
|||||||
|
|
||||||
# pylint: disable=C0103,C0116,C0301,R0201,R0912
|
# pylint: disable=C0103,C0116,C0301,R0201,R0912
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self, build: BuildBase[G]) -> None:
|
||||||
pass
|
self.build = build
|
||||||
|
|
||||||
def visit_Module(self, node: ast.Module) -> Module:
|
def visit_Module(self, node: ast.Module) -> Module[G]:
|
||||||
module = Module()
|
module = Module(self.build)
|
||||||
|
|
||||||
module.operators.update(PRELUDE_OPERATORS)
|
module.methods.update(self.build.methods)
|
||||||
module.types.update(PRELUDE_TYPES)
|
module.operators.update(self.build.operators)
|
||||||
|
module.types.update(self.build.types)
|
||||||
|
|
||||||
_not_implemented(not node.type_ignores, 'Module.type_ignores')
|
_not_implemented(not node.type_ignores, 'Module.type_ignores')
|
||||||
|
|
||||||
@ -140,7 +143,7 @@ class OurVisitor:
|
|||||||
|
|
||||||
return module
|
return module
|
||||||
|
|
||||||
def pre_visit_Module_stmt(self, module: Module, node: ast.stmt) -> Union[Function, StructDefinition, ModuleConstantDef]:
|
def pre_visit_Module_stmt(self, module: Module[G], node: ast.stmt) -> Union[Function, StructDefinition, ModuleConstantDef]:
|
||||||
if isinstance(node, ast.FunctionDef):
|
if isinstance(node, ast.FunctionDef):
|
||||||
return self.pre_visit_Module_FunctionDef(module, node)
|
return self.pre_visit_Module_FunctionDef(module, node)
|
||||||
|
|
||||||
@ -152,8 +155,8 @@ class OurVisitor:
|
|||||||
|
|
||||||
raise NotImplementedError(f'{node} on Module')
|
raise NotImplementedError(f'{node} on Module')
|
||||||
|
|
||||||
def pre_visit_Module_FunctionDef(self, module: Module, node: ast.FunctionDef) -> Function:
|
def pre_visit_Module_FunctionDef(self, module: Module[G], node: ast.FunctionDef) -> Function:
|
||||||
function = Function(node.name, node.lineno)
|
function = Function(node.name, node.lineno, self.build.none_)
|
||||||
|
|
||||||
_not_implemented(not node.args.posonlyargs, 'FunctionDef.args.posonlyargs')
|
_not_implemented(not node.args.posonlyargs, 'FunctionDef.args.posonlyargs')
|
||||||
|
|
||||||
@ -220,7 +223,7 @@ class OurVisitor:
|
|||||||
|
|
||||||
return function
|
return function
|
||||||
|
|
||||||
def pre_visit_Module_ClassDef(self, module: Module, node: ast.ClassDef) -> StructDefinition:
|
def pre_visit_Module_ClassDef(self, module: Module[G], node: ast.ClassDef) -> StructDefinition:
|
||||||
|
|
||||||
_not_implemented(not node.bases, 'ClassDef.bases')
|
_not_implemented(not node.bases, 'ClassDef.bases')
|
||||||
_not_implemented(not node.keywords, 'ClassDef.keywords')
|
_not_implemented(not node.keywords, 'ClassDef.keywords')
|
||||||
@ -246,9 +249,9 @@ class OurVisitor:
|
|||||||
|
|
||||||
members[stmt.target.id] = self.visit_type(module, stmt.annotation)
|
members[stmt.target.id] = self.visit_type(module, stmt.annotation)
|
||||||
|
|
||||||
return StructDefinition(prelude.struct(node.name, tuple(members.items())), node.lineno)
|
return StructDefinition(module.build.struct(node.name, tuple(members.items())), node.lineno)
|
||||||
|
|
||||||
def pre_visit_Module_AnnAssign(self, module: Module, node: ast.AnnAssign) -> ModuleConstantDef:
|
def pre_visit_Module_AnnAssign(self, module: Module[G], node: ast.AnnAssign) -> ModuleConstantDef:
|
||||||
if not isinstance(node.target, ast.Name):
|
if not isinstance(node.target, ast.Name):
|
||||||
_raise_static_error(node.target, 'Must be name')
|
_raise_static_error(node.target, 'Must be name')
|
||||||
if not isinstance(node.target.ctx, ast.Store):
|
if not isinstance(node.target.ctx, ast.Store):
|
||||||
@ -292,7 +295,7 @@ class OurVisitor:
|
|||||||
|
|
||||||
raise NotImplementedError(f'{node} on Module AnnAssign')
|
raise NotImplementedError(f'{node} on Module AnnAssign')
|
||||||
|
|
||||||
def visit_Module_stmt(self, module: Module, node: ast.stmt) -> None:
|
def visit_Module_stmt(self, module: Module[G], node: ast.stmt) -> None:
|
||||||
if isinstance(node, ast.FunctionDef):
|
if isinstance(node, ast.FunctionDef):
|
||||||
self.visit_Module_FunctionDef(module, node)
|
self.visit_Module_FunctionDef(module, node)
|
||||||
return
|
return
|
||||||
@ -305,7 +308,7 @@ class OurVisitor:
|
|||||||
|
|
||||||
raise NotImplementedError(f'{node} on Module')
|
raise NotImplementedError(f'{node} on Module')
|
||||||
|
|
||||||
def visit_Module_FunctionDef(self, module: Module, node: ast.FunctionDef) -> None:
|
def visit_Module_FunctionDef(self, module: Module[G], node: ast.FunctionDef) -> None:
|
||||||
function = module.functions[node.name]
|
function = module.functions[node.name]
|
||||||
|
|
||||||
our_locals: OurLocals = {
|
our_locals: OurLocals = {
|
||||||
@ -318,7 +321,7 @@ class OurVisitor:
|
|||||||
self.visit_Module_FunctionDef_stmt(module, function, our_locals, stmt)
|
self.visit_Module_FunctionDef_stmt(module, function, our_locals, stmt)
|
||||||
)
|
)
|
||||||
|
|
||||||
def visit_Module_FunctionDef_stmt(self, module: Module, function: Function, our_locals: OurLocals, node: ast.stmt) -> Statement:
|
def visit_Module_FunctionDef_stmt(self, module: Module[G], function: Function, our_locals: OurLocals, node: ast.stmt) -> Statement:
|
||||||
if isinstance(node, ast.Return):
|
if isinstance(node, ast.Return):
|
||||||
if node.value is None:
|
if node.value is None:
|
||||||
# TODO: Implement methods without return values
|
# TODO: Implement methods without return values
|
||||||
@ -350,7 +353,7 @@ class OurVisitor:
|
|||||||
|
|
||||||
raise NotImplementedError(f'{node} as stmt in FunctionDef')
|
raise NotImplementedError(f'{node} as stmt in FunctionDef')
|
||||||
|
|
||||||
def visit_Module_FunctionDef_expr(self, module: Module, function: Function, our_locals: OurLocals, node: ast.expr) -> Expression:
|
def visit_Module_FunctionDef_expr(self, module: Module[G], function: Function, our_locals: OurLocals, node: ast.expr) -> Expression:
|
||||||
if isinstance(node, ast.BinOp):
|
if isinstance(node, ast.BinOp):
|
||||||
operator: Union[str, Type3ClassMethod]
|
operator: Union[str, Type3ClassMethod]
|
||||||
|
|
||||||
@ -466,7 +469,7 @@ class OurVisitor:
|
|||||||
|
|
||||||
raise NotImplementedError(f'{node} as expr in FunctionDef')
|
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]:
|
def visit_Module_FunctionDef_Call(self, module: Module[G], function: Function, our_locals: OurLocals, node: ast.Call) -> Union[FunctionCall]:
|
||||||
if node.keywords:
|
if node.keywords:
|
||||||
_raise_static_error(node, 'Keyword calling not supported') # Yet?
|
_raise_static_error(node, 'Keyword calling not supported') # Yet?
|
||||||
|
|
||||||
@ -477,8 +480,8 @@ class OurVisitor:
|
|||||||
|
|
||||||
func: Union[Function, FunctionParam, Type3ClassMethod]
|
func: Union[Function, FunctionParam, Type3ClassMethod]
|
||||||
|
|
||||||
if node.func.id in PRELUDE_METHODS:
|
if node.func.id in module.methods:
|
||||||
func = PRELUDE_METHODS[node.func.id]
|
func = module.methods[node.func.id]
|
||||||
elif node.func.id in our_locals:
|
elif node.func.id in our_locals:
|
||||||
func = our_locals[node.func.id]
|
func = our_locals[node.func.id]
|
||||||
else:
|
else:
|
||||||
@ -494,7 +497,7 @@ class OurVisitor:
|
|||||||
)
|
)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def visit_Module_FunctionDef_Attribute(self, module: Module, function: Function, our_locals: OurLocals, node: ast.Attribute) -> Expression:
|
def visit_Module_FunctionDef_Attribute(self, module: Module[G], function: Function, our_locals: OurLocals, node: ast.Attribute) -> Expression:
|
||||||
if not isinstance(node.value, ast.Name):
|
if not isinstance(node.value, ast.Name):
|
||||||
_raise_static_error(node, 'Must reference a name')
|
_raise_static_error(node, 'Must reference a name')
|
||||||
|
|
||||||
@ -511,7 +514,7 @@ class OurVisitor:
|
|||||||
node.attr,
|
node.attr,
|
||||||
)
|
)
|
||||||
|
|
||||||
def visit_Module_FunctionDef_Subscript(self, module: Module, function: Function, our_locals: OurLocals, node: ast.Subscript) -> Expression:
|
def visit_Module_FunctionDef_Subscript(self, module: Module[G], function: Function, our_locals: OurLocals, node: ast.Subscript) -> Expression:
|
||||||
if not isinstance(node.value, ast.Name):
|
if not isinstance(node.value, ast.Name):
|
||||||
_raise_static_error(node, 'Must reference a name')
|
_raise_static_error(node, 'Must reference a name')
|
||||||
|
|
||||||
@ -537,7 +540,7 @@ class OurVisitor:
|
|||||||
|
|
||||||
return Subscript(varref, slice_expr)
|
return Subscript(varref, slice_expr)
|
||||||
|
|
||||||
def visit_Module_Constant(self, module: Module, node: Union[ast.Constant, ast.Tuple, ast.Call]) -> Union[ConstantPrimitive, ConstantBytes, ConstantTuple, ConstantStruct]:
|
def visit_Module_Constant(self, module: Module[G], node: Union[ast.Constant, ast.Tuple, ast.Call]) -> Union[ConstantPrimitive, ConstantBytes, ConstantTuple, ConstantStruct]:
|
||||||
if isinstance(node, ast.Tuple):
|
if isinstance(node, ast.Tuple):
|
||||||
tuple_data = [
|
tuple_data = [
|
||||||
self.visit_Module_Constant(module, arg_node)
|
self.visit_Module_Constant(module, arg_node)
|
||||||
@ -597,10 +600,10 @@ class OurVisitor:
|
|||||||
|
|
||||||
raise NotImplementedError(f'{node.value} as constant')
|
raise NotImplementedError(f'{node.value} as constant')
|
||||||
|
|
||||||
def visit_type(self, module: Module, node: ast.expr) -> Type3:
|
def visit_type(self, module: Module[G], node: ast.expr) -> Type3:
|
||||||
if isinstance(node, ast.Constant):
|
if isinstance(node, ast.Constant):
|
||||||
if node.value is None:
|
if node.value is None:
|
||||||
return prelude.none
|
return module.types['None']
|
||||||
|
|
||||||
_raise_static_error(node, f'Unrecognized type {node.value}')
|
_raise_static_error(node, f'Unrecognized type {node.value}')
|
||||||
|
|
||||||
@ -625,7 +628,7 @@ class OurVisitor:
|
|||||||
_raise_static_error(node, 'Must subscript using a list of types')
|
_raise_static_error(node, 'Must subscript using a list of types')
|
||||||
|
|
||||||
# Function type
|
# Function type
|
||||||
return prelude.function(*[
|
return module.build.function(*[
|
||||||
self.visit_type(module, e)
|
self.visit_type(module, e)
|
||||||
for e in func_arg_types
|
for e in func_arg_types
|
||||||
])
|
])
|
||||||
@ -637,7 +640,7 @@ class OurVisitor:
|
|||||||
_raise_static_error(node, 'Must subscript using a constant index')
|
_raise_static_error(node, 'Must subscript using a constant index')
|
||||||
|
|
||||||
if node.slice.value is Ellipsis:
|
if node.slice.value is Ellipsis:
|
||||||
return prelude.dynamic_array(
|
return module.build.dynamic_array(
|
||||||
self.visit_type(module, node.value),
|
self.visit_type(module, node.value),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -646,7 +649,7 @@ class OurVisitor:
|
|||||||
if not isinstance(node.ctx, ast.Load):
|
if not isinstance(node.ctx, ast.Load):
|
||||||
_raise_static_error(node, 'Must be load context')
|
_raise_static_error(node, 'Must be load context')
|
||||||
|
|
||||||
return prelude.static_array(
|
return module.build.static_array(
|
||||||
self.visit_type(module, node.value),
|
self.visit_type(module, node.value),
|
||||||
IntType3(node.slice.value),
|
IntType3(node.slice.value),
|
||||||
)
|
)
|
||||||
@ -655,7 +658,7 @@ class OurVisitor:
|
|||||||
if not isinstance(node.ctx, ast.Load):
|
if not isinstance(node.ctx, ast.Load):
|
||||||
_raise_static_error(node, 'Must be load context')
|
_raise_static_error(node, 'Must be load context')
|
||||||
|
|
||||||
return prelude.tuple_(
|
return module.build.tuple_(
|
||||||
*(self.visit_type(module, elt) for elt in node.elts)
|
*(self.visit_type(module, elt) for elt in node.elts)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -1,772 +0,0 @@
|
|||||||
"""
|
|
||||||
The prelude are all the builtin types, type classes and methods
|
|
||||||
"""
|
|
||||||
from typing import Any, Callable
|
|
||||||
from warnings import warn
|
|
||||||
|
|
||||||
from phasm.stdlib import types as stdtypes
|
|
||||||
from phasm.wasmgenerator import Generator
|
|
||||||
|
|
||||||
from ..type3.functions import (
|
|
||||||
Constraint_TypeClassInstanceExists,
|
|
||||||
TypeConstructorVariable,
|
|
||||||
TypeVariable,
|
|
||||||
TypeVariableApplication_Nullary,
|
|
||||||
)
|
|
||||||
from ..type3.routers import TypeClassArgsRouter, TypeVariableLookup
|
|
||||||
from ..type3.typeclasses import Type3Class, Type3ClassMethod
|
|
||||||
from ..type3.types import (
|
|
||||||
Type3,
|
|
||||||
TypeApplication_Nullary,
|
|
||||||
TypeConstructor_Base,
|
|
||||||
TypeConstructor_DynamicArray,
|
|
||||||
TypeConstructor_Function,
|
|
||||||
TypeConstructor_StaticArray,
|
|
||||||
TypeConstructor_Struct,
|
|
||||||
TypeConstructor_Tuple,
|
|
||||||
)
|
|
||||||
|
|
||||||
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', TypeApplication_Nullary(None, None))
|
|
||||||
"""
|
|
||||||
The bool type, either True or False
|
|
||||||
|
|
||||||
Suffixes with an underscores, as it's a Python builtin
|
|
||||||
"""
|
|
||||||
|
|
||||||
u8 = Type3('u8', TypeApplication_Nullary(None, None))
|
|
||||||
"""
|
|
||||||
The unsigned 8-bit integer type.
|
|
||||||
|
|
||||||
Operations on variables employ modular arithmetic, with modulus 2^8.
|
|
||||||
"""
|
|
||||||
|
|
||||||
u16 = Type3('u16', TypeApplication_Nullary(None, None))
|
|
||||||
"""
|
|
||||||
The unsigned 16-bit integer type.
|
|
||||||
|
|
||||||
Operations on variables employ modular arithmetic, with modulus 2^16.
|
|
||||||
"""
|
|
||||||
|
|
||||||
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', TypeApplication_Nullary(None, None))
|
|
||||||
"""
|
|
||||||
The unsigned 64-bit integer type.
|
|
||||||
|
|
||||||
Operations on variables employ modular arithmetic, with modulus 2^64.
|
|
||||||
"""
|
|
||||||
|
|
||||||
i8 = Type3('i8', TypeApplication_Nullary(None, None))
|
|
||||||
"""
|
|
||||||
The signed 8-bit integer type.
|
|
||||||
|
|
||||||
Operations on variables employ modular arithmetic, with modulus 2^8, but
|
|
||||||
with the middel point being 0.
|
|
||||||
"""
|
|
||||||
|
|
||||||
i16 = Type3('i16', TypeApplication_Nullary(None, None))
|
|
||||||
"""
|
|
||||||
The signed 16-bit integer type.
|
|
||||||
|
|
||||||
Operations on variables employ modular arithmetic, with modulus 2^16, but
|
|
||||||
with the middel point being 0.
|
|
||||||
"""
|
|
||||||
|
|
||||||
i32 = Type3('i32', TypeApplication_Nullary(None, None))
|
|
||||||
"""
|
|
||||||
The unsigned 32-bit integer type.
|
|
||||||
|
|
||||||
Operations on variables employ modular arithmetic, with modulus 2^32, but
|
|
||||||
with the middel point being 0.
|
|
||||||
"""
|
|
||||||
|
|
||||||
i64 = Type3('i64', TypeApplication_Nullary(None, None))
|
|
||||||
"""
|
|
||||||
The unsigned 64-bit integer type.
|
|
||||||
|
|
||||||
Operations on variables employ modular arithmetic, with modulus 2^64, but
|
|
||||||
with the middel point being 0.
|
|
||||||
"""
|
|
||||||
|
|
||||||
f32 = Type3('f32', TypeApplication_Nullary(None, None))
|
|
||||||
"""
|
|
||||||
A 32-bits IEEE 754 float, of 32 bits width.
|
|
||||||
"""
|
|
||||||
|
|
||||||
f64 = Type3('f64', TypeApplication_Nullary(None, None))
|
|
||||||
"""
|
|
||||||
A 32-bits IEEE 754 float, of 64 bits width.
|
|
||||||
"""
|
|
||||||
|
|
||||||
dynamic_array = TypeConstructor_DynamicArray('dynamic_array')
|
|
||||||
"""
|
|
||||||
This is a dynamic length piece of memory.
|
|
||||||
|
|
||||||
It should be applied with two arguments. It has a runtime
|
|
||||||
determined length, and each argument is the same.
|
|
||||||
"""
|
|
||||||
|
|
||||||
static_array = TypeConstructor_StaticArray('static_array')
|
|
||||||
"""
|
|
||||||
This is a fixed length piece of memory.
|
|
||||||
|
|
||||||
It should be applied with two arguments. It has a compile time
|
|
||||||
determined length, and each argument is the same.
|
|
||||||
"""
|
|
||||||
|
|
||||||
tuple_ = TypeConstructor_Tuple('tuple')
|
|
||||||
"""
|
|
||||||
This is a fixed length piece of memory.
|
|
||||||
|
|
||||||
It should be applied with zero or more arguments. It has a compile time
|
|
||||||
determined length, and each argument can be different.
|
|
||||||
"""
|
|
||||||
|
|
||||||
function = TypeConstructor_Function('function')
|
|
||||||
"""
|
|
||||||
This is a function.
|
|
||||||
|
|
||||||
It should be applied with one or more arguments. The last argument is the 'return' type.
|
|
||||||
"""
|
|
||||||
|
|
||||||
struct = TypeConstructor_Struct('struct')
|
|
||||||
"""
|
|
||||||
This is like a tuple, but each argument is named, so that developers
|
|
||||||
can get and set fields by name.
|
|
||||||
"""
|
|
||||||
|
|
||||||
a = TypeVariable('a', TypeVariableApplication_Nullary(None, None))
|
|
||||||
b = TypeVariable('b', TypeVariableApplication_Nullary(None, None))
|
|
||||||
|
|
||||||
t = TypeConstructorVariable('t')
|
|
||||||
|
|
||||||
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, operators={
|
|
||||||
'==': stdtypes.u8_eq_equals,
|
|
||||||
'!=': stdtypes.u8_eq_not_equals,
|
|
||||||
})
|
|
||||||
instance_type_class(Eq, u16, operators={
|
|
||||||
'==': stdtypes.u16_eq_equals,
|
|
||||||
'!=': stdtypes.u16_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, i16, operators={
|
|
||||||
'==': stdtypes.i16_eq_equals,
|
|
||||||
'!=': stdtypes.i16_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={
|
|
||||||
'min': [a, a, a],
|
|
||||||
'max': [a, a, a],
|
|
||||||
}, operators={
|
|
||||||
'<': [a, a, bool_],
|
|
||||||
'<=': [a, a, bool_],
|
|
||||||
'>': [a, a, bool_],
|
|
||||||
'>=': [a, a, bool_],
|
|
||||||
}, inherited_classes=[Eq])
|
|
||||||
|
|
||||||
instance_type_class(Ord, u8, 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, u16, methods={
|
|
||||||
'min': stdtypes.u16_ord_min,
|
|
||||||
'max': stdtypes.u16_ord_max,
|
|
||||||
}, operators={
|
|
||||||
'<': stdtypes.u16_ord_less_than,
|
|
||||||
'<=': stdtypes.u16_ord_less_than_or_equal,
|
|
||||||
'>': stdtypes.u16_ord_greater_than,
|
|
||||||
'>=': stdtypes.u16_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, i16, methods={
|
|
||||||
'min': stdtypes.i16_ord_min,
|
|
||||||
'max': stdtypes.i16_ord_max,
|
|
||||||
}, operators={
|
|
||||||
'<': stdtypes.i16_ord_less_than,
|
|
||||||
'<=': stdtypes.i16_ord_less_than_or_equal,
|
|
||||||
'>': stdtypes.i16_ord_greater_than,
|
|
||||||
'>=': stdtypes.i16_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={
|
|
||||||
'shl': [a, u32, a], # Logical shift left
|
|
||||||
'shr': [a, u32, a], # Logical shift right
|
|
||||||
'rotl': [a, u32, a], # Rotate bits left
|
|
||||||
'rotr': [a, u32, a], # Rotate bits right
|
|
||||||
# FIXME: Do we want to expose clz, ctz, popcnt?
|
|
||||||
}, operators={
|
|
||||||
'&': [a, a, a], # Bit-wise and
|
|
||||||
'|': [a, a, a], # Bit-wise or
|
|
||||||
'^': [a, a, a], # Bit-wise xor
|
|
||||||
})
|
|
||||||
|
|
||||||
instance_type_class(Bits, u8, 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, u16, methods={
|
|
||||||
'shl': stdtypes.u16_bits_logical_shift_left,
|
|
||||||
'shr': stdtypes.u16_bits_logical_shift_right,
|
|
||||||
'rotl': stdtypes.u16_bits_rotate_left,
|
|
||||||
'rotr': stdtypes.u16_bits_rotate_right,
|
|
||||||
}, operators={
|
|
||||||
'&': stdtypes.u16_bits_bitwise_and,
|
|
||||||
'|': stdtypes.u16_bits_bitwise_or,
|
|
||||||
'^': stdtypes.u16_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={
|
|
||||||
'+': [a, a, a],
|
|
||||||
'-': [a, a, a],
|
|
||||||
'*': [a, a, a],
|
|
||||||
'<<': [a, u32, a], # Arithmic shift left
|
|
||||||
'>>': [a, u32, a], # Arithmic shift right
|
|
||||||
})
|
|
||||||
|
|
||||||
instance_type_class(NatNum, u32, 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={
|
|
||||||
'abs': [a, a],
|
|
||||||
'neg': [a, a],
|
|
||||||
}, operators={}, inherited_classes=[NatNum])
|
|
||||||
|
|
||||||
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('Integral', (a, ), methods={
|
|
||||||
}, operators={
|
|
||||||
'//': [a, a, a],
|
|
||||||
'%': [a, a, a],
|
|
||||||
}, inherited_classes=[NatNum])
|
|
||||||
|
|
||||||
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={
|
|
||||||
'ceil': [a, a],
|
|
||||||
'floor': [a, a],
|
|
||||||
'trunc': [a, a],
|
|
||||||
'nearest': [a, a],
|
|
||||||
}, operators={
|
|
||||||
'/': [a, a, a],
|
|
||||||
}, inherited_classes=[NatNum])
|
|
||||||
|
|
||||||
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={
|
|
||||||
'sqrt': [a, a],
|
|
||||||
}, operators={}, inherited_classes=[Fractional])
|
|
||||||
|
|
||||||
# FIXME: Do we want to expose copysign?
|
|
||||||
|
|
||||||
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', (t, ), methods={
|
|
||||||
'len': [t(a), u32],
|
|
||||||
}, operators={}) # FIXME: Once we get type class families, add [] here
|
|
||||||
|
|
||||||
instance_type_class(Sized_, dynamic_array, methods={
|
|
||||||
'len': stdtypes.dynamic_array_sized_len,
|
|
||||||
})
|
|
||||||
instance_type_class(Sized_, static_array, methods={
|
|
||||||
'len': stdtypes.static_array_sized_len,
|
|
||||||
})
|
|
||||||
|
|
||||||
Extendable = Type3Class('Extendable', (a, b, ), methods={
|
|
||||||
'extend': [a, b],
|
|
||||||
'wrap': [b, a],
|
|
||||||
}, operators={})
|
|
||||||
|
|
||||||
instance_type_class(Extendable, u8, u16, methods={
|
|
||||||
'extend': stdtypes.u8_u16_extend,
|
|
||||||
'wrap': stdtypes.u8_u16_wrap,
|
|
||||||
})
|
|
||||||
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, u16, u32, methods={
|
|
||||||
'extend': stdtypes.u16_u32_extend,
|
|
||||||
'wrap': stdtypes.u16_u32_wrap,
|
|
||||||
})
|
|
||||||
instance_type_class(Extendable, u16, u64, methods={
|
|
||||||
'extend': stdtypes.u16_u64_extend,
|
|
||||||
'wrap': stdtypes.u16_u64_wrap,
|
|
||||||
})
|
|
||||||
instance_type_class(Extendable, u32, u64, methods={
|
|
||||||
'extend': stdtypes.u32_u64_extend,
|
|
||||||
'wrap': stdtypes.u32_u64_wrap,
|
|
||||||
})
|
|
||||||
instance_type_class(Extendable, i8, i16, methods={
|
|
||||||
'extend': stdtypes.i8_i16_extend,
|
|
||||||
'wrap': stdtypes.i8_i16_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, i16, i32, methods={
|
|
||||||
'extend': stdtypes.i16_i32_extend,
|
|
||||||
'wrap': stdtypes.i16_i32_wrap,
|
|
||||||
})
|
|
||||||
instance_type_class(Extendable, i16, i64, methods={
|
|
||||||
'extend': stdtypes.i16_i64_extend,
|
|
||||||
'wrap': stdtypes.i16_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,
|
|
||||||
})
|
|
||||||
|
|
||||||
Reinterpretable = Type3Class('Reinterpretable', (a, b, ), methods={
|
|
||||||
'reinterpret': [a, b]
|
|
||||||
}, operators={})
|
|
||||||
|
|
||||||
instance_type_class(Reinterpretable, u32, f32, methods={
|
|
||||||
'reinterpret': stdtypes.u32_f32_reinterpret,
|
|
||||||
})
|
|
||||||
instance_type_class(Reinterpretable, u64, f64, methods={
|
|
||||||
'reinterpret': stdtypes.u64_f64_reinterpret,
|
|
||||||
})
|
|
||||||
instance_type_class(Reinterpretable, i32, f32, methods={
|
|
||||||
'reinterpret': stdtypes.i32_f32_reinterpret,
|
|
||||||
})
|
|
||||||
instance_type_class(Reinterpretable, i64, f64, methods={
|
|
||||||
'reinterpret': stdtypes.i64_f64_reinterpret,
|
|
||||||
})
|
|
||||||
instance_type_class(Reinterpretable, f32, u32, methods={
|
|
||||||
'reinterpret': stdtypes.f32_u32_reinterpret,
|
|
||||||
})
|
|
||||||
instance_type_class(Reinterpretable, f64, u64, methods={
|
|
||||||
'reinterpret': stdtypes.f64_u64_reinterpret,
|
|
||||||
})
|
|
||||||
instance_type_class(Reinterpretable, f32, i32, methods={
|
|
||||||
'reinterpret': stdtypes.f32_i32_reinterpret,
|
|
||||||
})
|
|
||||||
instance_type_class(Reinterpretable, f64, i64, methods={
|
|
||||||
'reinterpret': stdtypes.f64_i64_reinterpret,
|
|
||||||
})
|
|
||||||
|
|
||||||
Convertable = Type3Class('Convertable', (a, b, ), methods={
|
|
||||||
'convert': [a, b],
|
|
||||||
'truncate': [b, a], # To prevent name clas with Fractional
|
|
||||||
}, operators={})
|
|
||||||
|
|
||||||
instance_type_class(Convertable, u32, f32, methods={
|
|
||||||
'convert': stdtypes.u32_f32_convert,
|
|
||||||
'truncate': stdtypes.u32_f32_truncate,
|
|
||||||
})
|
|
||||||
instance_type_class(Convertable, u32, f64, methods={
|
|
||||||
'convert': stdtypes.u32_f64_convert,
|
|
||||||
'truncate': stdtypes.u32_f64_truncate,
|
|
||||||
})
|
|
||||||
instance_type_class(Convertable, u64, f32, methods={
|
|
||||||
'convert': stdtypes.u64_f32_convert,
|
|
||||||
'truncate': stdtypes.u64_f32_truncate,
|
|
||||||
})
|
|
||||||
instance_type_class(Convertable, u64, f64, methods={
|
|
||||||
'convert': stdtypes.u64_f64_convert,
|
|
||||||
'truncate': stdtypes.u64_f64_truncate,
|
|
||||||
})
|
|
||||||
instance_type_class(Convertable, i32, f32, methods={
|
|
||||||
'convert': stdtypes.i32_f32_convert,
|
|
||||||
'truncate': stdtypes.i32_f32_truncate,
|
|
||||||
})
|
|
||||||
instance_type_class(Convertable, i32, f64, methods={
|
|
||||||
'convert': stdtypes.i32_f64_convert,
|
|
||||||
'truncate': stdtypes.i32_f64_truncate,
|
|
||||||
})
|
|
||||||
instance_type_class(Convertable, i64, f32, methods={
|
|
||||||
'convert': stdtypes.i64_f32_convert,
|
|
||||||
'truncate': stdtypes.i64_f32_truncate,
|
|
||||||
})
|
|
||||||
instance_type_class(Convertable, i64, f64, methods={
|
|
||||||
'convert': stdtypes.i64_f64_convert,
|
|
||||||
'truncate': stdtypes.i64_f64_truncate,
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
Foldable = Type3Class('Foldable', (t, ), methods={
|
|
||||||
'sum': [t(a), a],
|
|
||||||
'foldl': [[b, a, b], b, t(a), b],
|
|
||||||
'foldr': [[a, b, b], b, t(a), b],
|
|
||||||
}, operators={}, additional_context={
|
|
||||||
'sum': [Constraint_TypeClassInstanceExists(NatNum, (a, ))],
|
|
||||||
})
|
|
||||||
|
|
||||||
instance_type_class(Foldable, dynamic_array, methods={
|
|
||||||
'sum': stdtypes.dynamic_array_sum,
|
|
||||||
'foldl': stdtypes.dynamic_array_foldl,
|
|
||||||
'foldr': stdtypes.dynamic_array_foldr,
|
|
||||||
})
|
|
||||||
instance_type_class(Foldable, static_array, methods={
|
|
||||||
'sum': stdtypes.static_array_sum,
|
|
||||||
'foldl': stdtypes.static_array_foldl,
|
|
||||||
'foldr': stdtypes.static_array_foldr,
|
|
||||||
})
|
|
||||||
|
|
||||||
bytes_ = dynamic_array(u8)
|
|
||||||
|
|
||||||
PRELUDE_TYPES: dict[str, Type3] = {
|
|
||||||
'none': none,
|
|
||||||
'bool': bool_,
|
|
||||||
'u8': u8,
|
|
||||||
'u16': u16,
|
|
||||||
'u32': u32,
|
|
||||||
'u64': u64,
|
|
||||||
'i8': i8,
|
|
||||||
'i16': i16,
|
|
||||||
'i32': i32,
|
|
||||||
'i64': i64,
|
|
||||||
'f32': f32,
|
|
||||||
'f64': f64,
|
|
||||||
'bytes': bytes_,
|
|
||||||
}
|
|
||||||
|
|
||||||
PRELUDE_TYPE_CLASSES = {
|
|
||||||
'Eq': Eq,
|
|
||||||
'Ord': Ord,
|
|
||||||
'Bits': Bits,
|
|
||||||
'NatNum': NatNum,
|
|
||||||
'IntNum': IntNum,
|
|
||||||
'Integral': Integral,
|
|
||||||
'Fractional': Fractional,
|
|
||||||
'Floating': Floating,
|
|
||||||
'Extendable': Extendable,
|
|
||||||
'Promotable': Promotable,
|
|
||||||
}
|
|
||||||
|
|
||||||
PRELUDE_OPERATORS = {
|
|
||||||
**Bits.operators,
|
|
||||||
**Eq.operators,
|
|
||||||
**Ord.operators,
|
|
||||||
**Fractional.operators,
|
|
||||||
**Integral.operators,
|
|
||||||
**IntNum.operators,
|
|
||||||
**NatNum.operators,
|
|
||||||
}
|
|
||||||
|
|
||||||
PRELUDE_METHODS = {
|
|
||||||
**Bits.methods,
|
|
||||||
**Eq.methods,
|
|
||||||
**Ord.methods,
|
|
||||||
**Floating.methods,
|
|
||||||
**Fractional.methods,
|
|
||||||
**Integral.methods,
|
|
||||||
**IntNum.methods,
|
|
||||||
**NatNum.methods,
|
|
||||||
**Sized_.methods,
|
|
||||||
**Extendable.methods,
|
|
||||||
**Promotable.methods,
|
|
||||||
**Reinterpretable.methods,
|
|
||||||
**Convertable.methods,
|
|
||||||
**Foldable.methods,
|
|
||||||
}
|
|
||||||
@ -1,62 +0,0 @@
|
|||||||
from . import prelude
|
|
||||||
from .stdlib.types import TYPE_INFO_CONSTRUCTED, TYPE_INFO_MAP
|
|
||||||
from .type3.routers import NoRouteForTypeException, TypeApplicationRouter
|
|
||||||
from .type3.types import IntType3, Type3
|
|
||||||
|
|
||||||
|
|
||||||
def calculate_alloc_size_static_array(is_member: bool, args: tuple[Type3, IntType3]) -> int:
|
|
||||||
if is_member:
|
|
||||||
return TYPE_INFO_CONSTRUCTED.alloc_size
|
|
||||||
|
|
||||||
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 TYPE_INFO_CONSTRUCTED.alloc_size
|
|
||||||
|
|
||||||
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 TYPE_INFO_CONSTRUCTED.alloc_size
|
|
||||||
|
|
||||||
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:
|
|
||||||
typ_info = TYPE_INFO_MAP.get(typ.name)
|
|
||||||
if typ_info is not None:
|
|
||||||
return typ_info.alloc_size
|
|
||||||
|
|
||||||
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 TYPE_INFO_CONSTRUCTED.alloc_size
|
|
||||||
|
|
||||||
raise NotImplementedError(typ)
|
|
||||||
|
|
||||||
def calculate_member_offset(st_name: str, st_args: tuple[tuple[str, Type3], ...], needle: str) -> int:
|
|
||||||
result = 0
|
|
||||||
|
|
||||||
for memnam, memtyp in st_args:
|
|
||||||
if needle == memnam:
|
|
||||||
return result
|
|
||||||
|
|
||||||
result += calculate_alloc_size(memtyp, is_member=True)
|
|
||||||
|
|
||||||
raise Exception(f'{needle} not in {st_name}')
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -5,7 +5,14 @@ These need to be resolved before the program can be compiled.
|
|||||||
"""
|
"""
|
||||||
from typing import Any, Dict, Iterable, List, Optional, Tuple, Union
|
from typing import Any, Dict, Iterable, List, Optional, Tuple, Union
|
||||||
|
|
||||||
from .. import ourlang, prelude
|
from .. import ourlang
|
||||||
|
from ..build.base import BuildBase
|
||||||
|
from ..wasm import (
|
||||||
|
WasmTypeFloat32,
|
||||||
|
WasmTypeFloat64,
|
||||||
|
WasmTypeInt32,
|
||||||
|
WasmTypeInt64,
|
||||||
|
)
|
||||||
from .functions import FunctionArgument, TypeVariable
|
from .functions import FunctionArgument, TypeVariable
|
||||||
from .placeholders import PlaceholderForType, Type3OrPlaceholder
|
from .placeholders import PlaceholderForType, Type3OrPlaceholder
|
||||||
from .routers import NoRouteForTypeException, TypeApplicationRouter
|
from .routers import NoRouteForTypeException, TypeApplicationRouter
|
||||||
@ -59,26 +66,31 @@ class Context:
|
|||||||
Context for constraints
|
Context for constraints
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = ('type_class_instances_existing', )
|
__slots__ = ('build', )
|
||||||
|
|
||||||
# Constraint_TypeClassInstanceExists
|
build: BuildBase[Any]
|
||||||
type_class_instances_existing: set[tuple[Type3Class, tuple[Union[Type3, TypeConstructor_Base[Any], TypeConstructor_Struct], ...]]]
|
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self, build: BuildBase[Any]) -> None:
|
||||||
self.type_class_instances_existing = set()
|
self.build = build
|
||||||
|
|
||||||
class ConstraintBase:
|
class ConstraintBase:
|
||||||
"""
|
"""
|
||||||
Base class for constraints
|
Base class for constraints
|
||||||
"""
|
"""
|
||||||
__slots__ = ('comment', )
|
__slots__ = ('context', 'comment', )
|
||||||
|
|
||||||
|
context: Context
|
||||||
|
"""
|
||||||
|
Additional information regarding the type environment
|
||||||
|
"""
|
||||||
|
|
||||||
comment: Optional[str]
|
comment: Optional[str]
|
||||||
"""
|
"""
|
||||||
A comment to help the programmer with debugging the types in their program
|
A comment to help the programmer with debugging the types in their program
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, comment: Optional[str] = None) -> None:
|
def __init__(self, context: Context, comment: Optional[str] = None) -> None:
|
||||||
|
self.context = context
|
||||||
self.comment = comment
|
self.comment = comment
|
||||||
|
|
||||||
def check(self) -> CheckResult:
|
def check(self) -> CheckResult:
|
||||||
@ -114,8 +126,8 @@ class SameTypeConstraint(ConstraintBase):
|
|||||||
|
|
||||||
type_list: List[Type3OrPlaceholder]
|
type_list: List[Type3OrPlaceholder]
|
||||||
|
|
||||||
def __init__(self, *type_list: Type3OrPlaceholder, comment: Optional[str] = None) -> None:
|
def __init__(self, context: Context, *type_list: Type3OrPlaceholder, comment: Optional[str] = None) -> None:
|
||||||
super().__init__(comment=comment)
|
super().__init__(context=context, comment=comment)
|
||||||
|
|
||||||
assert len(type_list) > 1
|
assert len(type_list) > 1
|
||||||
self.type_list = [*type_list]
|
self.type_list = [*type_list]
|
||||||
@ -176,8 +188,8 @@ class SameTypeArgumentConstraint(ConstraintBase):
|
|||||||
tc_var: PlaceholderForType
|
tc_var: PlaceholderForType
|
||||||
arg_var: PlaceholderForType
|
arg_var: PlaceholderForType
|
||||||
|
|
||||||
def __init__(self, tc_var: PlaceholderForType, arg_var: PlaceholderForType, *, comment: str) -> None:
|
def __init__(self, context: Context, tc_var: PlaceholderForType, arg_var: PlaceholderForType, *, comment: str) -> None:
|
||||||
super().__init__(comment=comment)
|
super().__init__(context=context, comment=comment)
|
||||||
|
|
||||||
self.tc_var = tc_var
|
self.tc_var = tc_var
|
||||||
self.arg_var = arg_var
|
self.arg_var = arg_var
|
||||||
@ -202,6 +214,7 @@ class SameTypeArgumentConstraint(ConstraintBase):
|
|||||||
|
|
||||||
if isinstance(tc_typ.application, TypeApplication_Type):
|
if isinstance(tc_typ.application, TypeApplication_Type):
|
||||||
return [SameTypeConstraint(
|
return [SameTypeConstraint(
|
||||||
|
self.context,
|
||||||
tc_typ.application.arguments[0],
|
tc_typ.application.arguments[0],
|
||||||
self.arg_var,
|
self.arg_var,
|
||||||
comment=self.comment,
|
comment=self.comment,
|
||||||
@ -211,6 +224,7 @@ class SameTypeArgumentConstraint(ConstraintBase):
|
|||||||
# have the exact same number as arguments?
|
# have the exact same number as arguments?
|
||||||
if isinstance(tc_typ.application, TypeApplication_TypeInt):
|
if isinstance(tc_typ.application, TypeApplication_TypeInt):
|
||||||
return [SameTypeConstraint(
|
return [SameTypeConstraint(
|
||||||
|
self.context,
|
||||||
tc_typ.application.arguments[0],
|
tc_typ.application.arguments[0],
|
||||||
self.arg_var,
|
self.arg_var,
|
||||||
comment=self.comment,
|
comment=self.comment,
|
||||||
@ -234,8 +248,8 @@ class SameFunctionArgumentConstraint(ConstraintBase):
|
|||||||
func_arg: FunctionArgument
|
func_arg: FunctionArgument
|
||||||
type_var_map: dict[TypeVariable, PlaceholderForType]
|
type_var_map: dict[TypeVariable, PlaceholderForType]
|
||||||
|
|
||||||
def __init__(self, type3: PlaceholderForType, func_arg: FunctionArgument, type_var_map: dict[TypeVariable, PlaceholderForType], *, comment: str) -> None:
|
def __init__(self, context: Context, type3: PlaceholderForType, func_arg: FunctionArgument, type_var_map: dict[TypeVariable, PlaceholderForType], *, comment: str) -> None:
|
||||||
super().__init__(comment=comment)
|
super().__init__(context=context, comment=comment)
|
||||||
|
|
||||||
self.type3 = type3
|
self.type3 = type3
|
||||||
self.func_arg = func_arg
|
self.func_arg = func_arg
|
||||||
@ -270,8 +284,9 @@ class SameFunctionArgumentConstraint(ConstraintBase):
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
SameTypeConstraint(
|
SameTypeConstraint(
|
||||||
|
self.context,
|
||||||
typ,
|
typ,
|
||||||
prelude.function(*exp_type_arg_list),
|
self.context.build.function(*exp_type_arg_list),
|
||||||
comment=self.comment,
|
comment=self.comment,
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
@ -286,22 +301,27 @@ class SameFunctionArgumentConstraint(ConstraintBase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
class TupleMatchConstraint(ConstraintBase):
|
class TupleMatchConstraint(ConstraintBase):
|
||||||
__slots__ = ('exp_type', 'args', )
|
__slots__ = ('exp_type', 'args', 'generate_router', )
|
||||||
|
|
||||||
exp_type: Type3OrPlaceholder
|
exp_type: Type3OrPlaceholder
|
||||||
args: list[Type3OrPlaceholder]
|
args: list[Type3OrPlaceholder]
|
||||||
|
generate_router: TypeApplicationRouter['TupleMatchConstraint', CheckResult]
|
||||||
|
|
||||||
def __init__(self, exp_type: Type3OrPlaceholder, args: Iterable[Type3OrPlaceholder], comment: str):
|
def __init__(self, context: Context, exp_type: Type3OrPlaceholder, args: Iterable[Type3OrPlaceholder], comment: str):
|
||||||
super().__init__(comment=comment)
|
super().__init__(context=context, comment=comment)
|
||||||
|
|
||||||
self.exp_type = exp_type
|
self.exp_type = exp_type
|
||||||
self.args = list(args)
|
self.args = list(args)
|
||||||
|
self.generate_router = TypeApplicationRouter()
|
||||||
|
self.generate_router.add(context.build.dynamic_array, self.__class__._generate_dynamic_array)
|
||||||
|
self.generate_router.add(context.build.static_array, self.__class__._generate_static_array)
|
||||||
|
self.generate_router.add(context.build.tuple_, self.__class__._generate_tuple)
|
||||||
|
|
||||||
def _generate_dynamic_array(self, sa_args: tuple[Type3]) -> CheckResult:
|
def _generate_dynamic_array(self, sa_args: tuple[Type3]) -> CheckResult:
|
||||||
sa_type, = sa_args
|
sa_type, = sa_args
|
||||||
|
|
||||||
return [
|
return [
|
||||||
SameTypeConstraint(arg, sa_type)
|
SameTypeConstraint(self.context, arg, sa_type)
|
||||||
for arg in self.args
|
for arg in self.args
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -312,7 +332,7 @@ class TupleMatchConstraint(ConstraintBase):
|
|||||||
return Error('Mismatch between applied types argument count', comment=self.comment)
|
return Error('Mismatch between applied types argument count', comment=self.comment)
|
||||||
|
|
||||||
return [
|
return [
|
||||||
SameTypeConstraint(arg, sa_type)
|
SameTypeConstraint(self.context, arg, sa_type)
|
||||||
for arg in self.args
|
for arg in self.args
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -321,15 +341,10 @@ class TupleMatchConstraint(ConstraintBase):
|
|||||||
return Error('Mismatch between applied types argument count', comment=self.comment)
|
return Error('Mismatch between applied types argument count', comment=self.comment)
|
||||||
|
|
||||||
return [
|
return [
|
||||||
SameTypeConstraint(arg, oth_arg)
|
SameTypeConstraint(self.context, arg, oth_arg)
|
||||||
for arg, oth_arg in zip(self.args, tp_args, strict=True)
|
for arg, oth_arg in zip(self.args, tp_args, strict=True)
|
||||||
]
|
]
|
||||||
|
|
||||||
GENERATE_ROUTER = TypeApplicationRouter['TupleMatchConstraint', CheckResult]()
|
|
||||||
GENERATE_ROUTER.add(prelude.dynamic_array, _generate_dynamic_array)
|
|
||||||
GENERATE_ROUTER.add(prelude.static_array, _generate_static_array)
|
|
||||||
GENERATE_ROUTER.add(prelude.tuple_, _generate_tuple)
|
|
||||||
|
|
||||||
def check(self) -> CheckResult:
|
def check(self) -> CheckResult:
|
||||||
exp_type = self.exp_type
|
exp_type = self.exp_type
|
||||||
if isinstance(exp_type, PlaceholderForType):
|
if isinstance(exp_type, PlaceholderForType):
|
||||||
@ -339,7 +354,7 @@ class TupleMatchConstraint(ConstraintBase):
|
|||||||
exp_type = exp_type.resolve_as
|
exp_type = exp_type.resolve_as
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return self.__class__.GENERATE_ROUTER(self, exp_type)
|
return self.generate_router(self, exp_type)
|
||||||
except NoRouteForTypeException:
|
except NoRouteForTypeException:
|
||||||
raise NotImplementedError(exp_type)
|
raise NotImplementedError(exp_type)
|
||||||
|
|
||||||
@ -347,16 +362,14 @@ class MustImplementTypeClassConstraint(ConstraintBase):
|
|||||||
"""
|
"""
|
||||||
A type must implement a given type class
|
A type must implement a given type class
|
||||||
"""
|
"""
|
||||||
__slots__ = ('context', 'type_class3', 'types', )
|
__slots__ = ('type_class3', 'types', )
|
||||||
|
|
||||||
context: Context
|
|
||||||
type_class3: Type3Class
|
type_class3: Type3Class
|
||||||
types: list[Type3OrPlaceholder]
|
types: list[Type3OrPlaceholder]
|
||||||
|
|
||||||
def __init__(self, context: Context, type_class3: Type3Class, typ_list: list[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)
|
super().__init__(context=context, comment=comment)
|
||||||
|
|
||||||
self.context = context
|
|
||||||
self.type_class3 = type_class3
|
self.type_class3 = type_class3
|
||||||
self.types = typ_list
|
self.types = typ_list
|
||||||
|
|
||||||
@ -382,7 +395,7 @@ class MustImplementTypeClassConstraint(ConstraintBase):
|
|||||||
assert len(typ_list) == len(self.types)
|
assert len(typ_list) == len(self.types)
|
||||||
|
|
||||||
key = (self.type_class3, tuple(typ_list), )
|
key = (self.type_class3, tuple(typ_list), )
|
||||||
if key in self.context.type_class_instances_existing:
|
if key in self.context.build.type_class_instances:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
typ_cls_name = self.type_class3 if isinstance(self.type_class3, str) else self.type_class3.name
|
typ_cls_name = self.type_class3 if isinstance(self.type_class3, str) else self.type_class3.name
|
||||||
@ -410,32 +423,46 @@ class LiteralFitsConstraint(ConstraintBase):
|
|||||||
"""
|
"""
|
||||||
A literal value fits a given type
|
A literal value fits a given type
|
||||||
"""
|
"""
|
||||||
__slots__ = ('type3', 'literal', )
|
__slots__ = ('type3', 'literal', 'generate_router', )
|
||||||
|
|
||||||
type3: Type3OrPlaceholder
|
type3: Type3OrPlaceholder
|
||||||
literal: Union[ourlang.ConstantPrimitive, ourlang.ConstantBytes, ourlang.ConstantTuple, ourlang.ConstantStruct]
|
literal: Union[ourlang.ConstantPrimitive, ourlang.ConstantBytes, ourlang.ConstantTuple, ourlang.ConstantStruct]
|
||||||
|
generate_router: TypeApplicationRouter['LiteralFitsConstraint', CheckResult]
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
context: Context,
|
||||||
type3: Type3OrPlaceholder,
|
type3: Type3OrPlaceholder,
|
||||||
literal: Union[ourlang.ConstantPrimitive, ourlang.ConstantBytes, ourlang.ConstantTuple, ourlang.ConstantStruct],
|
literal: Union[ourlang.ConstantPrimitive, ourlang.ConstantBytes, ourlang.ConstantTuple, ourlang.ConstantStruct],
|
||||||
comment: Optional[str] = None,
|
comment: Optional[str] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
super().__init__(comment=comment)
|
super().__init__(context=context, comment=comment)
|
||||||
|
|
||||||
self.type3 = type3
|
self.type3 = type3
|
||||||
self.literal = literal
|
self.literal = literal
|
||||||
|
|
||||||
|
self.generate_router = TypeApplicationRouter['LiteralFitsConstraint', CheckResult]()
|
||||||
|
self.generate_router.add(context.build.dynamic_array, self.__class__._generate_dynamic_array)
|
||||||
|
self.generate_router.add(context.build.static_array, self.__class__._generate_static_array)
|
||||||
|
self.generate_router.add(context.build.struct, self.__class__._generate_struct)
|
||||||
|
self.generate_router.add(context.build.tuple_, self.__class__._generate_tuple)
|
||||||
|
|
||||||
def _generate_dynamic_array(self, da_args: tuple[Type3]) -> CheckResult:
|
def _generate_dynamic_array(self, da_args: tuple[Type3]) -> CheckResult:
|
||||||
|
da_type, = da_args
|
||||||
|
|
||||||
|
if da_type.name == 'u8':
|
||||||
|
if not isinstance(self.literal, ourlang.ConstantBytes):
|
||||||
|
return Error('Must be bytes', comment=self.comment)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
if not isinstance(self.literal, ourlang.ConstantTuple):
|
if not isinstance(self.literal, ourlang.ConstantTuple):
|
||||||
return Error('Must be tuple', comment=self.comment)
|
return Error('Must be tuple', comment=self.comment)
|
||||||
|
|
||||||
da_type, = da_args
|
|
||||||
|
|
||||||
res: list[ConstraintBase] = []
|
res: list[ConstraintBase] = []
|
||||||
|
|
||||||
res.extend(
|
res.extend(
|
||||||
LiteralFitsConstraint(da_type, y)
|
LiteralFitsConstraint(self.context, da_type, y)
|
||||||
for y in self.literal.value
|
for y in self.literal.value
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -443,7 +470,7 @@ class LiteralFitsConstraint(ConstraintBase):
|
|||||||
# gets updated when we figure out the type of the
|
# gets updated when we figure out the type of the
|
||||||
# expression the literal is used in
|
# expression the literal is used in
|
||||||
res.extend(
|
res.extend(
|
||||||
SameTypeConstraint(da_type, PlaceholderForType([y]))
|
SameTypeConstraint(self.context, da_type, PlaceholderForType([y]))
|
||||||
for y in self.literal.value
|
for y in self.literal.value
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -461,7 +488,7 @@ class LiteralFitsConstraint(ConstraintBase):
|
|||||||
res: list[ConstraintBase] = []
|
res: list[ConstraintBase] = []
|
||||||
|
|
||||||
res.extend(
|
res.extend(
|
||||||
LiteralFitsConstraint(sa_type, y)
|
LiteralFitsConstraint(self.context, sa_type, y)
|
||||||
for y in self.literal.value
|
for y in self.literal.value
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -469,7 +496,7 @@ class LiteralFitsConstraint(ConstraintBase):
|
|||||||
# gets updated when we figure out the type of the
|
# gets updated when we figure out the type of the
|
||||||
# expression the literal is used in
|
# expression the literal is used in
|
||||||
res.extend(
|
res.extend(
|
||||||
SameTypeConstraint(sa_type, PlaceholderForType([y]))
|
SameTypeConstraint(self.context, sa_type, PlaceholderForType([y]))
|
||||||
for y in self.literal.value
|
for y in self.literal.value
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -485,7 +512,7 @@ class LiteralFitsConstraint(ConstraintBase):
|
|||||||
res: list[ConstraintBase] = []
|
res: list[ConstraintBase] = []
|
||||||
|
|
||||||
res.extend(
|
res.extend(
|
||||||
LiteralFitsConstraint(x, y)
|
LiteralFitsConstraint(self.context, x, y)
|
||||||
for (_, x), y in zip(st_args, self.literal.value, strict=True)
|
for (_, x), y in zip(st_args, self.literal.value, strict=True)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -493,11 +520,12 @@ class LiteralFitsConstraint(ConstraintBase):
|
|||||||
# gets updated when we figure out the type of the
|
# gets updated when we figure out the type of the
|
||||||
# expression the literal is used in
|
# expression the literal is used in
|
||||||
res.extend(
|
res.extend(
|
||||||
SameTypeConstraint(x_t, PlaceholderForType([y]), comment=f'{self.literal.struct_type3.name}.{x_n}')
|
SameTypeConstraint(self.context, 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)
|
for (x_n, x_t, ), y in zip(st_args, self.literal.value, strict=True)
|
||||||
)
|
)
|
||||||
|
|
||||||
res.append(SameTypeConstraint(
|
res.append(SameTypeConstraint(
|
||||||
|
self.context,
|
||||||
self.literal.struct_type3,
|
self.literal.struct_type3,
|
||||||
self.type3,
|
self.type3,
|
||||||
comment='Struct types must match',
|
comment='Struct types must match',
|
||||||
@ -515,7 +543,7 @@ class LiteralFitsConstraint(ConstraintBase):
|
|||||||
res: list[ConstraintBase] = []
|
res: list[ConstraintBase] = []
|
||||||
|
|
||||||
res.extend(
|
res.extend(
|
||||||
LiteralFitsConstraint(x, y)
|
LiteralFitsConstraint(self.context, x, y)
|
||||||
for x, y in zip(tp_args, self.literal.value, strict=True)
|
for x, y in zip(tp_args, self.literal.value, strict=True)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -523,57 +551,35 @@ class LiteralFitsConstraint(ConstraintBase):
|
|||||||
# gets updated when we figure out the type of the
|
# gets updated when we figure out the type of the
|
||||||
# expression the literal is used in
|
# expression the literal is used in
|
||||||
res.extend(
|
res.extend(
|
||||||
SameTypeConstraint(x, PlaceholderForType([y]))
|
SameTypeConstraint(self.context, x, PlaceholderForType([y]))
|
||||||
for x, y in zip(tp_args, self.literal.value, strict=True)
|
for x, y in zip(tp_args, self.literal.value, strict=True)
|
||||||
)
|
)
|
||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
GENERATE_ROUTER = TypeApplicationRouter['LiteralFitsConstraint', CheckResult]()
|
|
||||||
GENERATE_ROUTER.add(prelude.dynamic_array, _generate_dynamic_array)
|
|
||||||
GENERATE_ROUTER.add(prelude.static_array, _generate_static_array)
|
|
||||||
GENERATE_ROUTER.add(prelude.struct, _generate_struct)
|
|
||||||
GENERATE_ROUTER.add(prelude.tuple_, _generate_tuple)
|
|
||||||
|
|
||||||
def check(self) -> CheckResult:
|
def check(self) -> CheckResult:
|
||||||
int_table: Dict[str, Tuple[int, bool]] = {
|
|
||||||
'u8': (1, False),
|
|
||||||
'u16': (2, False),
|
|
||||||
'u32': (4, False),
|
|
||||||
'u64': (8, False),
|
|
||||||
'i8': (1, True),
|
|
||||||
'i16': (2, True),
|
|
||||||
'i32': (4, True),
|
|
||||||
'i64': (8, True),
|
|
||||||
}
|
|
||||||
|
|
||||||
float_table: Dict[str, None] = {
|
|
||||||
'f32': None,
|
|
||||||
'f64': None,
|
|
||||||
}
|
|
||||||
|
|
||||||
if isinstance(self.type3, PlaceholderForType):
|
if isinstance(self.type3, PlaceholderForType):
|
||||||
if self.type3.resolve_as is None:
|
if self.type3.resolve_as is None:
|
||||||
return RequireTypeSubstitutes()
|
return RequireTypeSubstitutes()
|
||||||
|
|
||||||
self.type3 = self.type3.resolve_as
|
self.type3 = self.type3.resolve_as
|
||||||
|
|
||||||
if self.type3.name in int_table:
|
type_info = self.context.build.type_info_map.get(self.type3.name)
|
||||||
bts, sgn = int_table[self.type3.name]
|
|
||||||
|
if type_info is not None and (type_info.wasm_type is WasmTypeInt32 or type_info.wasm_type is WasmTypeInt64):
|
||||||
|
assert type_info.signed is not None
|
||||||
|
|
||||||
if isinstance(self.literal.value, int):
|
if isinstance(self.literal.value, int):
|
||||||
try:
|
try:
|
||||||
self.literal.value.to_bytes(bts, 'big', signed=sgn)
|
self.literal.value.to_bytes(type_info.alloc_size, 'big', signed=type_info.signed)
|
||||||
except OverflowError:
|
except OverflowError:
|
||||||
return Error(f'Must fit in {bts} byte(s)', comment=self.comment) # FIXME: Add line information
|
return Error(f'Must fit in {type_info.alloc_size} byte(s)', comment=self.comment) # FIXME: Add line information
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return Error('Must be integer', comment=self.comment) # FIXME: Add line information
|
return Error('Must be integer', comment=self.comment) # FIXME: Add line information
|
||||||
|
|
||||||
if self.type3.name in float_table:
|
if type_info is not None and (type_info.wasm_type is WasmTypeFloat32 or type_info.wasm_type is WasmTypeFloat64):
|
||||||
_ = float_table[self.type3.name]
|
|
||||||
|
|
||||||
if isinstance(self.literal.value, float):
|
if isinstance(self.literal.value, float):
|
||||||
# FIXME: Bit check
|
# FIXME: Bit check
|
||||||
|
|
||||||
@ -581,16 +587,10 @@ class LiteralFitsConstraint(ConstraintBase):
|
|||||||
|
|
||||||
return Error('Must be real', comment=self.comment) # FIXME: Add line information
|
return Error('Must be real', comment=self.comment) # FIXME: Add line information
|
||||||
|
|
||||||
if self.type3 is prelude.bytes_:
|
|
||||||
if isinstance(self.literal.value, bytes):
|
|
||||||
return None
|
|
||||||
|
|
||||||
return Error('Must be bytes', comment=self.comment) # FIXME: Add line information
|
|
||||||
|
|
||||||
exp_type = self.type3
|
exp_type = self.type3
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return self.__class__.GENERATE_ROUTER(self, exp_type)
|
return self.generate_router(self, exp_type)
|
||||||
except NoRouteForTypeException:
|
except NoRouteForTypeException:
|
||||||
raise NotImplementedError(exp_type)
|
raise NotImplementedError(exp_type)
|
||||||
|
|
||||||
@ -610,32 +610,40 @@ class CanBeSubscriptedConstraint(ConstraintBase):
|
|||||||
"""
|
"""
|
||||||
A value that is subscipted, i.e. a[0] (tuple) or a[b] (static array)
|
A value that is subscipted, i.e. a[0] (tuple) or a[b] (static array)
|
||||||
"""
|
"""
|
||||||
__slots__ = ('ret_type3', 'type3', 'index_type3', 'index_const', )
|
__slots__ = ('ret_type3', 'type3', 'index_type3', 'index_const', 'generate_router', )
|
||||||
|
|
||||||
ret_type3: PlaceholderForType
|
ret_type3: PlaceholderForType
|
||||||
type3: PlaceholderForType
|
type3: PlaceholderForType
|
||||||
index_type3: PlaceholderForType
|
index_type3: PlaceholderForType
|
||||||
index_const: int | None
|
index_const: int | None
|
||||||
|
generate_router: TypeApplicationRouter['CanBeSubscriptedConstraint', CheckResult]
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
context: Context,
|
||||||
ret_type3: PlaceholderForType,
|
ret_type3: PlaceholderForType,
|
||||||
type3: PlaceholderForType,
|
type3: PlaceholderForType,
|
||||||
index_type3: PlaceholderForType,
|
index_type3: PlaceholderForType,
|
||||||
index_const: int | None,
|
index_const: int | None,
|
||||||
comment: Optional[str] = None,
|
comment: Optional[str] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
super().__init__(comment=comment)
|
super().__init__(context=context, comment=comment)
|
||||||
|
|
||||||
self.ret_type3 = ret_type3
|
self.ret_type3 = ret_type3
|
||||||
self.type3 = type3
|
self.type3 = type3
|
||||||
self.index_type3 = index_type3
|
self.index_type3 = index_type3
|
||||||
self.index_const = index_const
|
self.index_const = index_const
|
||||||
|
|
||||||
|
|
||||||
|
self.generate_router = TypeApplicationRouter()
|
||||||
|
self.generate_router.add_n(context.build.types['bytes'], self.__class__._generate_bytes)
|
||||||
|
self.generate_router.add(context.build.static_array, self.__class__._generate_static_array)
|
||||||
|
self.generate_router.add(context.build.tuple_, self.__class__._generate_tuple)
|
||||||
|
|
||||||
def _generate_bytes(self) -> CheckResult:
|
def _generate_bytes(self) -> CheckResult:
|
||||||
return [
|
return [
|
||||||
SameTypeConstraint(prelude.u32, self.index_type3, comment='([]) :: bytes -> u32 -> u8'),
|
SameTypeConstraint(self.context, self.context.build.types['u32'], self.index_type3, comment='([]) :: bytes -> u32 -> u8'),
|
||||||
SameTypeConstraint(prelude.u8, self.ret_type3, comment='([]) :: bytes -> u32 -> u8'),
|
SameTypeConstraint(self.context, self.context.build.types['u8'], self.ret_type3, comment='([]) :: bytes -> u32 -> u8'),
|
||||||
]
|
]
|
||||||
|
|
||||||
def _generate_static_array(self, sa_args: tuple[Type3, IntType3]) -> CheckResult:
|
def _generate_static_array(self, sa_args: tuple[Type3, IntType3]) -> CheckResult:
|
||||||
@ -645,8 +653,8 @@ class CanBeSubscriptedConstraint(ConstraintBase):
|
|||||||
return Error('Tuple index out of range')
|
return Error('Tuple index out of range')
|
||||||
|
|
||||||
return [
|
return [
|
||||||
SameTypeConstraint(prelude.u32, self.index_type3, comment='([]) :: Subscriptable a => a b -> u32 -> b'),
|
SameTypeConstraint(self.context, self.context.build.types['u32'], self.index_type3, comment='([]) :: Subscriptable a => a b -> u32 -> b'),
|
||||||
SameTypeConstraint(sa_type, self.ret_type3, comment='([]) :: Subscriptable a => a b -> u32 -> b'),
|
SameTypeConstraint(self.context, sa_type, self.ret_type3, comment='([]) :: Subscriptable a => a b -> u32 -> b'),
|
||||||
]
|
]
|
||||||
|
|
||||||
def _generate_tuple(self, tp_args: tuple[Type3, ...]) -> CheckResult:
|
def _generate_tuple(self, tp_args: tuple[Type3, ...]) -> CheckResult:
|
||||||
@ -661,15 +669,10 @@ class CanBeSubscriptedConstraint(ConstraintBase):
|
|||||||
return Error('Tuple index out of range')
|
return Error('Tuple index out of range')
|
||||||
|
|
||||||
return [
|
return [
|
||||||
SameTypeConstraint(prelude.u32, self.index_type3, comment='([]) :: Subscriptable a => a b -> u32 -> b'),
|
SameTypeConstraint(self.context, self.context.build.types['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}'),
|
SameTypeConstraint(self.context, 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:
|
def check(self) -> CheckResult:
|
||||||
if self.type3.resolve_as is None:
|
if self.type3.resolve_as is None:
|
||||||
return RequireTypeSubstitutes()
|
return RequireTypeSubstitutes()
|
||||||
@ -677,7 +680,7 @@ class CanBeSubscriptedConstraint(ConstraintBase):
|
|||||||
exp_type = self.type3.resolve_as
|
exp_type = self.type3.resolve_as
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return self.__class__.GENERATE_ROUTER(self, exp_type)
|
return self.generate_router(self, exp_type)
|
||||||
except NoRouteForTypeException:
|
except NoRouteForTypeException:
|
||||||
return Error(f'{exp_type.name} cannot be subscripted')
|
return Error(f'{exp_type.name} cannot be subscripted')
|
||||||
|
|
||||||
|
|||||||
@ -3,9 +3,9 @@ This module generates the typing constraints for Phasm.
|
|||||||
|
|
||||||
The constraints solver can then try to resolve all constraints.
|
The constraints solver can then try to resolve all constraints.
|
||||||
"""
|
"""
|
||||||
from typing import Generator, List
|
from typing import Any, Generator, List
|
||||||
|
|
||||||
from .. import ourlang, prelude
|
from .. import ourlang
|
||||||
from .constraints import (
|
from .constraints import (
|
||||||
CanBeSubscriptedConstraint,
|
CanBeSubscriptedConstraint,
|
||||||
ConstraintBase,
|
ConstraintBase,
|
||||||
@ -30,16 +30,15 @@ from .types import Type3, TypeApplication_Struct, TypeConstructor_Function
|
|||||||
|
|
||||||
ConstraintGenerator = Generator[ConstraintBase, None, None]
|
ConstraintGenerator = Generator[ConstraintBase, None, None]
|
||||||
|
|
||||||
def phasm_type3_generate_constraints(inp: ourlang.Module) -> List[ConstraintBase]:
|
def phasm_type3_generate_constraints(inp: ourlang.Module[Any]) -> List[ConstraintBase]:
|
||||||
ctx = Context()
|
ctx = Context(inp.build)
|
||||||
ctx.type_class_instances_existing.update(prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING)
|
|
||||||
|
|
||||||
return [*module(ctx, inp)]
|
return [*module(ctx, inp)]
|
||||||
|
|
||||||
def constant(ctx: Context, inp: ourlang.Constant, phft: PlaceholderForType) -> ConstraintGenerator:
|
def constant(ctx: Context, inp: ourlang.Constant, phft: PlaceholderForType) -> ConstraintGenerator:
|
||||||
if isinstance(inp, (ourlang.ConstantPrimitive, ourlang.ConstantBytes, ourlang.ConstantTuple, ourlang.ConstantStruct)):
|
if isinstance(inp, (ourlang.ConstantPrimitive, ourlang.ConstantBytes, ourlang.ConstantTuple, ourlang.ConstantStruct)):
|
||||||
yield LiteralFitsConstraint(
|
yield LiteralFitsConstraint(
|
||||||
phft, inp,
|
ctx, phft, inp,
|
||||||
comment='The given literal must fit the expected type'
|
comment='The given literal must fit the expected type'
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
@ -77,7 +76,8 @@ def expression_function_call(ctx: Context, inp: ourlang.FunctionCall, phft: Plac
|
|||||||
|
|
||||||
def expression_function_reference(ctx: Context, inp: ourlang.FunctionReference, phft: PlaceholderForType) -> ConstraintGenerator:
|
def expression_function_reference(ctx: Context, inp: ourlang.FunctionReference, phft: PlaceholderForType) -> ConstraintGenerator:
|
||||||
yield SameTypeConstraint(
|
yield SameTypeConstraint(
|
||||||
prelude.function(*(x.type3 for x in inp.function.posonlyargs), inp.function.returns_type3),
|
ctx,
|
||||||
|
ctx.build.function(*(x.type3 for x in inp.function.posonlyargs), inp.function.returns_type3),
|
||||||
phft,
|
phft,
|
||||||
comment=f'typeOf("{inp.function.name}") == typeOf({inp.function.name})',
|
comment=f'typeOf("{inp.function.name}") == typeOf({inp.function.name})',
|
||||||
)
|
)
|
||||||
@ -151,6 +151,7 @@ def _expression_function_call(
|
|||||||
type_var_map.setdefault(func_arg, PlaceholderForType([]))
|
type_var_map.setdefault(func_arg, PlaceholderForType([]))
|
||||||
|
|
||||||
yield SameFunctionArgumentConstraint(
|
yield SameFunctionArgumentConstraint(
|
||||||
|
ctx,
|
||||||
func_var_map[sig_arg],
|
func_var_map[sig_arg],
|
||||||
sig_arg,
|
sig_arg,
|
||||||
type_var_map,
|
type_var_map,
|
||||||
@ -182,6 +183,7 @@ def _expression_function_call(
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
yield SameTypeArgumentConstraint(
|
yield SameTypeArgumentConstraint(
|
||||||
|
ctx,
|
||||||
type_var_map[sig_arg],
|
type_var_map[sig_arg],
|
||||||
type_var_map[sig_arg.application.arguments],
|
type_var_map[sig_arg.application.arguments],
|
||||||
comment=f'Ensure `{sig_arg.application.arguments.name}` matches in {signature}',
|
comment=f'Ensure `{sig_arg.application.arguments.name}` matches in {signature}',
|
||||||
@ -195,15 +197,15 @@ def _expression_function_call(
|
|||||||
comment = f'The type of the value passed to argument {arg_no} of function {func_name} should match the type of that argument'
|
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, TypeVariable):
|
if isinstance(sig_part, TypeVariable):
|
||||||
yield SameTypeConstraint(type_var_map[sig_part], arg_placeholders[arg_expr], comment=comment)
|
yield SameTypeConstraint(ctx, type_var_map[sig_part], arg_placeholders[arg_expr], comment=comment)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if isinstance(sig_part, Type3):
|
if isinstance(sig_part, Type3):
|
||||||
yield SameTypeConstraint(sig_part, arg_placeholders[arg_expr], comment=comment)
|
yield SameTypeConstraint(ctx, sig_part, arg_placeholders[arg_expr], comment=comment)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if isinstance(sig_part, FunctionArgument):
|
if isinstance(sig_part, FunctionArgument):
|
||||||
yield SameTypeConstraint(func_var_map[sig_part], arg_placeholders[arg_expr], comment=comment)
|
yield SameTypeConstraint(ctx, func_var_map[sig_part], arg_placeholders[arg_expr], comment=comment)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
raise NotImplementedError(sig_part)
|
raise NotImplementedError(sig_part)
|
||||||
@ -215,7 +217,7 @@ def expression(ctx: Context, inp: ourlang.Expression, phft: PlaceholderForType)
|
|||||||
return
|
return
|
||||||
|
|
||||||
if isinstance(inp, ourlang.VariableReference):
|
if isinstance(inp, ourlang.VariableReference):
|
||||||
yield SameTypeConstraint(inp.variable.type3, phft,
|
yield SameTypeConstraint(ctx, inp.variable.type3, phft,
|
||||||
comment=f'typeOf("{inp.variable.name}") == typeOf({inp.variable.name})')
|
comment=f'typeOf("{inp.variable.name}") == typeOf({inp.variable.name})')
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -239,6 +241,7 @@ def expression(ctx: Context, inp: ourlang.Expression, phft: PlaceholderForType)
|
|||||||
r_type.append(arg_phft)
|
r_type.append(arg_phft)
|
||||||
|
|
||||||
yield TupleMatchConstraint(
|
yield TupleMatchConstraint(
|
||||||
|
ctx,
|
||||||
phft,
|
phft,
|
||||||
r_type,
|
r_type,
|
||||||
comment='The type of a tuple is a combination of its members'
|
comment='The type of a tuple is a combination of its members'
|
||||||
@ -254,9 +257,9 @@ def expression(ctx: Context, inp: ourlang.Expression, phft: PlaceholderForType)
|
|||||||
yield from expression(ctx, inp.index, index_phft)
|
yield from expression(ctx, inp.index, index_phft)
|
||||||
|
|
||||||
if isinstance(inp.index, ourlang.ConstantPrimitive) and isinstance(inp.index.value, int):
|
if isinstance(inp.index, ourlang.ConstantPrimitive) and isinstance(inp.index.value, int):
|
||||||
yield CanBeSubscriptedConstraint(phft, varref_phft, index_phft, inp.index.value)
|
yield CanBeSubscriptedConstraint(ctx, phft, varref_phft, index_phft, inp.index.value)
|
||||||
else:
|
else:
|
||||||
yield CanBeSubscriptedConstraint(phft, varref_phft, index_phft, None)
|
yield CanBeSubscriptedConstraint(ctx, phft, varref_phft, index_phft, None)
|
||||||
return
|
return
|
||||||
|
|
||||||
if isinstance(inp, ourlang.AccessStructMember):
|
if isinstance(inp, ourlang.AccessStructMember):
|
||||||
@ -265,7 +268,7 @@ def expression(ctx: Context, inp: ourlang.Expression, phft: PlaceholderForType)
|
|||||||
mem_typ = dict(inp.struct_type3.application.arguments)[inp.member]
|
mem_typ = dict(inp.struct_type3.application.arguments)[inp.member]
|
||||||
|
|
||||||
yield from expression(ctx, inp.varref, PlaceholderForType([inp.varref])) # TODO
|
yield from expression(ctx, inp.varref, PlaceholderForType([inp.varref])) # TODO
|
||||||
yield SameTypeConstraint(mem_typ, phft,
|
yield SameTypeConstraint(ctx, 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}')
|
comment=f'The type of a struct member reference is the same as the type of struct member {inp.struct_type3.name}.{inp.member}')
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -276,7 +279,7 @@ def statement_return(ctx: Context, fun: ourlang.Function, inp: ourlang.Statement
|
|||||||
|
|
||||||
yield from expression(ctx, inp.value, phft)
|
yield from expression(ctx, inp.value, phft)
|
||||||
|
|
||||||
yield SameTypeConstraint(fun.returns_type3, phft,
|
yield SameTypeConstraint(ctx, fun.returns_type3, phft,
|
||||||
comment=f'The type of the value returned from function {fun.name} should match its return type')
|
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:
|
def statement_if(ctx: Context, fun: ourlang.Function, inp: ourlang.StatementIf) -> ConstraintGenerator:
|
||||||
@ -284,7 +287,7 @@ def statement_if(ctx: Context, fun: ourlang.Function, inp: ourlang.StatementIf)
|
|||||||
|
|
||||||
yield from expression(ctx, inp.test, test_phft)
|
yield from expression(ctx, inp.test, test_phft)
|
||||||
|
|
||||||
yield SameTypeConstraint(test_phft, prelude.bool_,
|
yield SameTypeConstraint(ctx, test_phft, ctx.build.types['bool'],
|
||||||
comment='Must pass a boolean expression to if')
|
comment='Must pass a boolean expression to if')
|
||||||
|
|
||||||
for stmt in inp.statements:
|
for stmt in inp.statements:
|
||||||
@ -317,10 +320,10 @@ def module_constant_def(ctx: Context, inp: ourlang.ModuleConstantDef) -> Constra
|
|||||||
phft = PlaceholderForType([inp.constant])
|
phft = PlaceholderForType([inp.constant])
|
||||||
|
|
||||||
yield from constant(ctx, inp.constant, phft)
|
yield from constant(ctx, inp.constant, phft)
|
||||||
yield SameTypeConstraint(inp.type3, phft,
|
yield SameTypeConstraint(ctx, inp.type3, phft,
|
||||||
comment=f'The type of the value for module constant definition {inp.name} should match the type of that constant')
|
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:
|
def module(ctx: Context, inp: ourlang.Module[Any]) -> ConstraintGenerator:
|
||||||
for cdef in inp.constant_defs.values():
|
for cdef in inp.constant_defs.values():
|
||||||
yield from module_constant_def(ctx, cdef)
|
yield from module_constant_def(ctx, cdef)
|
||||||
|
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
"""
|
"""
|
||||||
Entry point to the type3 system
|
Entry point to the type3 system
|
||||||
"""
|
"""
|
||||||
from typing import Dict, List
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
from .. import codestyle, ourlang
|
from .. import codestyle, ourlang
|
||||||
from .constraints import (
|
from .constraints import (
|
||||||
ConstraintBase,
|
ConstraintBase,
|
||||||
Error,
|
Error,
|
||||||
|
HumanReadableRet,
|
||||||
RequireTypeSubstitutes,
|
RequireTypeSubstitutes,
|
||||||
SameTypeConstraint,
|
|
||||||
SubstitutionMap,
|
SubstitutionMap,
|
||||||
)
|
)
|
||||||
from .constraintsgenerator import phasm_type3_generate_constraints
|
from .constraintsgenerator import phasm_type3_generate_constraints
|
||||||
@ -25,7 +25,28 @@ class Type3Exception(BaseException):
|
|||||||
Thrown when the Type3 system detects constraints that do not hold
|
Thrown when the Type3 system detects constraints that do not hold
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def phasm_type3(inp: ourlang.Module, verbose: bool = False) -> None:
|
class DeducedType:
|
||||||
|
__slots__ = ('phft', 'typ', 'comment')
|
||||||
|
|
||||||
|
phft: PlaceholderForType
|
||||||
|
typ: Type3
|
||||||
|
comment: str
|
||||||
|
|
||||||
|
def __init__(self, phft: PlaceholderForType, typ: Type3) -> None:
|
||||||
|
self.phft = phft
|
||||||
|
self.typ = typ
|
||||||
|
self.comment = 'Deduced type'
|
||||||
|
|
||||||
|
def human_readable(self) -> HumanReadableRet:
|
||||||
|
return (
|
||||||
|
'{phft} == {typ}',
|
||||||
|
{
|
||||||
|
'phft': self.phft,
|
||||||
|
'typ': self.typ,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
def phasm_type3(inp: ourlang.Module[Any], verbose: bool = False) -> None:
|
||||||
constraint_list = phasm_type3_generate_constraints(inp)
|
constraint_list = phasm_type3_generate_constraints(inp)
|
||||||
assert constraint_list
|
assert constraint_list
|
||||||
|
|
||||||
@ -116,7 +137,7 @@ def phasm_type3(inp: ourlang.Module, verbose: bool = False) -> None:
|
|||||||
for expr in plh.update_on_substitution:
|
for expr in plh.update_on_substitution:
|
||||||
expr.type3 = typ
|
expr.type3 = typ
|
||||||
|
|
||||||
def print_constraint(placeholder_id_map: Dict[int, str], constraint: ConstraintBase) -> None:
|
def print_constraint(placeholder_id_map: Dict[int, str], constraint: ConstraintBase | DeducedType) -> None:
|
||||||
txt, fmt = constraint.human_readable()
|
txt, fmt = constraint.human_readable()
|
||||||
act_fmt: Dict[str, str] = {}
|
act_fmt: Dict[str, str] = {}
|
||||||
for fmt_key, fmt_val in fmt.items():
|
for fmt_key, fmt_val in fmt.items():
|
||||||
@ -151,7 +172,7 @@ def get_printable_type_name(inp: Type3OrPlaceholder, placeholder_id_map: Dict[in
|
|||||||
def print_constraint_list(placeholder_id_map: Dict[int, str], constraint_list: List[ConstraintBase], placeholder_substitutes: SubstitutionMap) -> None:
|
def print_constraint_list(placeholder_id_map: Dict[int, str], constraint_list: List[ConstraintBase], placeholder_substitutes: SubstitutionMap) -> None:
|
||||||
print('=== v type3 constraint_list v === ')
|
print('=== v type3 constraint_list v === ')
|
||||||
for psk, psv in placeholder_substitutes.items():
|
for psk, psv in placeholder_substitutes.items():
|
||||||
print_constraint(placeholder_id_map, SameTypeConstraint(psk, psv, comment='Deduced type'))
|
print_constraint(placeholder_id_map, DeducedType(psk, psv))
|
||||||
|
|
||||||
for constraint in constraint_list:
|
for constraint in constraint_list:
|
||||||
print_constraint(placeholder_id_map, constraint)
|
print_constraint(placeholder_id_map, constraint)
|
||||||
|
|||||||
@ -69,6 +69,12 @@ class TypeVariableApplication_Nullary(TypeVariableApplication_Base[None, None]):
|
|||||||
For the type for this function argument it's not relevant if it was constructed.
|
For the type for this function argument it's not relevant if it was constructed.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def make_typevar(name: str) -> TypeVariable:
|
||||||
|
"""
|
||||||
|
Helper function to make a type variable for a non-constructed type.
|
||||||
|
"""
|
||||||
|
return TypeVariable(name, TypeVariableApplication_Nullary(None, None))
|
||||||
|
|
||||||
class TypeConstructorVariable:
|
class TypeConstructorVariable:
|
||||||
"""
|
"""
|
||||||
Types constructor variable are used in function definition.
|
Types constructor variable are used in function definition.
|
||||||
|
|||||||
@ -79,7 +79,7 @@ class Type3(KindArgument):
|
|||||||
|
|
||||||
def __eq__(self, other: Any) -> bool:
|
def __eq__(self, other: Any) -> bool:
|
||||||
if not isinstance(other, Type3):
|
if not isinstance(other, Type3):
|
||||||
raise NotImplementedError
|
raise NotImplementedError(other)
|
||||||
|
|
||||||
return self is other
|
return self is other
|
||||||
|
|
||||||
|
|||||||
@ -3,17 +3,18 @@ import struct
|
|||||||
import sys
|
import sys
|
||||||
from typing import Any, Callable, Generator, Iterable, List, TextIO, Union
|
from typing import Any, Callable, Generator, Iterable, List, TextIO, Union
|
||||||
|
|
||||||
from phasm import compiler, prelude
|
from phasm import compiler
|
||||||
|
from phasm.build import builtins
|
||||||
from phasm.codestyle import phasm_render
|
from phasm.codestyle import phasm_render
|
||||||
from phasm.runtime import (
|
|
||||||
calculate_alloc_size,
|
|
||||||
calculate_alloc_size_static_array,
|
|
||||||
calculate_alloc_size_struct,
|
|
||||||
calculate_alloc_size_tuple,
|
|
||||||
)
|
|
||||||
from phasm.stdlib.types import TYPE_INFO_CONSTRUCTED, TYPE_INFO_MAP
|
|
||||||
from phasm.type3 import types as type3types
|
from phasm.type3 import types as type3types
|
||||||
from phasm.type3.routers import NoRouteForTypeException, TypeApplicationRouter
|
from phasm.type3.routers import NoRouteForTypeException, TypeApplicationRouter
|
||||||
|
from phasm.wasm import (
|
||||||
|
WasmTypeFloat32,
|
||||||
|
WasmTypeFloat64,
|
||||||
|
WasmTypeInt32,
|
||||||
|
WasmTypeInt64,
|
||||||
|
WasmTypeNone,
|
||||||
|
)
|
||||||
|
|
||||||
from . import runners
|
from . import runners
|
||||||
|
|
||||||
@ -125,17 +126,14 @@ class Suite:
|
|||||||
runner.interpreter_dump_memory(sys.stderr)
|
runner.interpreter_dump_memory(sys.stderr)
|
||||||
|
|
||||||
for arg, arg_typ in zip(args, func_args, strict=True):
|
for arg, arg_typ in zip(args, func_args, strict=True):
|
||||||
if arg_typ in (prelude.u8, prelude.u16, prelude.u32, prelude.u64, ):
|
arg_typ_info = runner.phasm_ast.build.type_info_map.get(arg_typ.name)
|
||||||
|
|
||||||
|
if arg_typ_info and (arg_typ_info.wasm_type is WasmTypeInt32 or arg_typ_info.wasm_type is WasmTypeInt64):
|
||||||
assert isinstance(arg, int)
|
assert isinstance(arg, int)
|
||||||
wasm_args.append(arg)
|
wasm_args.append(arg)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if arg_typ in (prelude.i8, prelude.i16, prelude.i32, prelude.i64, ):
|
if arg_typ_info and (arg_typ_info.wasm_type is WasmTypeFloat32 or arg_typ_info.wasm_type is WasmTypeFloat64):
|
||||||
assert isinstance(arg, int)
|
|
||||||
wasm_args.append(arg)
|
|
||||||
continue
|
|
||||||
|
|
||||||
if arg_typ in (prelude.f32, prelude.f64, ):
|
|
||||||
assert isinstance(arg, float)
|
assert isinstance(arg, float)
|
||||||
wasm_args.append(arg)
|
wasm_args.append(arg)
|
||||||
continue
|
continue
|
||||||
@ -168,19 +166,6 @@ class Suite:
|
|||||||
def write_header(textio: TextIO, msg: str) -> None:
|
def write_header(textio: TextIO, msg: str) -> None:
|
||||||
textio.write(f'{DASHES} {msg.ljust(16)} {DASHES}\n')
|
textio.write(f'{DASHES} {msg.ljust(16)} {DASHES}\n')
|
||||||
|
|
||||||
WRITE_LOOKUP_MAP = {
|
|
||||||
'u8': compiler.module_data_u8,
|
|
||||||
'u16': compiler.module_data_u16,
|
|
||||||
'u32': compiler.module_data_u32,
|
|
||||||
'u64': compiler.module_data_u64,
|
|
||||||
'i8': compiler.module_data_i8,
|
|
||||||
'i16': compiler.module_data_i16,
|
|
||||||
'i32': compiler.module_data_i32,
|
|
||||||
'i64': compiler.module_data_i64,
|
|
||||||
'f32': compiler.module_data_f32,
|
|
||||||
'f64': compiler.module_data_f64,
|
|
||||||
}
|
|
||||||
|
|
||||||
def _write_memory_stored_value(
|
def _write_memory_stored_value(
|
||||||
runner: runners.RunnerBase,
|
runner: runners.RunnerBase,
|
||||||
adr: int,
|
adr: int,
|
||||||
@ -189,10 +174,14 @@ def _write_memory_stored_value(
|
|||||||
) -> int:
|
) -> int:
|
||||||
try:
|
try:
|
||||||
adr2 = ALLOCATE_MEMORY_STORED_ROUTER((runner, val), val_typ)
|
adr2 = ALLOCATE_MEMORY_STORED_ROUTER((runner, val), val_typ)
|
||||||
runner.interpreter_write_memory(adr, compiler.module_data_u32(adr2))
|
|
||||||
return TYPE_INFO_CONSTRUCTED.alloc_size
|
ptr_type_info = runner.phasm_ast.build.type_info_map['ptr']
|
||||||
|
to_write = compiler.module_data_primitive(ptr_type_info, adr2)
|
||||||
|
runner.interpreter_write_memory(adr, to_write)
|
||||||
|
return runner.phasm_ast.build.type_info_constructed.alloc_size
|
||||||
except NoRouteForTypeException:
|
except NoRouteForTypeException:
|
||||||
to_write = WRITE_LOOKUP_MAP[val_typ.name](val)
|
val_typ_info = runner.phasm_ast.build.type_info_map[val_typ.name]
|
||||||
|
to_write = compiler.module_data_primitive(val_typ_info, val)
|
||||||
runner.interpreter_write_memory(adr, to_write)
|
runner.interpreter_write_memory(adr, to_write)
|
||||||
return len(to_write)
|
return len(to_write)
|
||||||
|
|
||||||
@ -213,16 +202,19 @@ def _allocate_memory_stored_dynamic_array(attrs: tuple[runners.RunnerBase, Any],
|
|||||||
|
|
||||||
da_type, = da_args
|
da_type, = da_args
|
||||||
|
|
||||||
|
if da_type.name == 'u8':
|
||||||
|
return _allocate_memory_stored_bytes(attrs)
|
||||||
|
|
||||||
if not isinstance(val, tuple):
|
if not isinstance(val, tuple):
|
||||||
raise InvalidArgumentException(f'Expected tuple; got {val!r} instead')
|
raise InvalidArgumentException(f'Expected tuple; got {val!r} instead')
|
||||||
|
|
||||||
alloc_size = 4 + len(val) * calculate_alloc_size(da_type, True)
|
alloc_size = 4 + len(val) * runner.phasm_ast.build.calculate_alloc_size(da_type, True)
|
||||||
adr = runner.call('stdlib.alloc.__alloc__', alloc_size)
|
adr = runner.call('stdlib.alloc.__alloc__', alloc_size)
|
||||||
assert isinstance(adr, int) # Type int
|
assert isinstance(adr, int) # Type int
|
||||||
sys.stderr.write(f'Allocation 0x{adr:08x} {repr(val)}\n')
|
sys.stderr.write(f'Allocation 0x{adr:08x} {repr(val)}\n')
|
||||||
|
|
||||||
offset = adr
|
offset = adr
|
||||||
offset += _write_memory_stored_value(runner, offset, prelude.u32, len(val))
|
offset += _write_memory_stored_value(runner, offset, runner.phasm_ast.build.types['u32'], len(val))
|
||||||
for val_el_val in val:
|
for val_el_val in val:
|
||||||
offset += _write_memory_stored_value(runner, offset, da_type, val_el_val)
|
offset += _write_memory_stored_value(runner, offset, da_type, val_el_val)
|
||||||
return adr
|
return adr
|
||||||
@ -237,7 +229,7 @@ def _allocate_memory_stored_static_array(attrs: tuple[runners.RunnerBase, Any],
|
|||||||
if sa_len.value != len(val):
|
if sa_len.value != len(val):
|
||||||
raise InvalidArgumentException(f'Expected tuple of length {sa_len.value}; got {val!r} instead')
|
raise InvalidArgumentException(f'Expected tuple of length {sa_len.value}; got {val!r} instead')
|
||||||
|
|
||||||
alloc_size = calculate_alloc_size_static_array(False, sa_args)
|
alloc_size = runner.phasm_ast.build.calculate_alloc_size_static_array(sa_args)
|
||||||
adr = runner.call('stdlib.alloc.__alloc__', alloc_size)
|
adr = runner.call('stdlib.alloc.__alloc__', alloc_size)
|
||||||
assert isinstance(adr, int) # Type int
|
assert isinstance(adr, int) # Type int
|
||||||
sys.stderr.write(f'Allocation 0x{adr:08x} {repr(val)}\n')
|
sys.stderr.write(f'Allocation 0x{adr:08x} {repr(val)}\n')
|
||||||
@ -252,7 +244,7 @@ def _allocate_memory_stored_struct(attrs: tuple[runners.RunnerBase, Any], st_arg
|
|||||||
|
|
||||||
assert isinstance(val, dict)
|
assert isinstance(val, dict)
|
||||||
|
|
||||||
alloc_size = calculate_alloc_size_struct(False, st_args)
|
alloc_size = runner.phasm_ast.build.calculate_alloc_size_struct(st_args)
|
||||||
adr = runner.call('stdlib.alloc.__alloc__', alloc_size)
|
adr = runner.call('stdlib.alloc.__alloc__', alloc_size)
|
||||||
assert isinstance(adr, int)
|
assert isinstance(adr, int)
|
||||||
sys.stderr.write(f'Allocation 0x{adr:08x} {repr(val)}\n')
|
sys.stderr.write(f'Allocation 0x{adr:08x} {repr(val)}\n')
|
||||||
@ -272,7 +264,7 @@ def _allocate_memory_stored_tuple(attrs: tuple[runners.RunnerBase, Any], tp_args
|
|||||||
|
|
||||||
assert isinstance(val, tuple)
|
assert isinstance(val, tuple)
|
||||||
|
|
||||||
alloc_size = calculate_alloc_size_tuple(False, tp_args)
|
alloc_size = runner.phasm_ast.build.calculate_alloc_size_tuple(tp_args)
|
||||||
adr = runner.call('stdlib.alloc.__alloc__', alloc_size)
|
adr = runner.call('stdlib.alloc.__alloc__', alloc_size)
|
||||||
assert isinstance(adr, int)
|
assert isinstance(adr, int)
|
||||||
sys.stderr.write(f'Allocation 0x{adr:08x} {repr(val)}\n')
|
sys.stderr.write(f'Allocation 0x{adr:08x} {repr(val)}\n')
|
||||||
@ -285,11 +277,10 @@ def _allocate_memory_stored_tuple(attrs: tuple[runners.RunnerBase, Any], tp_args
|
|||||||
return adr
|
return adr
|
||||||
|
|
||||||
ALLOCATE_MEMORY_STORED_ROUTER = TypeApplicationRouter[tuple[runners.RunnerBase, Any], Any]()
|
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(builtins.dynamic_array, _allocate_memory_stored_dynamic_array)
|
||||||
ALLOCATE_MEMORY_STORED_ROUTER.add(prelude.dynamic_array, _allocate_memory_stored_dynamic_array)
|
ALLOCATE_MEMORY_STORED_ROUTER.add(builtins.static_array, _allocate_memory_stored_static_array)
|
||||||
ALLOCATE_MEMORY_STORED_ROUTER.add(prelude.static_array, _allocate_memory_stored_static_array)
|
ALLOCATE_MEMORY_STORED_ROUTER.add(builtins.struct, _allocate_memory_stored_struct)
|
||||||
ALLOCATE_MEMORY_STORED_ROUTER.add(prelude.struct, _allocate_memory_stored_struct)
|
ALLOCATE_MEMORY_STORED_ROUTER.add(builtins.tuple_, _allocate_memory_stored_tuple)
|
||||||
ALLOCATE_MEMORY_STORED_ROUTER.add(prelude.tuple_, _allocate_memory_stored_tuple)
|
|
||||||
|
|
||||||
def _load_memory_stored_returned_value(
|
def _load_memory_stored_returned_value(
|
||||||
runner: runners.RunnerBase,
|
runner: runners.RunnerBase,
|
||||||
@ -298,48 +289,26 @@ def _load_memory_stored_returned_value(
|
|||||||
) -> Any:
|
) -> Any:
|
||||||
ret_type3 = runner.phasm_ast.functions[func_name].returns_type3
|
ret_type3 = runner.phasm_ast.functions[func_name].returns_type3
|
||||||
|
|
||||||
if ret_type3 is prelude.none:
|
if ret_type3.name in runner.phasm_ast.build.type_info_map:
|
||||||
|
type_info = runner.phasm_ast.build.type_info_map[ret_type3.name]
|
||||||
|
|
||||||
|
if type_info.wasm_type is WasmTypeNone:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if ret_type3 is prelude.bool_:
|
if type_info.wasm_type is WasmTypeInt32 or type_info.wasm_type is WasmTypeInt64:
|
||||||
assert isinstance(wasm_value, int), wasm_value
|
assert isinstance(wasm_value, int), wasm_value
|
||||||
|
|
||||||
|
if type_info.signed is None:
|
||||||
|
# Must be bool type
|
||||||
return 0 != wasm_value
|
return 0 != wasm_value
|
||||||
|
|
||||||
if ret_type3 in (prelude.i8, prelude.i16, prelude.i32, prelude.i64):
|
data = wasm_value.to_bytes(8, 'big', signed=True)
|
||||||
assert isinstance(wasm_value, int), wasm_value
|
data = data[-type_info.alloc_size:]
|
||||||
|
wasm_value = int.from_bytes(data, 'big', signed=type_info.signed)
|
||||||
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)
|
|
||||||
|
|
||||||
if ret_type3 is prelude.i16:
|
|
||||||
# Values are actually i32
|
|
||||||
# Have to reinterpret to load proper value
|
|
||||||
data = struct.pack('<i', wasm_value)
|
|
||||||
wasm_value, = struct.unpack('<hxx', data)
|
|
||||||
|
|
||||||
return wasm_value
|
return wasm_value
|
||||||
|
|
||||||
if ret_type3 in (prelude.u8, prelude.u16, prelude.u32, prelude.u64):
|
if type_info.wasm_type is WasmTypeFloat32 or type_info.wasm_type is WasmTypeFloat64:
|
||||||
assert isinstance(wasm_value, int), wasm_value
|
|
||||||
|
|
||||||
if wasm_value < 0:
|
|
||||||
# WASM does not support unsigned values through its interface
|
|
||||||
# Cast and then reinterpret
|
|
||||||
|
|
||||||
letter = {
|
|
||||||
'u32': 'i',
|
|
||||||
'u64': 'q',
|
|
||||||
}[ret_type3.name]
|
|
||||||
|
|
||||||
data = struct.pack(f'<{letter}', wasm_value)
|
|
||||||
wasm_value, = struct.unpack(f'<{letter.upper()}', data)
|
|
||||||
|
|
||||||
return wasm_value
|
|
||||||
|
|
||||||
if ret_type3 in (prelude.f32, prelude.f64, ):
|
|
||||||
assert isinstance(wasm_value, float), wasm_value
|
assert isinstance(wasm_value, float), wasm_value
|
||||||
return wasm_value
|
return wasm_value
|
||||||
|
|
||||||
@ -348,46 +317,48 @@ def _load_memory_stored_returned_value(
|
|||||||
return LOAD_FROM_ADDRESS_ROUTER((runner, wasm_value), ret_type3)
|
return LOAD_FROM_ADDRESS_ROUTER((runner, wasm_value), ret_type3)
|
||||||
|
|
||||||
def _unpack(runner: runners.RunnerBase, typ: type3types.Type3, inp: bytes) -> Any:
|
def _unpack(runner: runners.RunnerBase, typ: type3types.Type3, inp: bytes) -> Any:
|
||||||
typ_info = TYPE_INFO_MAP.get(typ.name, TYPE_INFO_CONSTRUCTED)
|
typ_info = runner.phasm_ast.build.type_info_map.get(typ.name)
|
||||||
|
|
||||||
assert len(inp) == typ_info.alloc_size
|
if typ_info is None:
|
||||||
|
assert len(inp) == 4
|
||||||
|
|
||||||
if typ is prelude.u8:
|
|
||||||
return struct.unpack('<B', inp)[0]
|
|
||||||
|
|
||||||
if typ is prelude.u16:
|
|
||||||
return struct.unpack('<H', inp)[0]
|
|
||||||
|
|
||||||
if typ is prelude.u32:
|
|
||||||
return struct.unpack('<I', inp)[0]
|
|
||||||
|
|
||||||
if typ is prelude.u64:
|
|
||||||
return struct.unpack('<Q', inp)[0]
|
|
||||||
|
|
||||||
if typ is prelude.i8:
|
|
||||||
return struct.unpack('<b', inp)[0]
|
|
||||||
|
|
||||||
if typ is prelude.i16:
|
|
||||||
return struct.unpack('<h', inp)[0]
|
|
||||||
|
|
||||||
if typ is prelude.i32:
|
|
||||||
return struct.unpack('<i', inp)[0]
|
|
||||||
|
|
||||||
if typ is prelude.i64:
|
|
||||||
return struct.unpack('<q', inp)[0]
|
|
||||||
|
|
||||||
if typ is prelude.f32:
|
|
||||||
return struct.unpack('<f', inp)[0]
|
|
||||||
|
|
||||||
if typ is prelude.f64:
|
|
||||||
return struct.unpack('<d', inp)[0]
|
|
||||||
|
|
||||||
if typ_info is TYPE_INFO_CONSTRUCTED:
|
|
||||||
# Note: For applied types, inp should contain a 4 byte pointer
|
# Note: For applied types, inp should contain a 4 byte pointer
|
||||||
adr = struct.unpack('<I', inp)[0]
|
adr = struct.unpack('<I', inp)[0]
|
||||||
|
|
||||||
return LOAD_FROM_ADDRESS_ROUTER((runner, adr), typ)
|
return LOAD_FROM_ADDRESS_ROUTER((runner, adr), typ)
|
||||||
|
|
||||||
|
assert len(inp) == typ_info.alloc_size
|
||||||
|
|
||||||
|
if typ.name == 'u8':
|
||||||
|
return struct.unpack('<B', inp)[0]
|
||||||
|
|
||||||
|
if typ.name == 'u16':
|
||||||
|
return struct.unpack('<H', inp)[0]
|
||||||
|
|
||||||
|
if typ.name == 'u32':
|
||||||
|
return struct.unpack('<I', inp)[0]
|
||||||
|
|
||||||
|
if typ.name == 'u64':
|
||||||
|
return struct.unpack('<Q', inp)[0]
|
||||||
|
|
||||||
|
if typ.name == 'i8':
|
||||||
|
return struct.unpack('<b', inp)[0]
|
||||||
|
|
||||||
|
if typ.name == 'i16':
|
||||||
|
return struct.unpack('<h', inp)[0]
|
||||||
|
|
||||||
|
if typ.name == 'i32':
|
||||||
|
return struct.unpack('<i', inp)[0]
|
||||||
|
|
||||||
|
if typ.name == 'i64':
|
||||||
|
return struct.unpack('<q', inp)[0]
|
||||||
|
|
||||||
|
if typ.name == 'f32':
|
||||||
|
return struct.unpack('<f', inp)[0]
|
||||||
|
|
||||||
|
if typ.name == 'f64':
|
||||||
|
return struct.unpack('<d', inp)[0]
|
||||||
|
|
||||||
raise NotImplementedError(typ, inp)
|
raise NotImplementedError(typ, inp)
|
||||||
|
|
||||||
def _load_bytes_from_address(attrs: tuple[runners.RunnerBase, int]) -> bytes:
|
def _load_bytes_from_address(attrs: tuple[runners.RunnerBase, int]) -> bytes:
|
||||||
@ -410,13 +381,16 @@ def _load_dynamic_array_from_address(attrs: tuple[runners.RunnerBase, int], da_a
|
|||||||
runner, adr = attrs
|
runner, adr = attrs
|
||||||
da_type, = da_args
|
da_type, = da_args
|
||||||
|
|
||||||
|
if da_type.name == 'u8':
|
||||||
|
return _load_bytes_from_address(attrs)
|
||||||
|
|
||||||
sys.stderr.write(f'Reading 0x{adr:08x} {da_type:s}[...]\n')
|
sys.stderr.write(f'Reading 0x{adr:08x} {da_type:s}[...]\n')
|
||||||
|
|
||||||
read_bytes = runner.interpreter_read_memory(adr, 4)
|
read_bytes = runner.interpreter_read_memory(adr, 4)
|
||||||
array_len, = struct.unpack('<I', read_bytes)
|
array_len, = struct.unpack('<I', read_bytes)
|
||||||
adr += 4
|
adr += 4
|
||||||
|
|
||||||
arg_size_1 = calculate_alloc_size(da_type, is_member=True)
|
arg_size_1 = runner.phasm_ast.build.calculate_alloc_size(da_type, is_member=True)
|
||||||
arg_sizes = [arg_size_1 for _ in range(array_len)] # _split_read_bytes requires one arg per value
|
arg_sizes = [arg_size_1 for _ in range(array_len)] # _split_read_bytes requires one arg per value
|
||||||
|
|
||||||
read_bytes = runner.interpreter_read_memory(adr, sum(arg_sizes))
|
read_bytes = runner.interpreter_read_memory(adr, sum(arg_sizes))
|
||||||
@ -434,7 +408,7 @@ def _load_static_array_from_address(attrs: tuple[runners.RunnerBase, int], sa_ar
|
|||||||
|
|
||||||
sa_len = len_typ.value
|
sa_len = len_typ.value
|
||||||
|
|
||||||
arg_size_1 = calculate_alloc_size(sub_typ, is_member=True)
|
arg_size_1 = runner.phasm_ast.build.calculate_alloc_size(sub_typ, is_member=True)
|
||||||
arg_sizes = [arg_size_1 for _ in range(sa_len)] # _split_read_bytes requires one arg per value
|
arg_sizes = [arg_size_1 for _ in range(sa_len)] # _split_read_bytes requires one arg per value
|
||||||
|
|
||||||
read_bytes = runner.interpreter_read_memory(adr, sum(arg_sizes))
|
read_bytes = runner.interpreter_read_memory(adr, sum(arg_sizes))
|
||||||
@ -450,7 +424,7 @@ def _load_struct_from_address(attrs: tuple[runners.RunnerBase, int], st_args: tu
|
|||||||
sys.stderr.write(f'Reading 0x{adr:08x} struct {list(st_args)}\n')
|
sys.stderr.write(f'Reading 0x{adr:08x} struct {list(st_args)}\n')
|
||||||
|
|
||||||
arg_sizes = [
|
arg_sizes = [
|
||||||
calculate_alloc_size(x, is_member=True)
|
runner.phasm_ast.build.calculate_alloc_size(x, is_member=True)
|
||||||
for _, x in st_args
|
for _, x in st_args
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -467,7 +441,7 @@ def _load_tuple_from_address(attrs: tuple[runners.RunnerBase, int], tp_args: tup
|
|||||||
sys.stderr.write(f'Reading 0x{adr:08x} tuple {len(tp_args)}\n')
|
sys.stderr.write(f'Reading 0x{adr:08x} tuple {len(tp_args)}\n')
|
||||||
|
|
||||||
arg_sizes = [
|
arg_sizes = [
|
||||||
calculate_alloc_size(x, is_member=True)
|
runner.phasm_ast.build.calculate_alloc_size(x, is_member=True)
|
||||||
for x in tp_args
|
for x in tp_args
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -479,8 +453,7 @@ def _load_tuple_from_address(attrs: tuple[runners.RunnerBase, int], tp_args: tup
|
|||||||
)
|
)
|
||||||
|
|
||||||
LOAD_FROM_ADDRESS_ROUTER = TypeApplicationRouter[tuple[runners.RunnerBase, int], Any]()
|
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(builtins.dynamic_array, _load_dynamic_array_from_address)
|
||||||
LOAD_FROM_ADDRESS_ROUTER.add(prelude.dynamic_array, _load_dynamic_array_from_address)
|
LOAD_FROM_ADDRESS_ROUTER.add(builtins.static_array, _load_static_array_from_address)
|
||||||
LOAD_FROM_ADDRESS_ROUTER.add(prelude.static_array, _load_static_array_from_address)
|
LOAD_FROM_ADDRESS_ROUTER.add(builtins.struct, _load_struct_from_address)
|
||||||
LOAD_FROM_ADDRESS_ROUTER.add(prelude.struct, _load_struct_from_address)
|
LOAD_FROM_ADDRESS_ROUTER.add(builtins.tuple_, _load_tuple_from_address)
|
||||||
LOAD_FROM_ADDRESS_ROUTER.add(prelude.tuple_, _load_tuple_from_address)
|
|
||||||
|
|||||||
@ -11,6 +11,7 @@ from phasm.compiler import phasm_compile
|
|||||||
from phasm.optimise.removeunusedfuncs import removeunusedfuncs
|
from phasm.optimise.removeunusedfuncs import removeunusedfuncs
|
||||||
from phasm.parser import phasm_parse
|
from phasm.parser import phasm_parse
|
||||||
from phasm.type3.entry import phasm_type3
|
from phasm.type3.entry import phasm_type3
|
||||||
|
from phasm.wasmgenerator import Generator as WasmGenerator
|
||||||
|
|
||||||
Imports = Optional[Dict[str, Callable[[Any], Any]]]
|
Imports = Optional[Dict[str, Callable[[Any], Any]]]
|
||||||
|
|
||||||
@ -19,7 +20,7 @@ class RunnerBase:
|
|||||||
Base class
|
Base class
|
||||||
"""
|
"""
|
||||||
phasm_code: str
|
phasm_code: str
|
||||||
phasm_ast: ourlang.Module
|
phasm_ast: ourlang.Module[WasmGenerator]
|
||||||
wasm_ast: wasm.Module
|
wasm_ast: wasm.Module
|
||||||
wasm_asm: str
|
wasm_asm: str
|
||||||
wasm_bin: bytes
|
wasm_bin: bytes
|
||||||
|
|||||||
@ -15,7 +15,8 @@ def testEntry() -> bytes:
|
|||||||
|
|
||||||
result = Suite(code_py).run_code()
|
result = Suite(code_py).run_code()
|
||||||
|
|
||||||
assert b"Hello" == result.returned_value
|
assert b"Hello" == result.returned_value[0:5]
|
||||||
|
assert 5 == len(result.returned_value)
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
@pytest.mark.integration_test
|
||||||
def test_bytes_export_instantiation():
|
def test_bytes_export_instantiation():
|
||||||
|
|||||||
@ -5,7 +5,7 @@ from ..helpers import Suite
|
|||||||
|
|
||||||
@pytest.mark.integration_test
|
@pytest.mark.integration_test
|
||||||
@pytest.mark.parametrize('type_', ['f32', 'f64'])
|
@pytest.mark.parametrize('type_', ['f32', 'f64'])
|
||||||
def test_builtins_sqrt(type_):
|
def test_floating_sqrt(type_):
|
||||||
code_py = f"""
|
code_py = f"""
|
||||||
@exported
|
@exported
|
||||||
def testEntry() -> {type_}:
|
def testEntry() -> {type_}:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user