MVP #1
@ -29,6 +29,18 @@ class Visitor:
|
|||||||
|
|
||||||
module = wasm.Module()
|
module = wasm.Module()
|
||||||
|
|
||||||
|
module.functions.append(wasm.Function(
|
||||||
|
'___new_reference___',
|
||||||
|
False,
|
||||||
|
[
|
||||||
|
('alloc_size', 'i32'),
|
||||||
|
],
|
||||||
|
'i32',
|
||||||
|
[
|
||||||
|
wasm.Statement('i32.const', '4', comment='Stub')
|
||||||
|
]
|
||||||
|
))
|
||||||
|
|
||||||
# Do a check first for all function definitions
|
# Do a check first for all function definitions
|
||||||
# to get their types. Otherwise you cannot call
|
# to get their types. Otherwise you cannot call
|
||||||
# a method that you haven't defined just yet,
|
# a method that you haven't defined just yet,
|
||||||
@ -165,6 +177,7 @@ class Visitor:
|
|||||||
|
|
||||||
members: List[wasm.ClassMember] = []
|
members: List[wasm.ClassMember] = []
|
||||||
|
|
||||||
|
offset = 0
|
||||||
for stmt in node.body:
|
for stmt in node.body:
|
||||||
if not isinstance(stmt, ast.AnnAssign):
|
if not isinstance(stmt, ast.AnnAssign):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
@ -189,9 +202,12 @@ class Visitor:
|
|||||||
|
|
||||||
default = wasm.Constant(stmt.value.value)
|
default = wasm.Constant(stmt.value.value)
|
||||||
|
|
||||||
members.append(wasm.ClassMember(
|
member = wasm.ClassMember(
|
||||||
stmt.target.id, stmt.annotation.id, default
|
stmt.target.id, stmt.annotation.id, offset, default
|
||||||
))
|
)
|
||||||
|
|
||||||
|
members.append(member)
|
||||||
|
offset += member.alloc_size()
|
||||||
|
|
||||||
return wasm.Class(node.name, members)
|
return wasm.Class(node.name, members)
|
||||||
|
|
||||||
@ -294,7 +310,7 @@ class Visitor:
|
|||||||
return self.visit_Name(wlocals, exp_type, node)
|
return self.visit_Name(wlocals, exp_type, node)
|
||||||
|
|
||||||
if isinstance(node, ast.Attribute):
|
if isinstance(node, ast.Attribute):
|
||||||
return self.visit_Attribute(wlocals, exp_type, node)
|
return self.visit_Attribute(module, wlocals, exp_type, node)
|
||||||
|
|
||||||
raise NotImplementedError(node)
|
raise NotImplementedError(node)
|
||||||
|
|
||||||
@ -419,7 +435,7 @@ class Visitor:
|
|||||||
wlocals: WLocals,
|
wlocals: WLocals,
|
||||||
exp_type: str,
|
exp_type: str,
|
||||||
node: ast.Call,
|
node: ast.Call,
|
||||||
func: wasm.Class,
|
cls: wasm.Class,
|
||||||
) -> StatementGenerator:
|
) -> StatementGenerator:
|
||||||
"""
|
"""
|
||||||
Visits a Call node as (part of) an expression
|
Visits a Call node as (part of) an expression
|
||||||
@ -429,9 +445,20 @@ class Visitor:
|
|||||||
|
|
||||||
# TODO: malloc call
|
# TODO: malloc call
|
||||||
|
|
||||||
yield wasm.Statement('i32.const 0')
|
yield wasm.Statement('i32.const', str(cls.alloc_size()))
|
||||||
yield wasm.Statement('i32.load')
|
yield wasm.Statement('call', '$___new_reference___')
|
||||||
|
|
||||||
|
yield wasm.Statement('local.set', '$___new_reference___addr')
|
||||||
|
|
||||||
|
for member, arg in zip(cls.members, node.args):
|
||||||
|
if not isinstance(arg, ast.Constant):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
yield wasm.Statement('local.get', '$___new_reference___addr')
|
||||||
|
yield wasm.Statement(f'{member.type}.const', str(arg.value))
|
||||||
|
yield wasm.Statement(f'{member.type}.store', 'offset=' + str(member.offset))
|
||||||
|
|
||||||
|
yield wasm.Statement('local.get', '$___new_reference___addr')
|
||||||
|
|
||||||
def visit_Call_func(
|
def visit_Call_func(
|
||||||
self,
|
self,
|
||||||
@ -500,7 +527,13 @@ class Visitor:
|
|||||||
|
|
||||||
raise NotImplementedError(exp_type)
|
raise NotImplementedError(exp_type)
|
||||||
|
|
||||||
def visit_Attribute(self, wlocals: WLocals, exp_type: str, node: ast.Attribute) -> StatementGenerator:
|
def visit_Attribute(
|
||||||
|
self,
|
||||||
|
module: wasm.Module,
|
||||||
|
wlocals: WLocals,
|
||||||
|
exp_type: str,
|
||||||
|
node: ast.Attribute,
|
||||||
|
) -> StatementGenerator:
|
||||||
"""
|
"""
|
||||||
Visits an Attribute node as (part of) an expression
|
Visits an Attribute node as (part of) an expression
|
||||||
"""
|
"""
|
||||||
@ -511,8 +544,26 @@ class Visitor:
|
|||||||
if not isinstance(node.ctx, ast.Load):
|
if not isinstance(node.ctx, ast.Load):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
cls_list = [
|
||||||
|
x
|
||||||
|
for x in module.classes
|
||||||
|
if x.name == 'Rectangle' # TODO: STUB, since we can't acces the type properly
|
||||||
|
]
|
||||||
|
|
||||||
|
assert len(cls_list) == 1
|
||||||
|
cls = cls_list[0]
|
||||||
|
|
||||||
|
member_list = [
|
||||||
|
x
|
||||||
|
for x in cls.members
|
||||||
|
if x.name == node.attr
|
||||||
|
]
|
||||||
|
|
||||||
|
assert len(member_list) == 1
|
||||||
|
member = member_list[0]
|
||||||
|
|
||||||
yield wasm.Statement('local.get', '$' + node.value.id)
|
yield wasm.Statement('local.get', '$' + node.value.id)
|
||||||
yield wasm.Statement(exp_type + '.load', 'offset=0') # TODO: Calculate offset based on set struct
|
yield wasm.Statement(exp_type + '.load', 'offset=' + str(member.offset))
|
||||||
|
|
||||||
def visit_Name(self, wlocals: WLocals, exp_type: str, node: ast.Name) -> StatementGenerator:
|
def visit_Name(self, wlocals: WLocals, exp_type: str, node: ast.Name) -> StatementGenerator:
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -38,15 +38,19 @@ class Statement:
|
|||||||
"""
|
"""
|
||||||
Represents a Web Assembly statement
|
Represents a Web Assembly statement
|
||||||
"""
|
"""
|
||||||
def __init__(self, name: str, *args: str):
|
def __init__(self, name: str, *args: str, comment: Optional[str] = None):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.args = args
|
self.args = args
|
||||||
|
self.comment = comment
|
||||||
|
|
||||||
def generate(self) -> str:
|
def generate(self) -> str:
|
||||||
"""
|
"""
|
||||||
Generates the text version
|
Generates the text version
|
||||||
"""
|
"""
|
||||||
return '{} {}'.format(self.name, ' '.join(self.args))
|
args = ' '.join(self.args)
|
||||||
|
comment = f' ;; {self.comment}' if self.comment else ''
|
||||||
|
|
||||||
|
return f'{self.name} {args}{comment}'
|
||||||
|
|
||||||
class Function:
|
class Function:
|
||||||
"""
|
"""
|
||||||
@ -78,7 +82,9 @@ class Function:
|
|||||||
if self.result:
|
if self.result:
|
||||||
header += ' (result {})'.format(self.result)
|
header += ' (result {})'.format(self.result)
|
||||||
|
|
||||||
return '(func {}\n {})'.format(
|
header += ' (local $___new_reference___addr i32)'
|
||||||
|
|
||||||
|
return '(func {}\n {}\n )'.format(
|
||||||
header,
|
header,
|
||||||
'\n '.join(x.generate() for x in self.statements),
|
'\n '.join(x.generate() for x in self.statements),
|
||||||
)
|
)
|
||||||
@ -94,11 +100,20 @@ class ClassMember:
|
|||||||
"""
|
"""
|
||||||
Represents a Web Assembly class member
|
Represents a Web Assembly class member
|
||||||
"""
|
"""
|
||||||
def __init__(self, name: str, type_: str, default: Optional[Constant]) -> None:
|
def __init__(self, name: str, type_: str, offset: int, default: Optional[Constant]) -> None:
|
||||||
self.name = name
|
self.name = name
|
||||||
self.type = type_
|
self.type = type_
|
||||||
|
self.offset = offset
|
||||||
self.default = default
|
self.default = default
|
||||||
|
|
||||||
|
def alloc_size(self) -> int:
|
||||||
|
SIZE_MAP = {
|
||||||
|
'i32': 4,
|
||||||
|
'i64': 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
return SIZE_MAP[self.type]
|
||||||
|
|
||||||
class Class:
|
class Class:
|
||||||
"""
|
"""
|
||||||
Represents a Web Assembly class
|
Represents a Web Assembly class
|
||||||
@ -107,6 +122,9 @@ class Class:
|
|||||||
self.name = name
|
self.name = name
|
||||||
self.members = members
|
self.members = members
|
||||||
|
|
||||||
|
def alloc_size(self) -> int:
|
||||||
|
return sum(x.alloc_size() for x in self.members)
|
||||||
|
|
||||||
class Module:
|
class Module:
|
||||||
"""
|
"""
|
||||||
Represents a Web Assembly module
|
Represents a Web Assembly module
|
||||||
@ -121,7 +139,7 @@ class Module:
|
|||||||
Generates the text version
|
Generates the text version
|
||||||
"""
|
"""
|
||||||
return '(module\n (memory 1)\n (data (memory 0) (i32.const 0) {})\n {}\n {})\n'.format(
|
return '(module\n (memory 1)\n (data (memory 0) (i32.const 0) {})\n {}\n {})\n'.format(
|
||||||
'"\\\\04\\\\00\\\\00\\\\00"',
|
'"\\04\\00\\00\\00"',
|
||||||
'\n '.join(x.generate() for x in self.imports),
|
'\n '.join(x.generate() for x in self.imports),
|
||||||
'\n '.join(x.generate() for x in self.functions),
|
'\n '.join(x.generate() for x in self.functions),
|
||||||
)
|
)
|
||||||
|
|||||||
@ -31,6 +31,7 @@ def wat2wasm(code_wat):
|
|||||||
class SuiteResult:
|
class SuiteResult:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.log_int32_list = []
|
self.log_int32_list = []
|
||||||
|
self.returned_value = None
|
||||||
|
|
||||||
def callback_log_int32(self, store, value):
|
def callback_log_int32(self, store, value):
|
||||||
del store # auto passed by pywasm
|
del store # auto passed by pywasm
|
||||||
@ -58,6 +59,10 @@ class Suite:
|
|||||||
"""
|
"""
|
||||||
code_wat = process(self.code_py, self.test_name)
|
code_wat = process(self.code_py, self.test_name)
|
||||||
|
|
||||||
|
dashes = '-' * 16
|
||||||
|
|
||||||
|
sys.stderr.write(f'{dashes} Assembly {dashes}\n')
|
||||||
|
|
||||||
line_list = code_wat.split('\n')
|
line_list = code_wat.split('\n')
|
||||||
line_no_width = len(str(len(line_list)))
|
line_no_width = len(str(len(line_list)))
|
||||||
for line_no, line_txt in enumerate(line_list):
|
for line_no, line_txt in enumerate(line_list):
|
||||||
@ -72,6 +77,33 @@ class Suite:
|
|||||||
result = SuiteResult()
|
result = SuiteResult()
|
||||||
runtime = Runtime(module, result.make_imports(), {})
|
runtime = Runtime(module, result.make_imports(), {})
|
||||||
|
|
||||||
|
sys.stderr.write(f'{dashes} Memory (pre run) {dashes}\n')
|
||||||
|
_dump_memory(runtime.store.mems[0].data)
|
||||||
|
|
||||||
result.returned_value = runtime.exec('testEntry', args)
|
result.returned_value = runtime.exec('testEntry', args)
|
||||||
|
|
||||||
|
sys.stderr.write(f'{dashes} Memory (post run) {dashes}\n')
|
||||||
|
_dump_memory(runtime.store.mems[0].data)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def _dump_memory(mem):
|
||||||
|
line_width = 16
|
||||||
|
|
||||||
|
prev_line = None
|
||||||
|
skip = False
|
||||||
|
for idx in range(0, len(mem), line_width):
|
||||||
|
line = ''
|
||||||
|
for idx2 in range(0, line_width):
|
||||||
|
line += f'{mem[idx + idx2]:02X}'
|
||||||
|
if idx2 % 2 == 1:
|
||||||
|
line += ' '
|
||||||
|
|
||||||
|
if prev_line == line:
|
||||||
|
if not skip:
|
||||||
|
sys.stderr.write('**\n')
|
||||||
|
skip = True
|
||||||
|
else:
|
||||||
|
sys.stderr.write(f'{idx:08x} {line}\n')
|
||||||
|
|
||||||
|
prev_line = line
|
||||||
|
|||||||
@ -28,4 +28,3 @@ def testEntry() -> i32:
|
|||||||
result = Suite(code_py, 'test_fib').run_code()
|
result = Suite(code_py, 'test_fib').run_code()
|
||||||
|
|
||||||
assert 102334155 == result.returned_value
|
assert 102334155 == result.returned_value
|
||||||
assert False
|
|
||||||
|
|||||||
@ -236,6 +236,7 @@ def helper(left: i32, right: i32) -> i32:
|
|||||||
assert [] == result.log_int32_list
|
assert [] == result.log_int32_list
|
||||||
|
|
||||||
@pytest.mark.integration_test
|
@pytest.mark.integration_test
|
||||||
|
@pytest.mark.skip('Not yet implemented')
|
||||||
def test_assign():
|
def test_assign():
|
||||||
code_py = """
|
code_py = """
|
||||||
|
|
||||||
@ -269,5 +270,5 @@ def helper(shape: Rectangle) -> i32:
|
|||||||
|
|
||||||
result = Suite(code_py, 'test_call').run_code()
|
result = Suite(code_py, 'test_call').run_code()
|
||||||
|
|
||||||
assert 100 == result.returned_value
|
assert 252 == result.returned_value
|
||||||
assert [] == result.log_int32_list
|
assert [] == result.log_int32_list
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user