phasm/phasm/wasm.py
2022-08-09 20:42:02 +02:00

200 lines
4.9 KiB
Python

"""
Python classes for storing the representation of Web Assembly code,
and being able to conver it to Web Assembly Text Format
"""
from typing import Iterable, List, Optional, Tuple
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 WasmType(WatSerializable):
"""
Type base class
"""
class WasmTypeNone(WasmType):
"""
Type when there is no type
"""
def to_wat(self) -> str:
raise Exception('None type is only a placeholder')
class WasmTypeInt32(WasmType):
"""
i32 value
Signed or not depends on the operations, not the type
"""
def to_wat(self) -> str:
return 'i32'
class WasmTypeInt64(WasmType):
"""
i64 value
Signed or not depends on the operations, not the type
"""
def to_wat(self) -> str:
return 'i64'
class WasmTypeFloat32(WasmType):
"""
f32 value
"""
def to_wat(self) -> str:
return 'f32'
class WasmTypeFloat64(WasmType):
"""
f64 value
"""
def to_wat(self) -> str:
return 'f64'
class WasmTypeVector(WasmType):
"""
A vector is a 128-bit value
"""
def to_wat(self) -> str:
return 'v128'
class WasmTypeVectorInt32x4(WasmTypeVector):
"""
4 Int32 values in a single vector
"""
Param = Tuple[str, WasmType]
class Import(WatSerializable):
"""
Represents a Web Assembly import
"""
def __init__(
self,
module: str,
name: str,
intname: str,
params: Iterable[Param],
result: WasmType,
) -> None:
self.module = module
self.name = name
self.intname = intname
self.params = [*params]
self.result = result
def to_wat(self) -> str:
return '(import "{}" "{}" (func ${}{}{}))'.format(
self.module,
self.name,
self.intname,
''.join(
f' (param {typ.to_wat()})'
for _, typ in self.params
),
'' if isinstance(self.result, WasmTypeNone)
else f' (result {self.result.to_wat()})'
)
class Statement(WatSerializable):
"""
Represents a Web Assembly statement
"""
def __init__(self, name: str, *args: str, comment: Optional[str] = None):
self.name = name
self.args = args
self.comment = comment
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(WatSerializable):
"""
Represents a Web Assembly function
"""
def __init__(
self,
name: str,
exported_name: Optional[str],
params: Iterable[Param],
locals_: Iterable[Param],
result: WasmType,
statements: Iterable[Statement],
) -> None:
self.name = name
self.exported_name = exported_name
self.params = [*params]
self.locals = [*locals_]
self.result = result
self.statements = [*statements]
def to_wat(self) -> str:
header = f'${self.name}' # Name for internal use
if self.exported_name is not None:
# Name for external use
header += f' (export "{self.exported_name}")'
for nam, typ in self.params:
header += f' (param ${nam} {typ.to_wat()})'
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_wat()})'
return '(func {}\n {}\n )'.format(
header,
'\n '.join(x.to_wat() for x in self.statements),
)
class ModuleMemory(WatSerializable):
"""
Represents a WebAssembly module's memory
"""
def __init__(self, data: bytes = b'') -> None:
self.data = data
def to_wat(self) -> str:
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(WatSerializable):
"""
Represents a Web Assembly module
"""
def __init__(self) -> None:
self.imports: List[Import] = []
self.functions: List[Function] = []
self.memory = ModuleMemory()
def to_wat(self) -> str:
"""
Generates the text version
"""
return '(module\n {}\n {}\n {})\n'.format(
'\n '.join(x.to_wat() for x in self.imports),
self.memory.to_wat(),
'\n '.join(x.to_wat() for x in self.functions),
)