MVP #1

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

View File

@ -45,6 +45,11 @@ class Visitor:
function_body_map[stmt] = wnode function_body_map[stmt] = wnode
continue continue
if isinstance(stmt, ast.ClassDef):
wclass = self.pre_visit_ClassDef(module, stmt)
module.classes.append(wclass)
continue
# No other pre visits to do # No other pre visits to do
for stmt in node.body: for stmt in node.body:
@ -54,6 +59,9 @@ class Visitor:
# else: It's an import, no actual body to parse # else: It's an import, no actual body to parse
continue continue
if isinstance(stmt, ast.ClassDef):
continue
raise NotImplementedError(stmt) raise NotImplementedError(stmt)
return module return module
@ -70,8 +78,6 @@ class Visitor:
Nested / dynamicly created functions are not yet supported Nested / dynamicly created functions are not yet supported
""" """
del module
exported = False exported = False
if node.decorator_list: if node.decorator_list:
@ -108,13 +114,22 @@ class Visitor:
assert not node.args.kwarg assert not node.args.kwarg
assert not node.args.defaults assert not node.args.defaults
params = [ class_lookup = {
(a.arg, _parse_annotation(a.annotation), ) x.name: x
for a in [ for x in module.classes
*node.args.posonlyargs, }
*node.args.args,
] params = []
] for arg in [*node.args.posonlyargs, *node.args.args]:
if not isinstance(arg.annotation, ast.Name):
raise NotImplementedError
print(class_lookup)
print(arg.annotation.id)
if arg.annotation.id in class_lookup:
params.append((arg.arg, arg.annotation.id, ))
else:
params.append((arg.arg, _parse_annotation(arg.annotation), ))
return wasm.Function(node.name, exported, params, result, []) return wasm.Function(node.name, exported, params, result, [])
@ -137,6 +152,51 @@ class Visitor:
func.statements = statements func.statements = statements
def pre_visit_ClassDef(
self,
module: wasm.Module,
node: ast.ClassDef,
) -> wasm.Class:
"""
TODO: Document this
"""
del module
if node.bases or node.keywords or node.decorator_list:
raise NotImplementedError
members: List[wasm.ClassMember] = []
for stmt in node.body:
if not isinstance(stmt, ast.AnnAssign):
raise NotImplementedError
if not isinstance(stmt.target, ast.Name):
raise NotImplementedError
if not isinstance(stmt.annotation, ast.Name):
raise NotImplementedError
if stmt.annotation.id != 'i32':
raise NotImplementedError
if stmt.value is None:
default = None
else:
if not isinstance(stmt.value, ast.Constant):
raise NotImplementedError
if not isinstance(stmt.value.value, int):
raise NotImplementedError
default = wasm.Constant(stmt.value.value)
members.append(wasm.ClassMember(
stmt.target.id, stmt.annotation.id, default
))
return wasm.Class(node.name, members)
def visit_stmt( def visit_stmt(
self, self,
module: wasm.Module, module: wasm.Module,
@ -235,6 +295,9 @@ class Visitor:
if isinstance(node, ast.Name): if isinstance(node, ast.Name):
return self.visit_Name(wlocals, exp_type, node) return self.visit_Name(wlocals, exp_type, node)
if isinstance(node, ast.Attribute):
return [] # TODO
raise NotImplementedError(node) raise NotImplementedError(node)
def visit_UnaryOp( def visit_UnaryOp(
@ -331,10 +394,11 @@ class Visitor:
called_name = node.func.id called_name = node.func.id
search_list: List[Union[wasm.Function, wasm.Import]] search_list: List[Union[wasm.Function, wasm.Import, wasm.Class]]
search_list = [ search_list = [
*module.functions, *module.functions,
*module.imports, *module.imports,
*module.classes,
] ]
called_func_list = [ called_func_list = [
@ -346,8 +410,15 @@ class Visitor:
assert 1 == len(called_func_list), \ assert 1 == len(called_func_list), \
'Could not find function {}'.format(node.func.id) 'Could not find function {}'.format(node.func.id)
called_params = called_func_list[0].params if isinstance(called_func_list[0], wasm.Class):
called_result = called_func_list[0].result called_params = [
(x.name, x.type, )
for x in called_func_list[0].members
]
called_result: Optional[str] = called_func_list[0].name
else:
called_params = called_func_list[0].params
called_result = called_func_list[0].result
assert exp_type == called_result assert exp_type == called_result
@ -471,5 +542,5 @@ def _parse_annotation(ann: Optional[ast.expr]) -> str:
assert ann is not None, 'Web Assembly requires type annotations' assert ann is not None, 'Web Assembly requires type annotations'
assert isinstance(ann, ast.Name) assert isinstance(ann, ast.Name)
result = ann.id result = ann.id
assert result in ['i32', 'i64', 'f32', 'f64'] assert result in ['i32', 'i64', 'f32', 'f64'], result
return result return result

View File

@ -2,7 +2,7 @@
Python classes for storing the representation of Web Assembly code Python classes for storing the representation of Web Assembly code
""" """
from typing import Iterable, List, Optional, Tuple from typing import Iterable, List, Optional, Tuple, Union
Param = Tuple[str, str] Param = Tuple[str, str]
@ -83,6 +83,30 @@ class Function:
'\n '.join(x.generate() for x in self.statements), '\n '.join(x.generate() for x in self.statements),
) )
class Constant:
"""
TODO
"""
def __init__(self, value: Union[None, bool, int, float]) -> None:
self.value = value
class ClassMember:
"""
Represents a Web Assembly class member
"""
def __init__(self, name: str, type_: str, default: Optional[Constant]) -> None:
self.name = name
self.type = type_
self.default = default
class Class:
"""
Represents a Web Assembly class
"""
def __init__(self, name: str, members: List[ClassMember]) -> None:
self.name = name
self.members = members
class Module: class Module:
""" """
Represents a Web Assembly module Represents a Web Assembly module
@ -90,6 +114,7 @@ class Module:
def __init__(self) -> None: def __init__(self) -> None:
self.imports: List[Import] = [] self.imports: List[Import] = []
self.functions: List[Function] = [] self.functions: List[Function] = []
self.classes: List[Class] = []
def generate(self) -> str: def generate(self) -> str:
""" """

View File

@ -28,3 +28,4 @@ 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

View File

@ -234,3 +234,40 @@ def helper(left: i32, right: i32) -> i32:
assert 7 == result.returned_value assert 7 == result.returned_value
assert [] == result.log_int32_list assert [] == result.log_int32_list
@pytest.mark.integration_test
def test_assign():
code_py = """
@exported
def testEntry() -> i32:
a: i32 = 8947
return a
"""
result = Suite(code_py, 'test_call').run_code()
assert 8947 == result.returned_value
assert [] == result.log_int32_list
@pytest.mark.integration_test
def test_struct():
code_py = """
class Rectangle:
height: i32
width: i32
border: i32 # = 5
@exported
def testEntry() -> i32:
return helper(Rectangle(100, 150, 2))
def helper(shape: Rectangle) -> i32:
return shape.height + shape.width + shape.border
"""
result = Suite(code_py, 'test_call').run_code()
assert 100 == result.returned_value
assert [] == result.log_int32_list