diff --git a/phasm/build/__init__.py b/phasm/build/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/phasm/build/base.py b/phasm/build/base.py new file mode 100644 index 0000000..d72d34f --- /dev/null +++ b/phasm/build/base.py @@ -0,0 +1,291 @@ +""" +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.types import ( + IntType3, + Type3, + TypeConstructor_Base, + TypeConstructor_DynamicArray, + TypeConstructor_StaticArray, + TypeConstructor_Struct, + TypeConstructor_Tuple, +) +from ..type3.typeclasses import Type3Class, Type3ClassMethod +from ..type3.routers import ( + NoRouteForTypeException, + TypeApplicationRouter, + TypeClassArgsRouter, + TypeVariableLookup, +) +from ..wasm import WasmType, WasmTypeInt32 + +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), +]) + +class MissingImplementationWarning(Warning): + pass + +class BuildBase[G]: + __slots__ = ( + 'dynamic_array', + 'static_array', + 'struct', + 'tuple_', + + 'none_', + + 'type_info_map', + 'type_info_constructed', + + 'types', + '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. + """ + + 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(). + """ + + 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_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.static_array = builtins.static_array + self.struct = builtins.struct + self.tuple_ = builtins.tuple_ + + self.none_ = builtins.none + + self.type_info_map = {} + self.type_info_constructed = TypeInfo('t a', WasmTypeInt32, 'i32.load', 'i32.store', 4) + + self.types = { + 'None': self.none_, + } + 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 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) + + # TODO: Check for required existing instantiations + + # 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}') diff --git a/phasm/build/builtins.py b/phasm/build/builtins.py new file mode 100644 index 0000000..4c5bff6 --- /dev/null +++ b/phasm/build/builtins.py @@ -0,0 +1,22 @@ +""" +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_StaticArray, + TypeConstructor_Struct, + TypeConstructor_Tuple, +) + +dynamic_array = TypeConstructor_DynamicArray('dynamic_array') +static_array = TypeConstructor_StaticArray('static_array') +struct = TypeConstructor_Struct('struct') +tuple_ = TypeConstructor_Tuple('tuple') + +none = Type3('none', TypeApplication_Nullary(None, None)) diff --git a/phasm/build/default.py b/phasm/build/default.py new file mode 100644 index 0000000..7e811b7 --- /dev/null +++ b/phasm/build/default.py @@ -0,0 +1,80 @@ +""" +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 ..stdlib import types as stdlib_types +from ..type3.functions import ( + TypeVariable, + TypeVariableApplication_Nullary, +) +from ..type3.typeclasses import Type3Class +from ..type3.types import ( + Type3, + TypeApplication_Nullary, +) +from ..wasm import ( + WasmTypeFloat32, + WasmTypeFloat64, + WasmTypeInt32, + WasmTypeInt64, +) +from ..wasmgenerator import Generator + +from .base import BuildBase, TypeInfo + +class BuildDefault(BuildBase[Generator]): + def __init__(self) -> None: + super().__init__() + + u32 = Type3('f32', TypeApplication_Nullary(None, None)) + f32 = Type3('f32', TypeApplication_Nullary(None, None)) + f64 = Type3('f64', TypeApplication_Nullary(None, None)) + + self.type_info_map.update({ + 'u32': TypeInfo('u32', WasmTypeInt32, 'i32.load', 'i32.store', 4), + 'f32': TypeInfo('f32', WasmTypeFloat32, 'f32.load', 'f32.store', 4), + 'f64': TypeInfo('f64', WasmTypeFloat64, 'f64.load', 'f64.store', 8), + }) + + self.types.update({ + 'u32': u32, + 'f32': f32, + 'f64': f64, + }) + + a = TypeVariable('a', TypeVariableApplication_Nullary(None, None)) + + 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 + }) + + self.instance_type_class(NatNum, f32, operators={ + '+': stdlib_types.f32_natnum_add, + '-': stdlib_types.f32_natnum_sub, + '*': stdlib_types.f32_natnum_mul, + '<<': stdlib_types.f32_natnum_arithmic_shift_left, + '>>': stdlib_types.f32_natnum_arithmic_shift_right, + }) + self.instance_type_class(NatNum, f64, operators={ + '+': stdlib_types.f64_natnum_add, + '-': stdlib_types.f64_natnum_sub, + '*': stdlib_types.f64_natnum_mul, + '<<': stdlib_types.f64_natnum_arithmic_shift_left, + '>>': stdlib_types.f64_natnum_arithmic_shift_right, + }) + + self.methods.update({ + **NatNum.methods, + }) + self.operators.update({ + **NatNum.operators, + }) diff --git a/phasm/codestyle.py b/phasm/codestyle.py index 940ecfa..bd377fb 100644 --- a/phasm/codestyle.py +++ b/phasm/codestyle.py @@ -5,7 +5,7 @@ It's intented to be a "any color, as long as it's black" kind of renderer """ from typing import Generator -from . import ourlang, prelude +from . import ourlang from .type3.types import Type3, TypeApplication_Struct @@ -21,9 +21,6 @@ def type3(inp: Type3) -> str: """ Render: type's name """ - if inp is prelude.none: - return 'None' - return inp.name def struct_definition(inp: ourlang.StructDefinition) -> str: diff --git a/phasm/compiler.py b/phasm/compiler.py index e2df411..bf2c2a1 100644 --- a/phasm/compiler.py +++ b/phasm/compiler.py @@ -5,7 +5,7 @@ import struct from typing import List from . import ourlang, prelude, wasm -from .runtime import calculate_alloc_size, calculate_member_offset +from .build import builtins from .stdlib import alloc as stdlib_alloc from .stdlib import types as stdlib_types from .stdlib.types import TYPE_INFO_CONSTRUCTED, TYPE_INFO_MAP @@ -24,6 +24,12 @@ from .type3.types import ( TypeConstructor_StaticArray, TypeConstructor_Tuple, ) +from .wasm import ( + WasmTypeInt32, + WasmTypeInt64, + WasmTypeFloat32, + WasmTypeFloat64, +) from .wasmgenerator import Generator as WasmGenerator TYPE3_ASSERTION_ERROR = 'You must call phasm_type3 after calling phasm_parse before your program can be compiled' @@ -187,11 +193,11 @@ def expression_subscript_tuple( wgn.add_statement(el_type_info.wasm_load_func, f'offset={offset}') SUBSCRIPT_ROUTER = TypeApplicationRouter[tuple[WasmGenerator, ourlang.Module, ourlang.Subscript], None]() -SUBSCRIPT_ROUTER.add_n(prelude.bytes_, expression_subscript_bytes) -SUBSCRIPT_ROUTER.add(prelude.static_array, expression_subscript_static_array) -SUBSCRIPT_ROUTER.add(prelude.tuple_, expression_subscript_tuple) +# SUBSCRIPT_ROUTER.add(builtins.dynamic_array, expression_subscript_dynamic_array) +SUBSCRIPT_ROUTER.add(builtins.static_array, expression_subscript_static_array) +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 """ @@ -202,34 +208,23 @@ def expression(wgn: WasmGenerator, mod: ourlang.Module, inp: ourlang.Expression) if isinstance(inp, ourlang.ConstantPrimitive): assert inp.type3 is not None, TYPE3_ASSERTION_ERROR - if inp.type3 in (prelude.i8, prelude.u8, ): - # No native u8 type - treat as i32, with caution + type_info = mod.build.type_info_map[inp.type3.name] + if type_info.wasm_type is WasmTypeInt32: assert isinstance(inp.value, int) wgn.i32.const(inp.value) return - if inp.type3 in (prelude.i16, prelude.u16, ): - # 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, ): + if type_info.wasm_type is WasmTypeInt64: assert isinstance(inp.value, int) wgn.i64.const(inp.value) return - if inp.type3 == prelude.f32: + if type_info.wasm_type is WasmTypeFloat32: assert isinstance(inp.value, float) wgn.f32.const(inp.value) return - if inp.type3 == prelude.f64: + if type_info.wasm_type is WasmTypeFloat64: assert isinstance(inp.value, float) wgn.f64.const(inp.value) return @@ -285,7 +280,7 @@ def expression(wgn: WasmGenerator, mod: ourlang.Module, inp: ourlang.Expression) 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) return diff --git a/phasm/ourlang.py b/phasm/ourlang.py index 2bd3cac..f5cb765 100644 --- a/phasm/ourlang.py +++ b/phasm/ourlang.py @@ -4,6 +4,7 @@ Contains the syntax tree for ourlang from typing import Dict, Iterable, List, Optional, Union from . import prelude +from .build.base import BuildBase from .type3.functions import FunctionSignature, TypeVariableContext from .type3.typeclasses import Type3ClassMethod from .type3.types import Type3, TypeApplication_Struct @@ -288,14 +289,14 @@ class Function: returns_type3: Type3 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.lineno = lineno self.exported = False self.imported = None self.statements = [] self.signature = FunctionSignature(TypeVariableContext(), []) - self.returns_type3 = prelude.none # FIXME: This could be a placeholder + self.returns_type3 = returns_type3 self.posonlyargs = [] class StructDefinition: @@ -323,7 +324,7 @@ class StructConstructor(Function): struct_type3: Type3 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) @@ -331,7 +332,6 @@ class StructConstructor(Function): self.posonlyargs.append(FunctionParam(mem, typ, )) self.signature.args.append(typ) - self.returns_type3 = struct_type3 self.signature.args.append(struct_type3) self.struct_type3 = struct_type3 @@ -377,25 +377,30 @@ class ModuleData: def __init__(self) -> None: self.blocks = [] -class Module: +class Module[G]: """ 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 types: dict[str, Type3] struct_definitions: Dict[str, StructDefinition] constant_defs: Dict[str, ModuleConstantDef] functions: Dict[str, Function] + methods: Dict[str, Type3ClassMethod] operators: Dict[str, Type3ClassMethod] functions_table: dict[Function, int] - def __init__(self) -> None: + def __init__(self, build: BuildBase[G]) -> None: + self.build = build + self.data = ModuleData() self.types = {} self.struct_definitions = {} self.constant_defs = {} self.functions = {} + self.methods = {} self.operators = {} self.functions_table = {} diff --git a/phasm/parser.py b/phasm/parser.py index e72946d..595d2b1 100644 --- a/phasm/parser.py +++ b/phasm/parser.py @@ -5,6 +5,8 @@ import ast 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 .ourlang import ( AccessStructMember, @@ -31,12 +33,12 @@ from .ourlang import ( TupleInstantiation, VariableReference, ) -from .prelude import PRELUDE_METHODS, PRELUDE_OPERATORS, PRELUDE_TYPES from .type3.typeclasses import Type3ClassMethod 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 """ @@ -44,7 +46,8 @@ def phasm_parse(source: str) -> Module: res = OptimizerTransformer().visit(res) - our_visitor = OurVisitor() + build = BuildDefault() + our_visitor = OurVisitor(build) return our_visitor.visit_Module(res) OurLocals = Dict[str, Union[FunctionParam]] # FIXME: Does it become easier if we add ModuleConstantDef to this dict? @@ -75,7 +78,7 @@ class OptimizerTransformer(ast.NodeTransformer): return node.operand return node -class OurVisitor: +class OurVisitor[G]: """ Class to visit a Python syntax tree and create an ourlang syntax tree @@ -90,14 +93,14 @@ class OurVisitor: # pylint: disable=C0103,C0116,C0301,R0201,R0912 - def __init__(self) -> None: - pass + def __init__(self, build: BuildBase[G]) -> None: + self.build = build - def visit_Module(self, node: ast.Module) -> Module: - module = Module() + def visit_Module(self, node: ast.Module) -> Module[G]: + module = Module(self.build) - module.operators.update(PRELUDE_OPERATORS) - 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') @@ -140,7 +143,7 @@ class OurVisitor: 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): return self.pre_visit_Module_FunctionDef(module, node) @@ -153,7 +156,7 @@ class OurVisitor: raise NotImplementedError(f'{node} on Module') def pre_visit_Module_FunctionDef(self, module: Module, 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') @@ -477,8 +480,8 @@ class OurVisitor: func: Union[Function, FunctionParam, Type3ClassMethod] - if node.func.id in PRELUDE_METHODS: - func = PRELUDE_METHODS[node.func.id] + if node.func.id in module.methods: + func = module.methods[node.func.id] elif node.func.id in our_locals: func = our_locals[node.func.id] else: diff --git a/phasm/prelude/__init__.py b/phasm/prelude/__init__.py index f651ff9..e69de29 100644 --- a/phasm/prelude/__init__.py +++ b/phasm/prelude/__init__.py @@ -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, -} diff --git a/phasm/runtime.py b/phasm/runtime.py deleted file mode 100644 index d456971..0000000 --- a/phasm/runtime.py +++ /dev/null @@ -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}') diff --git a/phasm/stdlib/types.py b/phasm/stdlib/types.py index 38c5c98..b0cafac 100644 --- a/phasm/stdlib/types.py +++ b/phasm/stdlib/types.py @@ -1,13 +1,13 @@ """ stdlib: Standard types that are not wasm primitives """ -from typing import Mapping, NamedTuple, Type +from typing import Mapping +from phasm.build.base import TypeInfo from phasm.stdlib import alloc from phasm.type3.routers import TypeVariableLookup from phasm.type3.types import IntType3, Type3 from phasm.wasm import ( - WasmType, WasmTypeFloat32, WasmTypeFloat64, WasmTypeInt32, @@ -18,22 +18,6 @@ from phasm.wasmgenerator import Generator, func_wrapper from phasm.wasmgenerator import VarType_i32 as i32 from phasm.wasmgenerator import VarType_i64 as i64 -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), -]) - TYPE_INFO_MAP: Mapping[str, TypeInfo] = { 'none': TypeInfo('none', WasmTypeNone, 'nop', 'nop', 0), 'bool': TypeInfo('bool', WasmTypeInt32, 'i32.load8_u', 'i32.store8', 1), diff --git a/phasm/type3/constraints.py b/phasm/type3/constraints.py index cd1175c..ecf41a7 100644 --- a/phasm/type3/constraints.py +++ b/phasm/type3/constraints.py @@ -5,7 +5,8 @@ These need to be resolved before the program can be compiled. """ from typing import Any, Dict, Iterable, List, Optional, Tuple, Union -from .. import ourlang, prelude +from .. import ourlang +from ..build import builtins from .functions import FunctionArgument, TypeVariable from .placeholders import PlaceholderForType, Type3OrPlaceholder from .routers import NoRouteForTypeException, TypeApplicationRouter @@ -271,7 +272,7 @@ class SameFunctionArgumentConstraint(ConstraintBase): return [ SameTypeConstraint( typ, - prelude.function(*exp_type_arg_list), + builtins.function(*exp_type_arg_list), comment=self.comment, ) ] @@ -326,9 +327,9 @@ class TupleMatchConstraint(ConstraintBase): ] 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) + GENERATE_ROUTER.add(builtins.dynamic_array, _generate_dynamic_array) + GENERATE_ROUTER.add(builtins.static_array, _generate_static_array) + GENERATE_ROUTER.add(builtins.tuple_, _generate_tuple) def check(self) -> CheckResult: exp_type = self.exp_type @@ -530,10 +531,10 @@ class LiteralFitsConstraint(ConstraintBase): 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) + GENERATE_ROUTER.add(builtins.dynamic_array, _generate_dynamic_array) + GENERATE_ROUTER.add(builtins.static_array, _generate_static_array) + GENERATE_ROUTER.add(builtins.struct, _generate_struct) + GENERATE_ROUTER.add(builtins.tuple_, _generate_tuple) def check(self) -> CheckResult: int_table: Dict[str, Tuple[int, bool]] = { @@ -581,7 +582,7 @@ class LiteralFitsConstraint(ConstraintBase): return Error('Must be real', comment=self.comment) # FIXME: Add line information - if self.type3 is prelude.bytes_: + if self.type3 is builtins.bytes_: if isinstance(self.literal.value, bytes): return None @@ -666,9 +667,9 @@ class CanBeSubscriptedConstraint(ConstraintBase): ] 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) + # GENERATE_ROUTER.add_n(builtins.bytes_, _generate_bytes) + GENERATE_ROUTER.add(builtins.static_array, _generate_static_array) + GENERATE_ROUTER.add(builtins.tuple_, _generate_tuple) def check(self) -> CheckResult: if self.type3.resolve_as is None: diff --git a/phasm/type3/constraintsgenerator.py b/phasm/type3/constraintsgenerator.py index f28b88f..9d6a2b0 100644 --- a/phasm/type3/constraintsgenerator.py +++ b/phasm/type3/constraintsgenerator.py @@ -32,7 +32,7 @@ ConstraintGenerator = Generator[ConstraintBase, None, None] def phasm_type3_generate_constraints(inp: ourlang.Module) -> List[ConstraintBase]: ctx = Context() - ctx.type_class_instances_existing.update(prelude.PRELUDE_TYPE_CLASS_INSTANCES_EXISTING) + ctx.type_class_instances_existing.update(inp.build.type_class_instances) return [*module(ctx, inp)] diff --git a/phasm/type3/types.py b/phasm/type3/types.py index e6bc4f2..9406c1b 100644 --- a/phasm/type3/types.py +++ b/phasm/type3/types.py @@ -79,7 +79,7 @@ class Type3(KindArgument): def __eq__(self, other: Any) -> bool: if not isinstance(other, Type3): - raise NotImplementedError + raise NotImplementedError(other) return self is other diff --git a/tests/integration/helpers.py b/tests/integration/helpers.py index 192ee21..8c1f426 100644 --- a/tests/integration/helpers.py +++ b/tests/integration/helpers.py @@ -4,16 +4,15 @@ import sys from typing import Any, Callable, Generator, Iterable, List, TextIO, Union from phasm import compiler, prelude +from phasm.build import builtins 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.routers import NoRouteForTypeException, TypeApplicationRouter +from phasm.wasm import ( + WasmTypeFloat32, + WasmTypeFloat64, +) from . import runners @@ -285,11 +284,11 @@ def _allocate_memory_stored_tuple(attrs: tuple[runners.RunnerBase, Any], tp_args return adr ALLOCATE_MEMORY_STORED_ROUTER = TypeApplicationRouter[tuple[runners.RunnerBase, Any], Any]() -ALLOCATE_MEMORY_STORED_ROUTER.add_n(prelude.bytes_, _allocate_memory_stored_bytes) -ALLOCATE_MEMORY_STORED_ROUTER.add(prelude.dynamic_array, _allocate_memory_stored_dynamic_array) -ALLOCATE_MEMORY_STORED_ROUTER.add(prelude.static_array, _allocate_memory_stored_static_array) -ALLOCATE_MEMORY_STORED_ROUTER.add(prelude.struct, _allocate_memory_stored_struct) -ALLOCATE_MEMORY_STORED_ROUTER.add(prelude.tuple_, _allocate_memory_stored_tuple) +# 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(builtins.static_array, _allocate_memory_stored_static_array) +ALLOCATE_MEMORY_STORED_ROUTER.add(builtins.struct, _allocate_memory_stored_struct) +ALLOCATE_MEMORY_STORED_ROUTER.add(builtins.tuple_, _allocate_memory_stored_tuple) def _load_memory_stored_returned_value( runner: runners.RunnerBase, @@ -298,48 +297,50 @@ def _load_memory_stored_returned_value( ) -> Any: ret_type3 = runner.phasm_ast.functions[func_name].returns_type3 - if ret_type3 is prelude.none: - return None + type_info = runner.phasm_ast.build.type_info_map[ret_type3.name] - if ret_type3 is prelude.bool_: - assert isinstance(wasm_value, int), wasm_value - return 0 != wasm_value + # if ret_type3 is prelude.none: + # return None - if ret_type3 in (prelude.i8, prelude.i16, prelude.i32, prelude.i64): - assert isinstance(wasm_value, int), wasm_value + # if ret_type3 is prelude.bool_: + # assert isinstance(wasm_value, int), wasm_value + # return 0 != wasm_value - if ret_type3 is prelude.i8: - # Values are actually i32 - # Have to reinterpret to load proper value - data = struct.pack('