MVP #1

Merged
jbwdevries merged 73 commits from idea_crc32 into master 2022-08-21 12:59:21 +00:00
5 changed files with 109 additions and 181 deletions
Showing only changes of commit 14eede6b06 - Show all commits

View File

@ -16,8 +16,8 @@ def main(source: str, sink: str) -> int:
code_py = fil.read()
our_module = our_process(code_py, source)
wat_module = module(our_module)
code_wat = wat_module.generate()
wasm_module = module(our_module)
code_wat = wasm_module.to_wat()
with open(sink, 'w') as fil:
fil.write(code_wat)

View File

@ -8,31 +8,31 @@ from . import wasm
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):
return wasm.OurTypeNone()
return wasm.WasmTypeNone()
if isinstance(inp, ourlang.OurTypeUInt8):
# WebAssembly has only support for 32 and 64 bits
# So we need to store more memory per byte
return wasm.OurTypeInt32()
return wasm.WasmTypeInt32()
if isinstance(inp, ourlang.OurTypeInt32):
return wasm.OurTypeInt32()
return wasm.WasmTypeInt32()
if isinstance(inp, ourlang.OurTypeInt64):
return wasm.OurTypeInt64()
return wasm.WasmTypeInt64()
if isinstance(inp, ourlang.OurTypeFloat32):
return wasm.OurTypeFloat32()
return wasm.WasmTypeFloat32()
if isinstance(inp, ourlang.OurTypeFloat64):
return wasm.OurTypeFloat64()
return wasm.WasmTypeFloat64()
if isinstance(inp, (ourlang.Struct, ourlang.OurTypeTuple, ourlang.OurTypeBytes)):
# Structs and tuples are passed as pointer
# And pointers are i32
return wasm.OurTypeInt32()
return wasm.WasmTypeInt32()
raise NotImplementedError(type_, inp)
@ -238,14 +238,14 @@ def function(inp: ourlang.Function) -> wasm.Function:
*_generate_tuple_constructor(inp)
]
locals_ = [
('___new_reference___addr', wasm.OurTypeInt32(), ),
('___new_reference___addr', wasm.WasmTypeInt32(), ),
]
elif isinstance(inp, ourlang.StructConstructor):
statements = [
*_generate_struct_constructor(inp)
]
locals_ = [
('___new_reference___addr', wasm.OurTypeInt32(), ),
('___new_reference___addr', wasm.WasmTypeInt32(), ),
]
else:
statements = [
@ -257,7 +257,7 @@ def function(inp: ourlang.Function) -> wasm.Function:
return wasm.Function(
inp.name,
inp.exported,
inp.name if inp.exported else None,
[
function_argument(x)
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:
return wasm.Function(
'___new_reference___',
True,
'___new_reference___',
[
('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:
return wasm.Function(
'___access_bytes_index___',
False,
None,
[
('byt', type_(mod.types['i32']), ),
('ofs', type_(mod.types['i32']), ),

View File

@ -458,13 +458,12 @@ class Function:
"""
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
lineno: int
exported: bool
imported: bool
buildin: bool
statements: List[Statement]
returns: OurType
posonlyargs: List[Tuple[str, OurType]]
@ -474,7 +473,6 @@ class Function:
self.lineno = lineno
self.exported = False
self.imported = False
self.buildin = False
self.statements = []
self.returns = OurTypeNone()
self.posonlyargs = []

View File

@ -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
"""
from typing import Any, Iterable, List, Optional, Tuple, Union
from typing import Iterable, List, Optional, Tuple
###
### This part is more intermediate code
###
class WatSerializable:
"""
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:
def to_python(self) -> str:
raise NotImplementedError(self, 'to_python')
class WasmType(WatSerializable):
"""
Type base class
"""
def to_wasm(self) -> str:
raise NotImplementedError(self, 'to_wasm')
class WasmTypeNone(WasmType):
"""
Type when there is no type
"""
def to_wat(self) -> str:
raise Exception('None type is only a placeholder')
def alloc_size(self) -> int:
raise NotImplementedError(self, 'alloc_size')
class WasmTypeInt32(WasmType):
"""
i32 value
class OurTypeNone(OurType):
pass
class OurTypeBool(OurType):
pass
class OurTypeInt32(OurType):
def to_python(self) -> str:
Signed or not depends on the operations, not the type
"""
def to_wat(self) -> str:
return 'i32'
def to_wasm(self) -> str:
return 'i32'
class WasmTypeInt64(WasmType):
"""
i64 value
def alloc_size(self) -> int:
return 4
class OurTypeInt64(OurType):
def to_wasm(self) -> str:
Signed or not depends on the operations, not the type
"""
def to_wat(self) -> str:
return 'i64'
def alloc_size(self) -> int:
return 8
class OurTypeFloat32(OurType):
def to_wasm(self) -> str:
class WasmTypeFloat32(WasmType):
"""
f32 value
"""
def to_wat(self) -> str:
return 'f32'
def alloc_size(self) -> int:
return 4
class OurTypeFloat64(OurType):
def to_wasm(self) -> str:
class WasmTypeFloat64(WasmType):
"""
f64 value
"""
def to_wat(self) -> str:
return 'f64'
def alloc_size(self) -> int:
return 8
class OurTypeVector(OurType):
class WasmTypeVector(WasmType):
"""
A vector is a 128-bit value
"""
def to_wasm(self) -> str:
def to_wat(self) -> str:
return 'v128'
def alloc_size(self) -> int:
return 16
class OurTypeVectorInt32x4(OurTypeVector):
class WasmTypeVectorInt32x4(WasmTypeVector):
"""
4 Int32 values in a single vector
"""
class Constant:
"""
TODO
"""
def __init__(self, value: Union[None, bool, int, float]) -> None:
self.value = value
Param = Tuple[str, WasmType]
class ClassMember:
"""
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:
class Import(WatSerializable):
"""
Represents a Web Assembly import
"""
@ -156,7 +83,7 @@ class Import:
name: str,
intname: str,
params: Iterable[Param],
result: OurType,
result: WasmType,
) -> None:
self.module = module
self.name = name
@ -164,23 +91,20 @@ class Import:
self.params = [*params]
self.result = result
def generate(self) -> str:
"""
Generates the text version
"""
def to_wat(self) -> str:
return '(import "{}" "{}" (func ${}{}{}))'.format(
self.module,
self.name,
self.intname,
''.join(
f' (param {typ.to_wasm()})'
f' (param {typ.to_wat()})'
for _, typ in self.params
),
'' if isinstance(self.result, OurTypeNone)
else f' (result {self.result.to_wasm()})'
'' if isinstance(self.result, WasmTypeNone)
else f' (result {self.result.to_wat()})'
)
class Statement:
class Statement(WatSerializable):
"""
Represents a Web Assembly statement
"""
@ -189,73 +113,76 @@ class Statement:
self.args = args
self.comment = comment
def generate(self) -> str:
"""
Generates the text version
"""
def to_wat(self) -> str:
args = ' '.join(self.args)
comment = f' ;; {self.comment}' if self.comment else ''
return f'{self.name} {args}{comment}'
class Function:
class Function(WatSerializable):
"""
Represents a Web Assembly function
"""
def __init__(
self,
name: str,
exported: bool,
exported_name: Optional[str],
params: Iterable[Param],
locals_: Iterable[Param],
result: OurType,
result: WasmType,
statements: Iterable[Statement],
) -> None:
self.name = name
self.exported = exported
self.exported_name = exported_name
self.params = [*params]
self.locals = [*locals_]
self.result = result
self.statements = [*statements]
def generate(self) -> str:
"""
Generates the text version
"""
def to_wat(self) -> str:
header = f'${self.name}' # Name for internal use
if self.exported:
if self.exported_name is not None:
# Name for external use
# TODO: Consider: Make exported('export_name') work
header += f' (export "{self.name}")'
header += f' (export "{self.exported_name}")'
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):
header += f' (result {self.result.to_wasm()})'
if not isinstance(self.result, WasmTypeNone):
header += f' (result {self.result.to_wat()})'
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(
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:
self.data = data
def generate(self) -> str:
return '(memory 1)\n (data (memory 0) (i32.const 0) "{}")\n (export "memory" (memory 0))\n'.format(
''.join(
def to_wat(self) -> str:
"""
Renders this memory as WebAssembly Text
"""
data = ''.join(
f'\\{x:02x}'
for x in self.data
)
return (
'(memory 1)\n '
f'(data (memory 0) (i32.const 0) "{data}")\n '
'(export "memory" (memory 0))\n'
)
class Module:
class Module(WatSerializable):
"""
Represents a Web Assembly module
"""
@ -264,12 +191,12 @@ class Module:
self.functions: List[Function] = []
self.memory = ModuleMemory(b'\x04') # For ___new_reference___
def generate(self) -> str:
def to_wat(self) -> str:
"""
Generates the text version
"""
return '(module\n {}\n {}\n {})\n'.format(
'\n '.join(x.generate() for x in self.imports),
self.memory.generate(),
'\n '.join(x.generate() for x in self.functions),
'\n '.join(x.to_wat() for x in self.imports),
self.memory.to_wat(),
'\n '.join(x.to_wat() for x in self.functions),
)

View File

@ -59,6 +59,9 @@ class SuiteResult:
}
class Suite:
"""
WebAssembly test suite
"""
def __init__(self, code_py, test_name):
self.code_py = code_py
self.test_name = test_name
@ -76,10 +79,10 @@ class Suite:
assert self.code_py == '\n' + our_module.render() # \n for formatting in tests
# Compile
wat_module = module(our_module)
wasm_module = module(our_module)
# Render as text
code_wat = wat_module.generate()
code_wat = wasm_module.to_wat()
sys.stderr.write(f'{DASHES} Assembly {DASHES}\n')