Compare commits
5 Commits
master
...
phasm-plat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
243d9e68f2 | ||
|
|
d1b593d4e5 | ||
|
|
7ec273a732 | ||
|
|
959b643542 | ||
|
|
a044e3ef0c |
@ -84,6 +84,10 @@ def type3(inp: type3types.Type3OrPlaceholder) -> wasm.WasmType:
|
|||||||
# Tuples are passed as pointer, which are i32
|
# Tuples are passed as pointer, which are i32
|
||||||
return wasm.WasmTypeInt32()
|
return wasm.WasmTypeInt32()
|
||||||
|
|
||||||
|
if inp.base == type3types.IO:
|
||||||
|
assert 1 == len(inp.args)
|
||||||
|
return type3(inp.args[0])
|
||||||
|
|
||||||
raise NotImplementedError(type3, inp)
|
raise NotImplementedError(type3, inp)
|
||||||
|
|
||||||
# Operators that work for i32, i64, f32, f64
|
# Operators that work for i32, i64, f32, f64
|
||||||
@ -229,7 +233,13 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
|||||||
wgn.f64.const(inp.value)
|
wgn.f64.const(inp.value)
|
||||||
return
|
return
|
||||||
|
|
||||||
raise NotImplementedError(f'Constants with type {inp.type3}')
|
raise NotImplementedError(f'Constants with type {inp.type3:s}')
|
||||||
|
|
||||||
|
if isinstance(inp, ourlang.ConstantBytes):
|
||||||
|
assert inp.data_block is not None, 'Bytes must be memory stored'
|
||||||
|
assert inp.data_block.address is not None, 'Value not allocated'
|
||||||
|
wgn.i32.const(inp.data_block.address)
|
||||||
|
return
|
||||||
|
|
||||||
if isinstance(inp, ourlang.VariableReference):
|
if isinstance(inp, ourlang.VariableReference):
|
||||||
if isinstance(inp.variable, ourlang.FunctionParam):
|
if isinstance(inp.variable, ourlang.FunctionParam):
|
||||||
@ -239,6 +249,12 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
|||||||
if isinstance(inp.variable, ourlang.ModuleConstantDef):
|
if isinstance(inp.variable, ourlang.ModuleConstantDef):
|
||||||
assert isinstance(inp.type3, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR
|
assert isinstance(inp.type3, type3types.Type3), type3types.TYPE3_ASSERTION_ERROR
|
||||||
|
|
||||||
|
if inp.type3 is type3types.bytes:
|
||||||
|
assert inp.variable.data_block is not None, 'Bytes must be memory stored'
|
||||||
|
assert inp.variable.data_block.address is not None, 'Value not allocated'
|
||||||
|
wgn.i32.const(inp.variable.data_block.address)
|
||||||
|
return
|
||||||
|
|
||||||
if isinstance(inp.type3, type3types.StructType3):
|
if isinstance(inp.type3, type3types.StructType3):
|
||||||
assert inp.variable.data_block is not None, 'Structs must be memory stored'
|
assert inp.variable.data_block is not None, 'Structs must be memory stored'
|
||||||
assert inp.variable.data_block.address is not None, 'Value not allocated'
|
assert inp.variable.data_block.address is not None, 'Value not allocated'
|
||||||
@ -557,6 +573,12 @@ def statement_if(wgn: WasmGenerator, inp: ourlang.StatementIf) -> None:
|
|||||||
# for stat in inp.else_statements:
|
# for stat in inp.else_statements:
|
||||||
# statement(wgn, stat)
|
# statement(wgn, stat)
|
||||||
|
|
||||||
|
def statement_expression(wgn: WasmGenerator, inp: ourlang.StatementExpression) -> None:
|
||||||
|
"""
|
||||||
|
Compile: Expression whose result is ignored
|
||||||
|
"""
|
||||||
|
expression(wgn, inp.expr)
|
||||||
|
|
||||||
def statement(wgn: WasmGenerator, inp: ourlang.Statement) -> None:
|
def statement(wgn: WasmGenerator, inp: ourlang.Statement) -> None:
|
||||||
"""
|
"""
|
||||||
Compile: any statement
|
Compile: any statement
|
||||||
@ -572,6 +594,10 @@ def statement(wgn: WasmGenerator, inp: ourlang.Statement) -> None:
|
|||||||
if isinstance(inp, ourlang.StatementPass):
|
if isinstance(inp, ourlang.StatementPass):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if isinstance(inp, ourlang.StatementExpression):
|
||||||
|
statement_expression(wgn, inp)
|
||||||
|
return
|
||||||
|
|
||||||
raise NotImplementedError(statement, inp)
|
raise NotImplementedError(statement, inp)
|
||||||
|
|
||||||
def function_argument(inp: ourlang.FunctionParam) -> wasm.Param:
|
def function_argument(inp: ourlang.FunctionParam) -> wasm.Param:
|
||||||
@ -587,7 +613,7 @@ def import_(inp: ourlang.Function) -> wasm.Import:
|
|||||||
assert inp.imported
|
assert inp.imported
|
||||||
|
|
||||||
return wasm.Import(
|
return wasm.Import(
|
||||||
'imports',
|
inp.imported,
|
||||||
inp.name,
|
inp.name,
|
||||||
inp.name,
|
inp.name,
|
||||||
[
|
[
|
||||||
@ -721,6 +747,12 @@ def module_data(inp: ourlang.ModuleData) -> bytes:
|
|||||||
data_list.append(module_data_f64(constant.value))
|
data_list.append(module_data_f64(constant.value))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if constant.type3 == type3types.bytes:
|
||||||
|
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, constant.value)
|
raise NotImplementedError(constant, constant.type3, constant.value)
|
||||||
|
|
||||||
block_data = b''.join(data_list)
|
block_data = b''.join(data_list)
|
||||||
|
|||||||
@ -47,15 +47,32 @@ class ConstantPrimitive(Constant):
|
|||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f'ConstantPrimitive({repr(self.value)})'
|
return f'ConstantPrimitive({repr(self.value)})'
|
||||||
|
|
||||||
|
class ConstantBytes(Constant):
|
||||||
|
"""
|
||||||
|
A bytes constant value expression within a statement
|
||||||
|
"""
|
||||||
|
__slots__ = ('value', 'data_block', )
|
||||||
|
|
||||||
|
value: bytes
|
||||||
|
data_block: 'ModuleDataBlock'
|
||||||
|
|
||||||
|
def __init__(self, value: bytes, data_block: 'ModuleDataBlock') -> None:
|
||||||
|
super().__init__()
|
||||||
|
self.value = value
|
||||||
|
self.data_block = data_block
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f'ConstantBytes({repr(self.value)}, @{repr(self.data_block.address)})'
|
||||||
|
|
||||||
class ConstantTuple(Constant):
|
class ConstantTuple(Constant):
|
||||||
"""
|
"""
|
||||||
A Tuple constant value expression within a statement
|
A Tuple constant value expression within a statement
|
||||||
"""
|
"""
|
||||||
__slots__ = ('value', )
|
__slots__ = ('value', )
|
||||||
|
|
||||||
value: List[ConstantPrimitive]
|
value: List[Union[ConstantPrimitive, ConstantBytes]]
|
||||||
|
|
||||||
def __init__(self, value: List[ConstantPrimitive]) -> None: # FIXME: Tuple of tuples?
|
def __init__(self, value: List[Union[ConstantPrimitive, ConstantBytes]]) -> None: # FIXME: Tuple of tuples?
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.value = value
|
self.value = value
|
||||||
|
|
||||||
@ -69,9 +86,9 @@ class ConstantStruct(Constant):
|
|||||||
__slots__ = ('struct_name', 'value', )
|
__slots__ = ('struct_name', 'value', )
|
||||||
|
|
||||||
struct_name: str
|
struct_name: str
|
||||||
value: List[ConstantPrimitive]
|
value: List[Union[ConstantPrimitive, ConstantBytes]]
|
||||||
|
|
||||||
def __init__(self, struct_name: str, value: List[ConstantPrimitive]) -> None: # FIXME: Struct of structs?
|
def __init__(self, struct_name: str, value: List[Union[ConstantPrimitive, ConstantBytes]]) -> None: # FIXME: Struct of structs?
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.struct_name = struct_name
|
self.struct_name = struct_name
|
||||||
self.value = value
|
self.value = value
|
||||||
@ -253,6 +270,19 @@ class StatementIf(Statement):
|
|||||||
self.statements = []
|
self.statements = []
|
||||||
self.else_statements = []
|
self.else_statements = []
|
||||||
|
|
||||||
|
class StatementExpression(Statement):
|
||||||
|
"""
|
||||||
|
An expression within a function
|
||||||
|
|
||||||
|
The result of the expression is by the code.
|
||||||
|
"""
|
||||||
|
__slots__ = ('expr', )
|
||||||
|
|
||||||
|
expr: Expression
|
||||||
|
|
||||||
|
def __init__(self, expr: Expression) -> None:
|
||||||
|
self.expr = expr
|
||||||
|
|
||||||
class FunctionParam:
|
class FunctionParam:
|
||||||
"""
|
"""
|
||||||
A parameter for a Function
|
A parameter for a Function
|
||||||
@ -275,7 +305,7 @@ class Function:
|
|||||||
name: str
|
name: str
|
||||||
lineno: int
|
lineno: int
|
||||||
exported: bool
|
exported: bool
|
||||||
imported: bool
|
imported: Optional[str]
|
||||||
statements: List[Statement]
|
statements: List[Statement]
|
||||||
returns_type3: Type3
|
returns_type3: Type3
|
||||||
posonlyargs: List[FunctionParam]
|
posonlyargs: List[FunctionParam]
|
||||||
@ -284,7 +314,7 @@ class Function:
|
|||||||
self.name = name
|
self.name = name
|
||||||
self.lineno = lineno
|
self.lineno = lineno
|
||||||
self.exported = False
|
self.exported = False
|
||||||
self.imported = False
|
self.imported = None
|
||||||
self.statements = []
|
self.statements = []
|
||||||
self.returns_type3 = type3types.none # FIXME: This could be a placeholder
|
self.returns_type3 = type3types.none # FIXME: This could be a placeholder
|
||||||
self.posonlyargs = []
|
self.posonlyargs = []
|
||||||
@ -348,10 +378,10 @@ class ModuleDataBlock:
|
|||||||
"""
|
"""
|
||||||
__slots__ = ('data', 'address', )
|
__slots__ = ('data', 'address', )
|
||||||
|
|
||||||
data: List[ConstantPrimitive]
|
data: List[Union[ConstantPrimitive, ConstantBytes]]
|
||||||
address: Optional[int]
|
address: Optional[int]
|
||||||
|
|
||||||
def __init__(self, data: List[ConstantPrimitive]) -> None:
|
def __init__(self, data: List[Union[ConstantPrimitive, ConstantBytes]]) -> None:
|
||||||
self.data = data
|
self.data = data
|
||||||
self.address = None
|
self.address = None
|
||||||
|
|
||||||
|
|||||||
@ -16,7 +16,7 @@ from .ourlang import (
|
|||||||
|
|
||||||
Expression,
|
Expression,
|
||||||
BinaryOp,
|
BinaryOp,
|
||||||
ConstantPrimitive, ConstantTuple, ConstantStruct,
|
ConstantPrimitive, ConstantBytes, ConstantTuple, ConstantStruct,
|
||||||
TupleInstantiation,
|
TupleInstantiation,
|
||||||
|
|
||||||
FunctionCall, AccessStructMember, Subscript,
|
FunctionCall, AccessStructMember, Subscript,
|
||||||
@ -27,6 +27,7 @@ from .ourlang import (
|
|||||||
|
|
||||||
Statement,
|
Statement,
|
||||||
StatementIf, StatementPass, StatementReturn,
|
StatementIf, StatementPass, StatementReturn,
|
||||||
|
StatementExpression,
|
||||||
|
|
||||||
FunctionParam,
|
FunctionParam,
|
||||||
ModuleConstantDef,
|
ModuleConstantDef,
|
||||||
@ -133,16 +134,34 @@ class OurVisitor:
|
|||||||
# Do stmts at the end so we have the return value
|
# Do stmts at the end so we have the return value
|
||||||
|
|
||||||
for decorator in node.decorator_list:
|
for decorator in node.decorator_list:
|
||||||
if not isinstance(decorator, ast.Name):
|
if isinstance(decorator, ast.Call):
|
||||||
_raise_static_error(decorator, 'Function decorators must be string')
|
if not isinstance(decorator.func, ast.Name):
|
||||||
if not isinstance(decorator.ctx, ast.Load):
|
_raise_static_error(decorator, 'Function decorators must be string')
|
||||||
_raise_static_error(decorator, 'Must be load context')
|
if not isinstance(decorator.func.ctx, ast.Load):
|
||||||
_not_implemented(decorator.id in ('exported', 'imported'), 'Custom decorators')
|
_raise_static_error(decorator, 'Must be load context')
|
||||||
|
_not_implemented(decorator.func.id == 'imported', 'Custom decorators')
|
||||||
|
|
||||||
if decorator.id == 'exported':
|
if 1 != len(decorator.args):
|
||||||
function.exported = True
|
_raise_static_error(decorator, 'One argument expected')
|
||||||
|
if not isinstance(decorator.args[0], ast.Constant):
|
||||||
|
_raise_static_error(decorator, 'Service name must be a constant')
|
||||||
|
if not isinstance(decorator.args[0].value, str):
|
||||||
|
_raise_static_error(decorator, 'Service name must be a stirng')
|
||||||
|
if 0 != len(decorator.keywords): # TODO: Allow for namespace keyword
|
||||||
|
_raise_static_error(decorator, 'No keyword arguments expected')
|
||||||
|
|
||||||
|
function.imported = decorator.args[0].value
|
||||||
else:
|
else:
|
||||||
function.imported = True
|
if not isinstance(decorator, ast.Name):
|
||||||
|
_raise_static_error(decorator, 'Function decorators must be string')
|
||||||
|
if not isinstance(decorator.ctx, ast.Load):
|
||||||
|
_raise_static_error(decorator, 'Must be load context')
|
||||||
|
_not_implemented(decorator.id in ('exported', 'imported'), 'Custom decorators')
|
||||||
|
|
||||||
|
if decorator.id == 'exported':
|
||||||
|
function.exported = True
|
||||||
|
else:
|
||||||
|
function.imported = 'imports'
|
||||||
|
|
||||||
if node.returns is not None: # Note: `-> None` would be a ast.Constant
|
if node.returns is not None: # Note: `-> None` would be a ast.Constant
|
||||||
function.returns_type3 = self.visit_type(module, node.returns)
|
function.returns_type3 = self.visit_type(module, node.returns)
|
||||||
@ -190,12 +209,20 @@ class OurVisitor:
|
|||||||
|
|
||||||
if isinstance(node.value, ast.Constant):
|
if isinstance(node.value, ast.Constant):
|
||||||
type3 = self.visit_type(module, node.annotation)
|
type3 = self.visit_type(module, node.annotation)
|
||||||
|
|
||||||
|
value_data = self.visit_Module_Constant(module, node.value)
|
||||||
|
|
||||||
|
if isinstance(value_data, ConstantBytes):
|
||||||
|
data_block = value_data.data_block
|
||||||
|
else:
|
||||||
|
data_block = None
|
||||||
|
|
||||||
return ModuleConstantDef(
|
return ModuleConstantDef(
|
||||||
node.target.id,
|
node.target.id,
|
||||||
node.lineno,
|
node.lineno,
|
||||||
type3,
|
type3,
|
||||||
self.visit_Module_Constant(module, node.value),
|
value_data,
|
||||||
None,
|
data_block,
|
||||||
)
|
)
|
||||||
|
|
||||||
if isinstance(node.value, ast.Tuple):
|
if isinstance(node.value, ast.Tuple):
|
||||||
@ -317,6 +344,9 @@ class OurVisitor:
|
|||||||
if isinstance(node, ast.Pass):
|
if isinstance(node, ast.Pass):
|
||||||
return StatementPass()
|
return StatementPass()
|
||||||
|
|
||||||
|
if isinstance(node, ast.Expr):
|
||||||
|
return StatementExpression(self.visit_Module_FunctionDef_expr(module, function, our_locals, node.value))
|
||||||
|
|
||||||
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, function: Function, our_locals: OurLocals, node: ast.expr) -> Expression:
|
||||||
@ -550,17 +580,39 @@ class OurVisitor:
|
|||||||
|
|
||||||
return Subscript(varref, slice_expr)
|
return Subscript(varref, slice_expr)
|
||||||
|
|
||||||
def visit_Module_Constant(self, module: Module, node: ast.Constant) -> ConstantPrimitive:
|
def visit_Module_Constant(self, module: Module, node: ast.Constant) -> Union[ConstantPrimitive, ConstantBytes]:
|
||||||
del module
|
|
||||||
|
|
||||||
_not_implemented(node.kind is None, 'Constant.kind')
|
_not_implemented(node.kind is None, 'Constant.kind')
|
||||||
|
|
||||||
if isinstance(node.value, (int, float, )):
|
if isinstance(node.value, (int, float, )):
|
||||||
return ConstantPrimitive(node.value)
|
return ConstantPrimitive(node.value)
|
||||||
|
|
||||||
|
if isinstance(node.value, bytes):
|
||||||
|
data_block = ModuleDataBlock([])
|
||||||
|
module.data.blocks.append(data_block)
|
||||||
|
|
||||||
|
result = ConstantBytes(node.value, data_block)
|
||||||
|
data_block.data.append(result)
|
||||||
|
return result
|
||||||
|
|
||||||
raise NotImplementedError(f'{node.value} as constant')
|
raise NotImplementedError(f'{node.value} as constant')
|
||||||
|
|
||||||
def visit_type(self, module: Module, node: ast.expr) -> type3types.Type3:
|
def visit_type(self, module: Module, node: ast.expr) -> type3types.Type3:
|
||||||
|
if isinstance(node, ast.Call):
|
||||||
|
if not isinstance(node.func, ast.Name):
|
||||||
|
_raise_static_error(node, 'Can only call Monads by name')
|
||||||
|
|
||||||
|
if node.keywords:
|
||||||
|
_raise_static_error(node, 'Monads cannot have keyword arguments')
|
||||||
|
|
||||||
|
monad_type = self.visit_type(module, node.func)
|
||||||
|
if not isinstance(monad_type, type3types.MonadType3):
|
||||||
|
_raise_static_error(node, 'Must be a Monad')
|
||||||
|
|
||||||
|
return type3types.AppliedType3(
|
||||||
|
monad_type,
|
||||||
|
(self.visit_type(module, x) for x in node.args)
|
||||||
|
)
|
||||||
|
|
||||||
if isinstance(node, ast.Constant):
|
if isinstance(node, ast.Constant):
|
||||||
if node.value is None:
|
if node.value is None:
|
||||||
return type3types.none
|
return type3types.none
|
||||||
|
|||||||
0
phasm/transformers/__init__.py
Normal file
0
phasm/transformers/__init__.py
Normal file
0
phasm/transformers/wasm/__init__.py
Normal file
0
phasm/transformers/wasm/__init__.py
Normal file
6
phasm/transformers/wasm/phasmplatform/__init__.py
Normal file
6
phasm/transformers/wasm/phasmplatform/__init__.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
from . import splitoncall
|
||||||
|
|
||||||
|
from phasm.wasm import Module
|
||||||
|
|
||||||
|
def transform(module: Module) -> None:
|
||||||
|
splitoncall.transform(module)
|
||||||
63
phasm/transformers/wasm/phasmplatform/splitoncall.py
Normal file
63
phasm/transformers/wasm/phasmplatform/splitoncall.py
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
from typing import List
|
||||||
|
|
||||||
|
from phasm.wasm import (
|
||||||
|
Function, Module, Statement
|
||||||
|
)
|
||||||
|
|
||||||
|
def is_imported(module: Module, name: str) -> bool:
|
||||||
|
for imprt in module.imports:
|
||||||
|
if name == imprt.intname:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def split_on_call_function(module: Module, function: Function) -> List[Function]:
|
||||||
|
function_list = []
|
||||||
|
statement_list = []
|
||||||
|
idx = 0
|
||||||
|
while function.statements:
|
||||||
|
stmt = function.statements.pop(0)
|
||||||
|
if not stmt.name == 'call' or is_imported(module, stmt.args[0][1:]):
|
||||||
|
statement_list.append(stmt)
|
||||||
|
continue
|
||||||
|
|
||||||
|
function_list.append(Function(
|
||||||
|
f'{function.name}.{idx}',
|
||||||
|
None,
|
||||||
|
function.params,
|
||||||
|
function.locals,
|
||||||
|
function.result,
|
||||||
|
statement_list + [stmt] + [Statement('call', f'${function.name}.{idx + 1}')]
|
||||||
|
))
|
||||||
|
statement_list = []
|
||||||
|
idx += 1
|
||||||
|
|
||||||
|
function_list.append(Function(
|
||||||
|
f'{function.name}.{idx}',
|
||||||
|
None,
|
||||||
|
function.params,
|
||||||
|
function.locals,
|
||||||
|
function.result,
|
||||||
|
statement_list
|
||||||
|
))
|
||||||
|
|
||||||
|
if function.exported_name:
|
||||||
|
function_list.append(Function(
|
||||||
|
function.name + '.e',
|
||||||
|
function.exported_name,
|
||||||
|
function.params,
|
||||||
|
[],
|
||||||
|
function.result,
|
||||||
|
[
|
||||||
|
Statement('local.get', '$' + x[0])
|
||||||
|
for x in function.params
|
||||||
|
] + [Statement('call', f'${function.name}.0')]
|
||||||
|
))
|
||||||
|
|
||||||
|
return function_list
|
||||||
|
|
||||||
|
def transform(module: Module) -> None:
|
||||||
|
new_functions = []
|
||||||
|
for func in module.functions:
|
||||||
|
new_functions.extend(split_on_call_function(module, func))
|
||||||
|
module.functions = new_functions
|
||||||
@ -323,12 +323,12 @@ class LiteralFitsConstraint(ConstraintBase):
|
|||||||
__slots__ = ('type3', 'literal', )
|
__slots__ = ('type3', 'literal', )
|
||||||
|
|
||||||
type3: types.Type3OrPlaceholder
|
type3: types.Type3OrPlaceholder
|
||||||
literal: Union[ourlang.ConstantPrimitive, ourlang.ConstantTuple, ourlang.ConstantStruct]
|
literal: Union[ourlang.ConstantPrimitive, ourlang.ConstantBytes, ourlang.ConstantTuple, ourlang.ConstantStruct]
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
type3: types.Type3OrPlaceholder,
|
type3: types.Type3OrPlaceholder,
|
||||||
literal: Union[ourlang.ConstantPrimitive, 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__(comment=comment)
|
||||||
@ -380,6 +380,12 @@ class LiteralFitsConstraint(ConstraintBase):
|
|||||||
|
|
||||||
return Error('Must be real') # FIXME: Add line information
|
return Error('Must be real') # FIXME: Add line information
|
||||||
|
|
||||||
|
if self.type3 is types.bytes:
|
||||||
|
if isinstance(self.literal.value, bytes):
|
||||||
|
return None
|
||||||
|
|
||||||
|
return Error('Must be bytes') # FIXME: Add line information
|
||||||
|
|
||||||
res: NewConstraintList
|
res: NewConstraintList
|
||||||
|
|
||||||
if isinstance(self.type3, types.AppliedType3):
|
if isinstance(self.type3, types.AppliedType3):
|
||||||
|
|||||||
@ -25,7 +25,7 @@ def phasm_type3_generate_constraints(inp: ourlang.Module) -> List[ConstraintBase
|
|||||||
return [*module(ctx, inp)]
|
return [*module(ctx, inp)]
|
||||||
|
|
||||||
def constant(ctx: Context, inp: ourlang.Constant) -> ConstraintGenerator:
|
def constant(ctx: Context, inp: ourlang.Constant) -> ConstraintGenerator:
|
||||||
if isinstance(inp, (ourlang.ConstantPrimitive, ourlang.ConstantTuple, ourlang.ConstantStruct)):
|
if isinstance(inp, (ourlang.ConstantPrimitive, ourlang.ConstantBytes, ourlang.ConstantTuple, ourlang.ConstantStruct)):
|
||||||
yield LiteralFitsConstraint(inp.type3, inp)
|
yield LiteralFitsConstraint(inp.type3, inp)
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -183,6 +183,12 @@ def statement_if(ctx: Context, fun: ourlang.Function, inp: ourlang.StatementIf)
|
|||||||
for stmt in inp.else_statements:
|
for stmt in inp.else_statements:
|
||||||
yield from statement(ctx, fun, stmt)
|
yield from statement(ctx, fun, stmt)
|
||||||
|
|
||||||
|
def statement_expression(ctx: Context, fun: ourlang.Function, inp: ourlang.StatementExpression) -> ConstraintGenerator:
|
||||||
|
yield from expression(ctx, inp.expr)
|
||||||
|
|
||||||
|
yield SameTypeConstraint(fun.returns_type3, inp.expr.type3,
|
||||||
|
comment=f'TODO')
|
||||||
|
|
||||||
def statement(ctx: Context, fun: ourlang.Function, inp: ourlang.Statement) -> ConstraintGenerator:
|
def statement(ctx: Context, fun: ourlang.Function, inp: ourlang.Statement) -> ConstraintGenerator:
|
||||||
if isinstance(inp, ourlang.StatementReturn):
|
if isinstance(inp, ourlang.StatementReturn):
|
||||||
yield from statement_return(ctx, fun, inp)
|
yield from statement_return(ctx, fun, inp)
|
||||||
@ -192,6 +198,10 @@ def statement(ctx: Context, fun: ourlang.Function, inp: ourlang.Statement) -> Co
|
|||||||
yield from statement_if(ctx, fun, inp)
|
yield from statement_if(ctx, fun, inp)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if isinstance(inp, ourlang.StatementExpression):
|
||||||
|
yield from statement_expression(ctx, fun, inp)
|
||||||
|
return
|
||||||
|
|
||||||
raise NotImplementedError(statement, fun, inp)
|
raise NotImplementedError(statement, fun, inp)
|
||||||
|
|
||||||
def function(ctx: Context, inp: ourlang.Function) -> ConstraintGenerator:
|
def function(ctx: Context, inp: ourlang.Function) -> ConstraintGenerator:
|
||||||
|
|||||||
@ -69,6 +69,14 @@ class PrimitiveType3(Type3):
|
|||||||
|
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
|
||||||
|
class MonadType3(Type3):
|
||||||
|
"""
|
||||||
|
A type method to indicate that actions have to be made
|
||||||
|
one after the other, usually because they affect outside state.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__ = ()
|
||||||
|
|
||||||
class IntType3(Type3):
|
class IntType3(Type3):
|
||||||
"""
|
"""
|
||||||
Sometimes you can have an int as type, e.g. when using static arrays
|
Sometimes you can have an int as type, e.g. when using static arrays
|
||||||
@ -146,7 +154,7 @@ class AppliedType3(Type3):
|
|||||||
"""
|
"""
|
||||||
__slots__ = ('base', 'args', )
|
__slots__ = ('base', 'args', )
|
||||||
|
|
||||||
base: PrimitiveType3
|
base: Union[PrimitiveType3, MonadType3]
|
||||||
"""
|
"""
|
||||||
The base type
|
The base type
|
||||||
"""
|
"""
|
||||||
@ -156,7 +164,7 @@ class AppliedType3(Type3):
|
|||||||
The applied types (or placeholders there for)
|
The applied types (or placeholders there for)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, base: PrimitiveType3, args: Iterable[Type3OrPlaceholder]) -> None:
|
def __init__(self, base: Union[PrimitiveType3, MonadType3], args: Iterable[Type3OrPlaceholder]) -> None:
|
||||||
args = [*args]
|
args = [*args]
|
||||||
assert args, 'Must at least one argument'
|
assert args, 'Must at least one argument'
|
||||||
|
|
||||||
@ -307,6 +315,8 @@ It should be applied with zero or more arguments. It has a compile time
|
|||||||
determined length, and each argument can be different.
|
determined length, and each argument can be different.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
IO = MonadType3('IO')
|
||||||
|
|
||||||
LOOKUP_TABLE: Dict[str, Type3] = {
|
LOOKUP_TABLE: Dict[str, Type3] = {
|
||||||
'none': none,
|
'none': none,
|
||||||
'bool': bool_,
|
'bool': bool_,
|
||||||
@ -319,4 +329,6 @@ LOOKUP_TABLE: Dict[str, Type3] = {
|
|||||||
'f32': f32,
|
'f32': f32,
|
||||||
'f64': f64,
|
'f64': f64,
|
||||||
'bytes': bytes,
|
'bytes': bytes,
|
||||||
|
|
||||||
|
'IO': IO,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,12 @@ class WatSerializable:
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError(self, 'to_wat')
|
raise NotImplementedError(self, 'to_wat')
|
||||||
|
|
||||||
|
def alloc_size(self) -> int:
|
||||||
|
"""
|
||||||
|
Returns how many bytes a variable of this type takes up in memory
|
||||||
|
"""
|
||||||
|
raise NotImplementedError(self, 'alloc_size')
|
||||||
|
|
||||||
class WasmType(WatSerializable):
|
class WasmType(WatSerializable):
|
||||||
"""
|
"""
|
||||||
Type base class
|
Type base class
|
||||||
@ -36,6 +42,9 @@ class WasmTypeInt32(WasmType):
|
|||||||
def to_wat(self) -> str:
|
def to_wat(self) -> str:
|
||||||
return 'i32'
|
return 'i32'
|
||||||
|
|
||||||
|
def alloc_size(self) -> int:
|
||||||
|
return 4
|
||||||
|
|
||||||
class WasmTypeInt64(WasmType):
|
class WasmTypeInt64(WasmType):
|
||||||
"""
|
"""
|
||||||
i64 value
|
i64 value
|
||||||
@ -52,6 +61,9 @@ class WasmTypeFloat32(WasmType):
|
|||||||
def to_wat(self) -> str:
|
def to_wat(self) -> str:
|
||||||
return 'f32'
|
return 'f32'
|
||||||
|
|
||||||
|
def alloc_size(self) -> int:
|
||||||
|
return 4
|
||||||
|
|
||||||
class WasmTypeFloat64(WasmType):
|
class WasmTypeFloat64(WasmType):
|
||||||
"""
|
"""
|
||||||
f64 value
|
f64 value
|
||||||
@ -179,6 +191,34 @@ class ModuleMemory(WatSerializable):
|
|||||||
'(export "memory" (memory 0))\n'
|
'(export "memory" (memory 0))\n'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
class TableElement(WatSerializable):
|
||||||
|
"""
|
||||||
|
Represents a Web Assembly table element
|
||||||
|
"""
|
||||||
|
def __init__(self, offset: int, args: Iterable[str]) -> None:
|
||||||
|
self.offset = offset
|
||||||
|
self.args = [*args]
|
||||||
|
|
||||||
|
def to_wat(self) -> str:
|
||||||
|
args = ' '.join(self.args)
|
||||||
|
|
||||||
|
return f'(elem (i32.const {self.offset}) {args})'
|
||||||
|
|
||||||
|
class Table(WatSerializable):
|
||||||
|
"""
|
||||||
|
Represents a Web Assembly table
|
||||||
|
"""
|
||||||
|
def __init__(self, size: int, typ: str, elements: List[TableElement]) -> None:
|
||||||
|
self.size = size
|
||||||
|
self.type = typ
|
||||||
|
self.elements = [*elements]
|
||||||
|
|
||||||
|
def to_wat(self) -> str:
|
||||||
|
return (
|
||||||
|
f'(table {self.size} {self.type})\n '
|
||||||
|
+ '\n '.join(x.to_wat() for x in self.elements)
|
||||||
|
)
|
||||||
|
|
||||||
class Module(WatSerializable):
|
class Module(WatSerializable):
|
||||||
"""
|
"""
|
||||||
Represents a Web Assembly module
|
Represents a Web Assembly module
|
||||||
@ -187,13 +227,15 @@ class Module(WatSerializable):
|
|||||||
self.imports: List[Import] = []
|
self.imports: List[Import] = []
|
||||||
self.functions: List[Function] = []
|
self.functions: List[Function] = []
|
||||||
self.memory = ModuleMemory()
|
self.memory = ModuleMemory()
|
||||||
|
self.tables: List[Table] = []
|
||||||
|
|
||||||
def to_wat(self) -> str:
|
def to_wat(self) -> str:
|
||||||
"""
|
"""
|
||||||
Generates the text version
|
Generates the text version
|
||||||
"""
|
"""
|
||||||
return '(module\n {}\n {}\n {})\n'.format(
|
return '(module\n {}\n {}\n {}\n {})\n'.format(
|
||||||
'\n '.join(x.to_wat() for x in self.imports),
|
'\n '.join(x.to_wat() for x in self.imports),
|
||||||
self.memory.to_wat(),
|
self.memory.to_wat(),
|
||||||
|
'\n '.join(x.to_wat() for x in self.tables),
|
||||||
'\n '.join(x.to_wat() for x in self.functions),
|
'\n '.join(x.to_wat() for x in self.functions),
|
||||||
)
|
)
|
||||||
|
|||||||
@ -3,6 +3,9 @@ from typing import Any, Callable
|
|||||||
class Module:
|
class Module:
|
||||||
...
|
...
|
||||||
|
|
||||||
|
def link_function(self, module_name: str, function_name: str, function: Callable[[Any], Any]) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
class Runtime:
|
class Runtime:
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|||||||
@ -152,12 +152,13 @@ class RunnerPywasm3(RunnerBase):
|
|||||||
self.rtime = self.env.new_runtime(1024 * 1024)
|
self.rtime = self.env.new_runtime(1024 * 1024)
|
||||||
|
|
||||||
def interpreter_load(self, imports: Optional[Dict[str, Callable[[Any], Any]]] = None) -> None:
|
def interpreter_load(self, imports: Optional[Dict[str, Callable[[Any], Any]]] = None) -> None:
|
||||||
if imports is not None:
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
self.mod = self.env.parse_module(self.wasm_bin)
|
self.mod = self.env.parse_module(self.wasm_bin)
|
||||||
self.rtime.load(self.mod)
|
self.rtime.load(self.mod)
|
||||||
|
|
||||||
|
if imports is not None:
|
||||||
|
for key, val in imports.items():
|
||||||
|
self.mod.link_function('imports', key, val)
|
||||||
|
|
||||||
def interpreter_write_memory(self, offset: int, data: Iterable[int]) -> None:
|
def interpreter_write_memory(self, offset: int, data: Iterable[int]) -> None:
|
||||||
memory = self.rtime.get_memory(0)
|
memory = self.rtime.get_memory(0)
|
||||||
|
|
||||||
|
|||||||
0
tests/integration/test_transformers/__init__.py
Normal file
0
tests/integration/test_transformers/__init__.py
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
from phasm import wasm
|
||||||
|
|
||||||
|
from ...runners import RunnerPywasm, RunnerPywasm3, RunnerWasmtime, RunnerWasmer
|
||||||
|
|
||||||
|
from phasm.transformers.wasm import phasmplatform as sut
|
||||||
|
|
||||||
|
none = wasm.WasmTypeNone()
|
||||||
|
f32 = wasm.WasmTypeFloat32()
|
||||||
|
|
||||||
|
def run(module, func, *, imports = None):
|
||||||
|
# runner = RunnerPywasm('-')
|
||||||
|
runner = RunnerPywasm3('-')
|
||||||
|
# runner = RunnerWasmtime('-')
|
||||||
|
runner.wasm_ast = module
|
||||||
|
runner.compile_wat()
|
||||||
|
runner.dump_wasm_wat(sys.stderr)
|
||||||
|
runner.compile_wasm()
|
||||||
|
runner.interpreter_setup()
|
||||||
|
runner.interpreter_load(imports)
|
||||||
|
return runner.call(func)
|
||||||
|
|
||||||
|
|
||||||
|
def return_f32_30_0() -> float:
|
||||||
|
return 30.0
|
||||||
|
|
||||||
|
|
||||||
|
def make_return_f32_30_0_import():
|
||||||
|
return wasm.Import('imports', 'return_f32_30_0', 'return_f32_30_0', [], f32)
|
||||||
|
|
||||||
|
|
||||||
|
def make_func1():
|
||||||
|
"""
|
||||||
|
func1: No params, just a return value
|
||||||
|
|
||||||
|
In Haskell:
|
||||||
|
func1 :: f32
|
||||||
|
func1 = 30
|
||||||
|
"""
|
||||||
|
return wasm.Function('func1', 'func1', [], [], f32, [
|
||||||
|
wasm.Statement('f32.const', '30'),
|
||||||
|
wasm.Statement('return'),
|
||||||
|
])
|
||||||
|
|
||||||
|
def make_func2():
|
||||||
|
"""
|
||||||
|
func2: Calls imported method
|
||||||
|
|
||||||
|
In Haskell:
|
||||||
|
return_f32_30_0 :: IO(f32)
|
||||||
|
|
||||||
|
func2 :: IO(f32)
|
||||||
|
func2 = return_f32_30_0
|
||||||
|
"""
|
||||||
|
return wasm.Function('func2', 'func2', [], [], f32, [
|
||||||
|
wasm.Statement('call', '$return_f32_30_0'),
|
||||||
|
wasm.Statement('return'),
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
def test_const_return_func():
|
||||||
|
module = wasm.Module()
|
||||||
|
module.functions.append(make_func1())
|
||||||
|
|
||||||
|
assert 30 == run(module, 'func1')
|
||||||
|
|
||||||
|
sut.transform(module)
|
||||||
|
|
||||||
|
assert 30 == run(module, 'func1')
|
||||||
|
|
||||||
|
def test_call_imported_method():
|
||||||
|
module = wasm.Module()
|
||||||
|
module.imports.append(make_return_f32_30_0_import())
|
||||||
|
module.functions.append(make_func2())
|
||||||
|
|
||||||
|
assert 30 == run(module, 'func2', imports={'return_f32_30_0': return_f32_30_0})
|
||||||
|
|
||||||
|
sut.transform(module)
|
||||||
|
|
||||||
|
assert 30 == run(module, 'func2', imports={'return_f32_30_0': return_f32_30_0})
|
||||||
|
|
||||||
|
def test_parameters():
|
||||||
|
assert 0
|
||||||
|
|
||||||
|
# func1: No params, no return value
|
||||||
|
# func1 = func4 10 20
|
||||||
|
func1_statements = [
|
||||||
|
wasm.Statement('f32.const', '30'),
|
||||||
|
wasm.Statement('f32.const', '8'),
|
||||||
|
wasm.Statement('call', '$func3'),
|
||||||
|
wasm.Statement('f32.const', '0'),
|
||||||
|
wasm.Statement('call', '$log'),
|
||||||
|
]
|
||||||
|
func1 = wasm.Function('func1', 'func1', [], [], none, func1_statements)
|
||||||
|
|
||||||
|
|
||||||
|
# func2: No params, with return value
|
||||||
|
# func2 = remote_value_get
|
||||||
|
func2_statements = [
|
||||||
|
wasm.Statement('call', '$remote_value_get'),
|
||||||
|
]
|
||||||
|
func2 = wasm.Function('func2', 'func2', [], [], f32, func2_statements)
|
||||||
|
|
||||||
|
# With params, no return value
|
||||||
|
# func3 left right = (func4 left right) + func2
|
||||||
|
func3_statements = [
|
||||||
|
wasm.Statement('local.get', '$left'),
|
||||||
|
wasm.Statement('local.get', '$right'),
|
||||||
|
wasm.Statement('call', '$func4'),
|
||||||
|
wasm.Statement('call', '$func2'),
|
||||||
|
wasm.Statement('f32.add'),
|
||||||
|
wasm.Statement('call', '$log'),
|
||||||
|
]
|
||||||
|
func3 = wasm.Function('func3', 'func3', [
|
||||||
|
('left', f32, ),
|
||||||
|
('right', f32, ),
|
||||||
|
], [], none, func3_statements)
|
||||||
|
|
||||||
|
# func4: With params, and return value
|
||||||
|
# func4 l r = l * 2 + r
|
||||||
|
func4_statements = [
|
||||||
|
wasm.Statement('local.get', '$left'),
|
||||||
|
wasm.Statement('f32.const', '2'),
|
||||||
|
wasm.Statement('f32.mul'),
|
||||||
|
wasm.Statement('local.get', '$right'),
|
||||||
|
wasm.Statement('f32.add'),
|
||||||
|
wasm.Statement('return'),
|
||||||
|
]
|
||||||
|
func4 = wasm.Function('func4', None, [
|
||||||
|
('left', f32, ),
|
||||||
|
('right', f32, ),
|
||||||
|
], [], f32, func4_statements)
|
||||||
|
|
||||||
|
module = wasm.Module()
|
||||||
|
module.imports.append(wasm.Import('imports', 'log', 'log', [('a', f32, )], none))
|
||||||
|
module.imports.append(wasm.Import('imports', 'remote_value_get', 'remote_value_get', [], f32))
|
||||||
|
module.functions.append(func1)
|
||||||
|
module.functions.append(func2)
|
||||||
|
module.functions.append(func3)
|
||||||
|
module.functions.append(func4)
|
||||||
|
|
||||||
|
def my_remote_value_get() -> float:
|
||||||
|
return 19.0
|
||||||
|
|
||||||
|
log = []
|
||||||
|
def my_log(a: float) -> None:
|
||||||
|
log.append(a)
|
||||||
|
|
||||||
|
imports = {'log': my_log, 'remote_value_get': my_remote_value_get}
|
||||||
|
|
||||||
|
run(module, 'func1', imports=imports)
|
||||||
|
|
||||||
|
assert [87, 0] == log
|
||||||
|
|
||||||
|
sut.transform(module)
|
||||||
|
|
||||||
|
run(module, 'func1', imports=imports)
|
||||||
|
|
||||||
|
assert 0
|
||||||
|
|
||||||
|
assert [87, 0] == log
|
||||||
|
|
||||||
|
# def test_locals():
|
||||||
Loading…
x
Reference in New Issue
Block a user