MVP #1
2
Makefile
2
Makefile
@ -8,7 +8,7 @@ server:
|
||||
python3.8 -m http.server
|
||||
|
||||
test: venv/.done
|
||||
venv/bin/pytest tests
|
||||
venv/bin/pytest tests $(TEST_FLAGS)
|
||||
|
||||
lint: venv/.done
|
||||
venv/bin/pylint py2wasm
|
||||
|
||||
@ -2,3 +2,9 @@ webasm (python)
|
||||
===============
|
||||
|
||||
You will need wat2wasm from github.com/WebAssembly/wabt in your path.
|
||||
|
||||
Ideas
|
||||
=====
|
||||
- https://github.com/wasmerio/wasmer-python
|
||||
- https://github.com/diekmann/wasm-fizzbuzz
|
||||
- https://blog.scottlogic.com/2018/04/26/webassembly-by-hand.html
|
||||
|
||||
@ -127,6 +127,9 @@ class Visitor:
|
||||
|
||||
return self.visit_Call(module, wlocals, "None", stmt.value)
|
||||
|
||||
if isinstance(stmt, ast.If):
|
||||
return self.visit_If(module, func, wlocals, stmt)
|
||||
|
||||
raise NotImplementedError(stmt)
|
||||
|
||||
def visit_Return(
|
||||
@ -137,14 +140,44 @@ class Visitor:
|
||||
stmt: ast.Return,
|
||||
) -> StatementGenerator:
|
||||
"""
|
||||
Visits a statement node
|
||||
Visits a Return node
|
||||
"""
|
||||
|
||||
assert stmt.value is not None
|
||||
|
||||
return_type = _parse_annotation(func.returns)
|
||||
|
||||
return self.visit_expr(module, wlocals, return_type, stmt.value)
|
||||
yield from self.visit_expr(module, wlocals, return_type, stmt.value)
|
||||
yield wasm.Statement('return')
|
||||
|
||||
def visit_If(
|
||||
self,
|
||||
module: wasm.Module,
|
||||
func: ast.FunctionDef,
|
||||
wlocals: WLocals,
|
||||
stmt: ast.If,
|
||||
) -> StatementGenerator:
|
||||
"""
|
||||
Visits an If node
|
||||
"""
|
||||
yield from self.visit_expr(
|
||||
module,
|
||||
wlocals,
|
||||
'bool',
|
||||
stmt.test,
|
||||
)
|
||||
|
||||
yield wasm.Statement('if')
|
||||
|
||||
for py_stmt in stmt.body:
|
||||
yield from self.visit_stmt(module, func, wlocals, py_stmt)
|
||||
|
||||
yield wasm.Statement('else')
|
||||
|
||||
for py_stmt in stmt.orelse:
|
||||
yield from self.visit_stmt(module, func, wlocals, py_stmt)
|
||||
|
||||
yield wasm.Statement('end')
|
||||
|
||||
def visit_expr(
|
||||
self,
|
||||
@ -159,6 +192,12 @@ class Visitor:
|
||||
if isinstance(node, ast.BinOp):
|
||||
return self.visit_BinOp(module, wlocals, exp_type, node)
|
||||
|
||||
if isinstance(node, ast.UnaryOp):
|
||||
return self.visit_UnaryOp(module, wlocals, exp_type, node)
|
||||
|
||||
if isinstance(node, ast.Compare):
|
||||
return self.visit_Compare(module, wlocals, exp_type, node)
|
||||
|
||||
if isinstance(node, ast.Call):
|
||||
return self.visit_Call(module, wlocals, exp_type, node)
|
||||
|
||||
@ -170,6 +209,24 @@ class Visitor:
|
||||
|
||||
raise NotImplementedError(node)
|
||||
|
||||
def visit_UnaryOp(
|
||||
self,
|
||||
module: wasm.Module,
|
||||
wlocals: WLocals,
|
||||
exp_type: str,
|
||||
node: ast.UnaryOp,
|
||||
) -> StatementGenerator:
|
||||
"""
|
||||
Visits a UnaryOp node as (part of) an expression
|
||||
"""
|
||||
del module
|
||||
del wlocals
|
||||
|
||||
if not isinstance(node.operand, ast.Constant):
|
||||
raise NotImplementedError
|
||||
|
||||
return self.visit_Constant(exp_type, node.operand)
|
||||
|
||||
def visit_BinOp(
|
||||
self,
|
||||
module: wasm.Module,
|
||||
@ -189,6 +246,34 @@ class Visitor:
|
||||
|
||||
raise NotImplementedError(node.op)
|
||||
|
||||
def visit_Compare(
|
||||
self,
|
||||
module: wasm.Module,
|
||||
wlocals: WLocals,
|
||||
exp_type: str,
|
||||
node: ast.Compare,
|
||||
) -> StatementGenerator:
|
||||
"""
|
||||
Visits a Compare node as (part of) an expression
|
||||
"""
|
||||
assert 'bool' == exp_type
|
||||
|
||||
if 1 != len(node.ops) or 1 != len(node.comparators):
|
||||
raise NotImplementedError
|
||||
|
||||
yield from self.visit_expr(module, wlocals, 'i32', node.left)
|
||||
yield from self.visit_expr(module, wlocals, 'i32', node.comparators[0])
|
||||
|
||||
if isinstance(node.ops[0], ast.Lt):
|
||||
yield wasm.Statement('i32.lt_s')
|
||||
return
|
||||
|
||||
if isinstance(node.ops[0], ast.Gt):
|
||||
yield wasm.Statement('i32.gt_s')
|
||||
return
|
||||
|
||||
raise NotImplementedError(node.ops)
|
||||
|
||||
def visit_Call(
|
||||
self,
|
||||
module: wasm.Module,
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
mypy==0.812
|
||||
pylint==2.7.4
|
||||
pytest==6.2.2
|
||||
pytest-integration==0.2.2
|
||||
pywasm==1.0.7
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
import pytest
|
||||
|
||||
from .helpers import Suite
|
||||
|
||||
@pytest.mark.slow_integration_test
|
||||
def test_fib():
|
||||
code_py = """
|
||||
def helper(n: i32, a: i32, b: i32) -> i32:
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
import pytest
|
||||
|
||||
from .helpers import Suite
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_return():
|
||||
code_py = """
|
||||
@exported
|
||||
@ -7,11 +10,25 @@ def testEntry() -> i32:
|
||||
return 13
|
||||
"""
|
||||
|
||||
result = Suite(code_py, 'test_fib').run_code()
|
||||
result = Suite(code_py, 'test_return').run_code()
|
||||
|
||||
assert 13 == result.returned_value
|
||||
assert [] == result.log_int32_list
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_arg():
|
||||
code_py = """
|
||||
@exported
|
||||
def testEntry(a: i32) -> i32:
|
||||
return a
|
||||
"""
|
||||
|
||||
result = Suite(code_py, 'test_return').run_code(125)
|
||||
|
||||
assert 125 == result.returned_value
|
||||
assert [] == result.log_int32_list
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_addition():
|
||||
code_py = """
|
||||
@exported
|
||||
@ -19,11 +36,57 @@ def testEntry() -> i32:
|
||||
return 10 + 3
|
||||
"""
|
||||
|
||||
result = Suite(code_py, 'test_fib').run_code()
|
||||
result = Suite(code_py, 'test_addition').run_code()
|
||||
|
||||
assert 13 == result.returned_value
|
||||
assert [] == result.log_int32_list
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_if_simple():
|
||||
code_py = """
|
||||
@exported
|
||||
def testEntry(a: i32) -> i32:
|
||||
if a > 10:
|
||||
return 1
|
||||
|
||||
return 0
|
||||
"""
|
||||
|
||||
suite = Suite(code_py, 'test_return')
|
||||
|
||||
result = suite.run_code(10)
|
||||
assert 0 == result.returned_value
|
||||
assert [] == result.log_int32_list
|
||||
|
||||
result = suite.run_code(11)
|
||||
assert 1 == result.returned_value
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_if_complex():
|
||||
code_py = """
|
||||
@exported
|
||||
def testEntry(a: i32) -> i32:
|
||||
if a > 10:
|
||||
return 10
|
||||
elif a > 0:
|
||||
return a
|
||||
else:
|
||||
return 0
|
||||
|
||||
return -1 # Required due to function type
|
||||
"""
|
||||
|
||||
suite = Suite(code_py, 'test_return')
|
||||
|
||||
assert 10 == suite.run_code(20).returned_value
|
||||
assert 10 == suite.run_code(10).returned_value
|
||||
|
||||
assert 8 == suite.run_code(8).returned_value
|
||||
|
||||
assert 0 == suite.run_code(0).returned_value
|
||||
assert 0 == suite.run_code(-1).returned_value
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_call():
|
||||
code_py = """
|
||||
def helper(left: i32, right: i32) -> i32:
|
||||
@ -34,7 +97,7 @@ def testEntry() -> i32:
|
||||
return helper(10, 3)
|
||||
"""
|
||||
|
||||
result = Suite(code_py, 'test_fib').run_code()
|
||||
result = Suite(code_py, 'test_call').run_code()
|
||||
|
||||
assert 13 == result.returned_value
|
||||
assert [] == result.log_int32_list
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user