Cleanup to wasm.py
This commit is contained in:
parent
c181c61040
commit
14eede6b06
@ -16,8 +16,8 @@ def main(source: str, sink: str) -> int:
|
|||||||
code_py = fil.read()
|
code_py = fil.read()
|
||||||
|
|
||||||
our_module = our_process(code_py, source)
|
our_module = our_process(code_py, source)
|
||||||
wat_module = module(our_module)
|
wasm_module = module(our_module)
|
||||||
code_wat = wat_module.generate()
|
code_wat = wasm_module.to_wat()
|
||||||
|
|
||||||
with open(sink, 'w') as fil:
|
with open(sink, 'w') as fil:
|
||||||
fil.write(code_wat)
|
fil.write(code_wat)
|
||||||
|
|||||||
@ -8,31 +8,31 @@ from . import wasm
|
|||||||
|
|
||||||
Statements = Generator[wasm.Statement, None, None]
|
Statements = Generator[wasm.Statement, None, None]
|
||||||
|
|
||||||
def type_(inp: ourlang.OurType) -> wasm.OurType:
|
def type_(inp: ourlang.OurType) -> wasm.WasmType:
|
||||||
if isinstance(inp, ourlang.OurTypeNone):
|
if isinstance(inp, ourlang.OurTypeNone):
|
||||||
return wasm.OurTypeNone()
|
return wasm.WasmTypeNone()
|
||||||
|
|
||||||
if isinstance(inp, ourlang.OurTypeUInt8):
|
if isinstance(inp, ourlang.OurTypeUInt8):
|
||||||
# WebAssembly has only support for 32 and 64 bits
|
# WebAssembly has only support for 32 and 64 bits
|
||||||
# So we need to store more memory per byte
|
# So we need to store more memory per byte
|
||||||
return wasm.OurTypeInt32()
|
return wasm.WasmTypeInt32()
|
||||||
|
|
||||||
if isinstance(inp, ourlang.OurTypeInt32):
|
if isinstance(inp, ourlang.OurTypeInt32):
|
||||||
return wasm.OurTypeInt32()
|
return wasm.WasmTypeInt32()
|
||||||
|
|
||||||
if isinstance(inp, ourlang.OurTypeInt64):
|
if isinstance(inp, ourlang.OurTypeInt64):
|
||||||
return wasm.OurTypeInt64()
|
return wasm.WasmTypeInt64()
|
||||||
|
|
||||||
if isinstance(inp, ourlang.OurTypeFloat32):
|
if isinstance(inp, ourlang.OurTypeFloat32):
|
||||||
return wasm.OurTypeFloat32()
|
return wasm.WasmTypeFloat32()
|
||||||
|
|
||||||
if isinstance(inp, ourlang.OurTypeFloat64):
|
if isinstance(inp, ourlang.OurTypeFloat64):
|
||||||
return wasm.OurTypeFloat64()
|
return wasm.WasmTypeFloat64()
|
||||||
|
|
||||||
if isinstance(inp, (ourlang.Struct, ourlang.OurTypeTuple, ourlang.OurTypeBytes)):
|
if isinstance(inp, (ourlang.Struct, ourlang.OurTypeTuple, ourlang.OurTypeBytes)):
|
||||||
# Structs and tuples are passed as pointer
|
# Structs and tuples are passed as pointer
|
||||||
# And pointers are i32
|
# And pointers are i32
|
||||||
return wasm.OurTypeInt32()
|
return wasm.WasmTypeInt32()
|
||||||
|
|
||||||
raise NotImplementedError(type_, inp)
|
raise NotImplementedError(type_, inp)
|
||||||
|
|
||||||
@ -238,14 +238,14 @@ def function(inp: ourlang.Function) -> wasm.Function:
|
|||||||
*_generate_tuple_constructor(inp)
|
*_generate_tuple_constructor(inp)
|
||||||
]
|
]
|
||||||
locals_ = [
|
locals_ = [
|
||||||
('___new_reference___addr', wasm.OurTypeInt32(), ),
|
('___new_reference___addr', wasm.WasmTypeInt32(), ),
|
||||||
]
|
]
|
||||||
elif isinstance(inp, ourlang.StructConstructor):
|
elif isinstance(inp, ourlang.StructConstructor):
|
||||||
statements = [
|
statements = [
|
||||||
*_generate_struct_constructor(inp)
|
*_generate_struct_constructor(inp)
|
||||||
]
|
]
|
||||||
locals_ = [
|
locals_ = [
|
||||||
('___new_reference___addr', wasm.OurTypeInt32(), ),
|
('___new_reference___addr', wasm.WasmTypeInt32(), ),
|
||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
statements = [
|
statements = [
|
||||||
@ -257,7 +257,7 @@ def function(inp: ourlang.Function) -> wasm.Function:
|
|||||||
|
|
||||||
return wasm.Function(
|
return wasm.Function(
|
||||||
inp.name,
|
inp.name,
|
||||||
inp.exported,
|
inp.name if inp.exported else None,
|
||||||
[
|
[
|
||||||
function_argument(x)
|
function_argument(x)
|
||||||
for x in inp.posonlyargs
|
for x in inp.posonlyargs
|
||||||
@ -290,7 +290,7 @@ def module(inp: ourlang.Module) -> wasm.Module:
|
|||||||
def _generate____new_reference___(mod: ourlang.Module) -> wasm.Function:
|
def _generate____new_reference___(mod: ourlang.Module) -> wasm.Function:
|
||||||
return wasm.Function(
|
return wasm.Function(
|
||||||
'___new_reference___',
|
'___new_reference___',
|
||||||
True,
|
'___new_reference___',
|
||||||
[
|
[
|
||||||
('alloc_size', type_(mod.types['i32']), ),
|
('alloc_size', type_(mod.types['i32']), ),
|
||||||
],
|
],
|
||||||
@ -313,7 +313,7 @@ def _generate____new_reference___(mod: ourlang.Module) -> wasm.Function:
|
|||||||
def _generate____access_bytes_index___(mod: ourlang.Module) -> wasm.Function:
|
def _generate____access_bytes_index___(mod: ourlang.Module) -> wasm.Function:
|
||||||
return wasm.Function(
|
return wasm.Function(
|
||||||
'___access_bytes_index___',
|
'___access_bytes_index___',
|
||||||
False,
|
None,
|
||||||
[
|
[
|
||||||
('byt', type_(mod.types['i32']), ),
|
('byt', type_(mod.types['i32']), ),
|
||||||
('ofs', type_(mod.types['i32']), ),
|
('ofs', type_(mod.types['i32']), ),
|
||||||
|
|||||||
@ -458,13 +458,12 @@ class Function:
|
|||||||
"""
|
"""
|
||||||
A function processes input and produces output
|
A function processes input and produces output
|
||||||
"""
|
"""
|
||||||
__slots__ = ('name', 'lineno', 'exported', 'imported', 'buildin', 'statements', 'returns', 'posonlyargs', )
|
__slots__ = ('name', 'lineno', 'exported', 'imported', 'statements', 'returns', 'posonlyargs', )
|
||||||
|
|
||||||
name: str
|
name: str
|
||||||
lineno: int
|
lineno: int
|
||||||
exported: bool
|
exported: bool
|
||||||
imported: bool
|
imported: bool
|
||||||
buildin: bool
|
|
||||||
statements: List[Statement]
|
statements: List[Statement]
|
||||||
returns: OurType
|
returns: OurType
|
||||||
posonlyargs: List[Tuple[str, OurType]]
|
posonlyargs: List[Tuple[str, OurType]]
|
||||||
@ -474,7 +473,6 @@ class Function:
|
|||||||
self.lineno = lineno
|
self.lineno = lineno
|
||||||
self.exported = False
|
self.exported = False
|
||||||
self.imported = False
|
self.imported = False
|
||||||
self.buildin = False
|
|
||||||
self.statements = []
|
self.statements = []
|
||||||
self.returns = OurTypeNone()
|
self.returns = OurTypeNone()
|
||||||
self.posonlyargs = []
|
self.posonlyargs = []
|
||||||
|
|||||||
249
py2wasm/wasm.py
249
py2wasm/wasm.py
@ -3,150 +3,77 @@ Python classes for storing the representation of Web Assembly code,
|
|||||||
and being able to conver it to Web Assembly Text Format
|
and being able to conver it to Web Assembly Text Format
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Any, Iterable, List, Optional, Tuple, Union
|
from typing import Iterable, List, Optional, Tuple
|
||||||
|
|
||||||
###
|
class WatSerializable:
|
||||||
### This part is more intermediate code
|
"""
|
||||||
###
|
Mixin for clases that can be serialized as WebAssembly Text
|
||||||
|
"""
|
||||||
|
def to_wat(self) -> str:
|
||||||
|
"""
|
||||||
|
Renders this object as WebAssembly Text
|
||||||
|
"""
|
||||||
|
raise NotImplementedError(self, 'to_wat')
|
||||||
|
|
||||||
class OurType:
|
class WasmType(WatSerializable):
|
||||||
def to_python(self) -> str:
|
"""
|
||||||
raise NotImplementedError(self, 'to_python')
|
Type base class
|
||||||
|
"""
|
||||||
|
|
||||||
def to_wasm(self) -> str:
|
class WasmTypeNone(WasmType):
|
||||||
raise NotImplementedError(self, 'to_wasm')
|
"""
|
||||||
|
Type when there is no type
|
||||||
|
"""
|
||||||
|
def to_wat(self) -> str:
|
||||||
|
raise Exception('None type is only a placeholder')
|
||||||
|
|
||||||
def alloc_size(self) -> int:
|
class WasmTypeInt32(WasmType):
|
||||||
raise NotImplementedError(self, 'alloc_size')
|
"""
|
||||||
|
i32 value
|
||||||
|
|
||||||
class OurTypeNone(OurType):
|
Signed or not depends on the operations, not the type
|
||||||
pass
|
"""
|
||||||
|
def to_wat(self) -> str:
|
||||||
class OurTypeBool(OurType):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class OurTypeInt32(OurType):
|
|
||||||
def to_python(self) -> str:
|
|
||||||
return 'i32'
|
return 'i32'
|
||||||
|
|
||||||
def to_wasm(self) -> str:
|
class WasmTypeInt64(WasmType):
|
||||||
return 'i32'
|
"""
|
||||||
|
i64 value
|
||||||
|
|
||||||
def alloc_size(self) -> int:
|
Signed or not depends on the operations, not the type
|
||||||
return 4
|
"""
|
||||||
|
def to_wat(self) -> str:
|
||||||
class OurTypeInt64(OurType):
|
|
||||||
def to_wasm(self) -> str:
|
|
||||||
return 'i64'
|
return 'i64'
|
||||||
|
|
||||||
def alloc_size(self) -> int:
|
class WasmTypeFloat32(WasmType):
|
||||||
return 8
|
"""
|
||||||
|
f32 value
|
||||||
class OurTypeFloat32(OurType):
|
"""
|
||||||
def to_wasm(self) -> str:
|
def to_wat(self) -> str:
|
||||||
return 'f32'
|
return 'f32'
|
||||||
|
|
||||||
def alloc_size(self) -> int:
|
class WasmTypeFloat64(WasmType):
|
||||||
return 4
|
"""
|
||||||
|
f64 value
|
||||||
class OurTypeFloat64(OurType):
|
"""
|
||||||
def to_wasm(self) -> str:
|
def to_wat(self) -> str:
|
||||||
return 'f64'
|
return 'f64'
|
||||||
|
|
||||||
def alloc_size(self) -> int:
|
class WasmTypeVector(WasmType):
|
||||||
return 8
|
|
||||||
|
|
||||||
class OurTypeVector(OurType):
|
|
||||||
"""
|
"""
|
||||||
A vector is a 128-bit value
|
A vector is a 128-bit value
|
||||||
"""
|
"""
|
||||||
def to_wasm(self) -> str:
|
def to_wat(self) -> str:
|
||||||
return 'v128'
|
return 'v128'
|
||||||
|
|
||||||
def alloc_size(self) -> int:
|
class WasmTypeVectorInt32x4(WasmTypeVector):
|
||||||
return 16
|
|
||||||
|
|
||||||
class OurTypeVectorInt32x4(OurTypeVector):
|
|
||||||
"""
|
"""
|
||||||
4 Int32 values in a single vector
|
4 Int32 values in a single vector
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Constant:
|
Param = Tuple[str, WasmType]
|
||||||
"""
|
|
||||||
TODO
|
|
||||||
"""
|
|
||||||
def __init__(self, value: Union[None, bool, int, float]) -> None:
|
|
||||||
self.value = value
|
|
||||||
|
|
||||||
class ClassMember:
|
class Import(WatSerializable):
|
||||||
"""
|
|
||||||
Represents a class member
|
|
||||||
"""
|
|
||||||
def __init__(self, name: str, type_: OurType, offset: int, default: Optional[Constant]) -> None:
|
|
||||||
self.name = name
|
|
||||||
self.type = type_
|
|
||||||
self.offset = offset
|
|
||||||
self.default = default
|
|
||||||
|
|
||||||
class OurTypeClass(OurType):
|
|
||||||
"""
|
|
||||||
Represents a class
|
|
||||||
"""
|
|
||||||
def __init__(self, name: str, members: List[ClassMember]) -> None:
|
|
||||||
self.name = name
|
|
||||||
self.members = members
|
|
||||||
|
|
||||||
def to_wasm(self) -> str:
|
|
||||||
return 'i32' # WASM uses 32 bit pointers
|
|
||||||
|
|
||||||
def alloc_size(self) -> int:
|
|
||||||
return sum(x.type.alloc_size() for x in self.members)
|
|
||||||
|
|
||||||
class TupleMember:
|
|
||||||
"""
|
|
||||||
Represents a tuple member
|
|
||||||
"""
|
|
||||||
def __init__(self, type_: OurType, offset: int) -> None:
|
|
||||||
self.type = type_
|
|
||||||
self.offset = offset
|
|
||||||
|
|
||||||
class OurTypeTuple(OurType):
|
|
||||||
"""
|
|
||||||
Represents a tuple
|
|
||||||
"""
|
|
||||||
def __init__(self, members: List[TupleMember]) -> None:
|
|
||||||
self.members = members
|
|
||||||
|
|
||||||
def to_python(self) -> str:
|
|
||||||
return 'Tuple[' + (
|
|
||||||
', '.join(x.type.to_python() for x in self.members)
|
|
||||||
) + ']'
|
|
||||||
|
|
||||||
def to_wasm(self) -> str:
|
|
||||||
return 'i32' # WASM uses 32 bit pointers
|
|
||||||
|
|
||||||
def alloc_size(self) -> int:
|
|
||||||
return sum(x.type.alloc_size() for x in self.members)
|
|
||||||
|
|
||||||
def __eq__(self, other: Any) -> bool:
|
|
||||||
if not isinstance(other, OurTypeTuple):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
return (
|
|
||||||
len(self.members) == len(other.members)
|
|
||||||
and all(
|
|
||||||
self.members[x].type == other.members[x].type
|
|
||||||
for x in range(len(self.members))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
Param = Tuple[str, OurType]
|
|
||||||
|
|
||||||
###
|
|
||||||
## This part is more actual web assembly
|
|
||||||
###
|
|
||||||
|
|
||||||
class Import:
|
|
||||||
"""
|
"""
|
||||||
Represents a Web Assembly import
|
Represents a Web Assembly import
|
||||||
"""
|
"""
|
||||||
@ -156,7 +83,7 @@ class Import:
|
|||||||
name: str,
|
name: str,
|
||||||
intname: str,
|
intname: str,
|
||||||
params: Iterable[Param],
|
params: Iterable[Param],
|
||||||
result: OurType,
|
result: WasmType,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.module = module
|
self.module = module
|
||||||
self.name = name
|
self.name = name
|
||||||
@ -164,23 +91,20 @@ class Import:
|
|||||||
self.params = [*params]
|
self.params = [*params]
|
||||||
self.result = result
|
self.result = result
|
||||||
|
|
||||||
def generate(self) -> str:
|
def to_wat(self) -> str:
|
||||||
"""
|
|
||||||
Generates the text version
|
|
||||||
"""
|
|
||||||
return '(import "{}" "{}" (func ${}{}{}))'.format(
|
return '(import "{}" "{}" (func ${}{}{}))'.format(
|
||||||
self.module,
|
self.module,
|
||||||
self.name,
|
self.name,
|
||||||
self.intname,
|
self.intname,
|
||||||
''.join(
|
''.join(
|
||||||
f' (param {typ.to_wasm()})'
|
f' (param {typ.to_wat()})'
|
||||||
for _, typ in self.params
|
for _, typ in self.params
|
||||||
),
|
),
|
||||||
'' if isinstance(self.result, OurTypeNone)
|
'' if isinstance(self.result, WasmTypeNone)
|
||||||
else f' (result {self.result.to_wasm()})'
|
else f' (result {self.result.to_wat()})'
|
||||||
)
|
)
|
||||||
|
|
||||||
class Statement:
|
class Statement(WatSerializable):
|
||||||
"""
|
"""
|
||||||
Represents a Web Assembly statement
|
Represents a Web Assembly statement
|
||||||
"""
|
"""
|
||||||
@ -189,73 +113,76 @@ class Statement:
|
|||||||
self.args = args
|
self.args = args
|
||||||
self.comment = comment
|
self.comment = comment
|
||||||
|
|
||||||
def generate(self) -> str:
|
def to_wat(self) -> str:
|
||||||
"""
|
|
||||||
Generates the text version
|
|
||||||
"""
|
|
||||||
args = ' '.join(self.args)
|
args = ' '.join(self.args)
|
||||||
comment = f' ;; {self.comment}' if self.comment else ''
|
comment = f' ;; {self.comment}' if self.comment else ''
|
||||||
|
|
||||||
return f'{self.name} {args}{comment}'
|
return f'{self.name} {args}{comment}'
|
||||||
|
|
||||||
class Function:
|
class Function(WatSerializable):
|
||||||
"""
|
"""
|
||||||
Represents a Web Assembly function
|
Represents a Web Assembly function
|
||||||
"""
|
"""
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
name: str,
|
name: str,
|
||||||
exported: bool,
|
exported_name: Optional[str],
|
||||||
params: Iterable[Param],
|
params: Iterable[Param],
|
||||||
locals_: Iterable[Param],
|
locals_: Iterable[Param],
|
||||||
result: OurType,
|
result: WasmType,
|
||||||
statements: Iterable[Statement],
|
statements: Iterable[Statement],
|
||||||
) -> None:
|
) -> None:
|
||||||
self.name = name
|
self.name = name
|
||||||
self.exported = exported
|
self.exported_name = exported_name
|
||||||
self.params = [*params]
|
self.params = [*params]
|
||||||
self.locals = [*locals_]
|
self.locals = [*locals_]
|
||||||
self.result = result
|
self.result = result
|
||||||
self.statements = [*statements]
|
self.statements = [*statements]
|
||||||
|
|
||||||
def generate(self) -> str:
|
def to_wat(self) -> str:
|
||||||
"""
|
|
||||||
Generates the text version
|
|
||||||
"""
|
|
||||||
header = f'${self.name}' # Name for internal use
|
header = f'${self.name}' # Name for internal use
|
||||||
|
|
||||||
if self.exported:
|
if self.exported_name is not None:
|
||||||
# Name for external use
|
# Name for external use
|
||||||
# TODO: Consider: Make exported('export_name') work
|
header += f' (export "{self.exported_name}")'
|
||||||
header += f' (export "{self.name}")'
|
|
||||||
|
|
||||||
for nam, typ in self.params:
|
for nam, typ in self.params:
|
||||||
header += f' (param ${nam} {typ.to_wasm()})'
|
header += f' (param ${nam} {typ.to_wat()})'
|
||||||
|
|
||||||
if not isinstance(self.result, OurTypeNone):
|
if not isinstance(self.result, WasmTypeNone):
|
||||||
header += f' (result {self.result.to_wasm()})'
|
header += f' (result {self.result.to_wat()})'
|
||||||
|
|
||||||
for nam, typ in self.locals:
|
for nam, typ in self.locals:
|
||||||
header += f' (local ${nam} {typ.to_wasm()})'
|
header += f' (local ${nam} {typ.to_wat()})'
|
||||||
|
|
||||||
return '(func {}\n {}\n )'.format(
|
return '(func {}\n {}\n )'.format(
|
||||||
header,
|
header,
|
||||||
'\n '.join(x.generate() for x in self.statements),
|
'\n '.join(x.to_wat() for x in self.statements),
|
||||||
)
|
)
|
||||||
|
|
||||||
class ModuleMemory:
|
class ModuleMemory(WatSerializable):
|
||||||
|
"""
|
||||||
|
Represents a WebAssembly module's memory
|
||||||
|
"""
|
||||||
def __init__(self, data: bytes = b'') -> None:
|
def __init__(self, data: bytes = b'') -> None:
|
||||||
self.data = data
|
self.data = data
|
||||||
|
|
||||||
def generate(self) -> str:
|
def to_wat(self) -> str:
|
||||||
return '(memory 1)\n (data (memory 0) (i32.const 0) "{}")\n (export "memory" (memory 0))\n'.format(
|
"""
|
||||||
''.join(
|
Renders this memory as WebAssembly Text
|
||||||
f'\\{x:02x}'
|
"""
|
||||||
for x in self.data
|
data = ''.join(
|
||||||
)
|
f'\\{x:02x}'
|
||||||
|
for x in self.data
|
||||||
)
|
)
|
||||||
|
|
||||||
class Module:
|
return (
|
||||||
|
'(memory 1)\n '
|
||||||
|
f'(data (memory 0) (i32.const 0) "{data}")\n '
|
||||||
|
'(export "memory" (memory 0))\n'
|
||||||
|
)
|
||||||
|
|
||||||
|
class Module(WatSerializable):
|
||||||
"""
|
"""
|
||||||
Represents a Web Assembly module
|
Represents a Web Assembly module
|
||||||
"""
|
"""
|
||||||
@ -264,12 +191,12 @@ class Module:
|
|||||||
self.functions: List[Function] = []
|
self.functions: List[Function] = []
|
||||||
self.memory = ModuleMemory(b'\x04') # For ___new_reference___
|
self.memory = ModuleMemory(b'\x04') # For ___new_reference___
|
||||||
|
|
||||||
def generate(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'.format(
|
||||||
'\n '.join(x.generate() for x in self.imports),
|
'\n '.join(x.to_wat() for x in self.imports),
|
||||||
self.memory.generate(),
|
self.memory.to_wat(),
|
||||||
'\n '.join(x.generate() for x in self.functions),
|
'\n '.join(x.to_wat() for x in self.functions),
|
||||||
)
|
)
|
||||||
|
|||||||
@ -59,6 +59,9 @@ class SuiteResult:
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Suite:
|
class Suite:
|
||||||
|
"""
|
||||||
|
WebAssembly test suite
|
||||||
|
"""
|
||||||
def __init__(self, code_py, test_name):
|
def __init__(self, code_py, test_name):
|
||||||
self.code_py = code_py
|
self.code_py = code_py
|
||||||
self.test_name = test_name
|
self.test_name = test_name
|
||||||
@ -76,10 +79,10 @@ class Suite:
|
|||||||
assert self.code_py == '\n' + our_module.render() # \n for formatting in tests
|
assert self.code_py == '\n' + our_module.render() # \n for formatting in tests
|
||||||
|
|
||||||
# Compile
|
# Compile
|
||||||
wat_module = module(our_module)
|
wasm_module = module(our_module)
|
||||||
|
|
||||||
# Render as text
|
# Render as text
|
||||||
code_wat = wat_module.generate()
|
code_wat = wasm_module.to_wat()
|
||||||
|
|
||||||
sys.stderr.write(f'{DASHES} Assembly {DASHES}\n')
|
sys.stderr.write(f'{DASHES} Assembly {DASHES}\n')
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user