MVP #1
1
TODO.md
1
TODO.md
@ -1,5 +1,4 @@
|
||||
# TODO
|
||||
|
||||
- Implement foldl for bytes
|
||||
- Implement a trace() builtin for debugging
|
||||
- Implement a proper type matching / checking system
|
||||
|
||||
65
examples/fold.html
Normal file
65
examples/fold.html
Normal file
@ -0,0 +1,65 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Examples - Fold</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Fold</h1>
|
||||
|
||||
<a href="index.html">List</a> - <a href="fold.py.html">Source</a> - <a href="fold.wat.html">WebAssembly</a>
|
||||
|
||||
<div style="white-space: pre;" id="results"></div>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
let importObject = {
|
||||
'imports': {
|
||||
'log': log,
|
||||
}
|
||||
};
|
||||
|
||||
let results = document.getElementById('results');
|
||||
|
||||
function log(txt)
|
||||
{
|
||||
let span = document.createElement('span');
|
||||
span.innerHTML = txt;
|
||||
results.appendChild(span);
|
||||
let br = document.createElement('br');
|
||||
results.appendChild(br);
|
||||
}
|
||||
|
||||
WebAssembly.instantiateStreaming(fetch('fold.wasm'), importObject)
|
||||
.then(app => {
|
||||
stdlib_alloc___init__ = app.instance.exports['stdlib.alloc.__init__']
|
||||
stdlib_alloc___init__()
|
||||
|
||||
stdlib_types___alloc_bytes__ = app.instance.exports['stdlib.types.__alloc_bytes__']
|
||||
|
||||
let offset0 = stdlib_types___alloc_bytes__(0);
|
||||
let offset1 = stdlib_types___alloc_bytes__(1);
|
||||
let offset2 = stdlib_types___alloc_bytes__(2);
|
||||
let offset4 = stdlib_types___alloc_bytes__(4);
|
||||
|
||||
var i8arr0 = new Uint8Array(app.instance.exports.memory.buffer, offset0 + 4, 0);
|
||||
var i8arr1 = new Uint8Array(app.instance.exports.memory.buffer, offset1 + 4, 1);
|
||||
i8arr1[0] = 0x20;
|
||||
var i8arr2 = new Uint8Array(app.instance.exports.memory.buffer, offset2 + 4, 2);
|
||||
i8arr2[0] = 0x20;
|
||||
i8arr2[1] = 0x10;
|
||||
var i8arr4 = new Uint8Array(app.instance.exports.memory.buffer, offset4 + 4, 4);
|
||||
i8arr4[0] = 0x20;
|
||||
i8arr4[1] = 0x10;
|
||||
i8arr4[2] = 0x08;
|
||||
i8arr4[3] = 0x04;
|
||||
i8arr4[4] = 0x02;
|
||||
|
||||
log('foldl(or, 1, [' + i8arr0 + ']) = ' + app.instance.exports.foldl_u8_or_1(offset0));
|
||||
log('foldl(or, 1, [' + i8arr1 + ']) = ' + app.instance.exports.foldl_u8_or_1(offset1));
|
||||
log('foldl(or, 1, [' + i8arr2 + ']) = ' + app.instance.exports.foldl_u8_or_1(offset2));
|
||||
log('foldl(or, 1, [' + i8arr4 + ']) = ' + app.instance.exports.foldl_u8_or_1(offset4));
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
6
examples/fold.py
Normal file
6
examples/fold.py
Normal file
@ -0,0 +1,6 @@
|
||||
def u8_or(l: u8, r: u8) -> u8:
|
||||
return l | r
|
||||
|
||||
@exported
|
||||
def foldl_u8_or_1(b: bytes) -> u8:
|
||||
return foldl(u8_or, 1, b)
|
||||
@ -11,6 +11,7 @@
|
||||
<h2>Technical</h2>
|
||||
<ul>
|
||||
<li><a href="buffer.html">Buffer</a></li>
|
||||
<li><a href="fold.html">Folding</a></li>
|
||||
<li><a href="imported.html">Imported</a></li>
|
||||
</ul>
|
||||
</body>
|
||||
|
||||
@ -125,6 +125,10 @@ def expression(inp: ourlang.Expression) -> str:
|
||||
if isinstance(inp, ourlang.AccessTupleMember):
|
||||
return f'{expression(inp.varref)}[{inp.member.idx}]'
|
||||
|
||||
if isinstance(inp, ourlang.Fold):
|
||||
fold_name = 'foldl' if ourlang.Fold.Direction.LEFT == inp.dir else 'foldr'
|
||||
return f'{fold_name}({inp.func.name}, {expression(inp.base)}, {expression(inp.iter)})'
|
||||
|
||||
raise NotImplementedError(expression, inp)
|
||||
|
||||
def statement(inp: ourlang.Statement) -> Statements:
|
||||
|
||||
@ -77,9 +77,10 @@ OPERATOR_MAP = {
|
||||
|
||||
U8_OPERATOR_MAP = {
|
||||
# Under the hood, this is an i32
|
||||
# Implementing XOR is fine since the 3 remaining
|
||||
# Implementing XOR, OR is fine since the 3 remaining
|
||||
# bytes stay zero after this operation
|
||||
'^': 'xor',
|
||||
'|': 'or',
|
||||
}
|
||||
|
||||
U32_OPERATOR_MAP = {
|
||||
@ -254,6 +255,15 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
||||
return
|
||||
|
||||
if isinstance(inp, ourlang.Fold):
|
||||
expression_fold(wgn, inp)
|
||||
return
|
||||
|
||||
raise NotImplementedError(expression, inp)
|
||||
|
||||
def expression_fold(wgn: WasmGenerator, inp: ourlang.Fold) -> None:
|
||||
"""
|
||||
Compile: Fold expression
|
||||
"""
|
||||
mtyp = LOAD_STORE_TYPE_MAP.get(inp.base.type.__class__)
|
||||
if mtyp is None:
|
||||
# In the future might extend this by having structs or tuples
|
||||
@ -263,16 +273,63 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
|
||||
if inp.iter.type.__class__.__name__ != 'TypeBytes':
|
||||
raise NotImplementedError(expression, inp, inp.iter.type)
|
||||
|
||||
tmp_var = wgn.temp_var_u8(f'fold_{codestyle.type_(inp.type)}')
|
||||
wgn.add_statement('nop', comment='acu :: u8')
|
||||
acu_var = wgn.temp_var_u8(f'fold_{codestyle.type_(inp.type)}_acu')
|
||||
wgn.add_statement('nop', comment='adr :: bytes*')
|
||||
adr_var = wgn.temp_var_i32(f'fold_i32_adr')
|
||||
wgn.add_statement('nop', comment='len :: i32')
|
||||
len_var = wgn.temp_var_i32(f'fold_i32_len')
|
||||
|
||||
wgn.add_statement('nop', comment='acu = base')
|
||||
expression(wgn, inp.base)
|
||||
wgn.local.set(tmp_var)
|
||||
wgn.local.set(acu_var)
|
||||
|
||||
wgn.add_statement('nop', comment='adr = adr(iter)')
|
||||
expression(wgn, inp.iter)
|
||||
wgn.local.set(adr_var)
|
||||
|
||||
wgn.add_statement('nop', comment='len = len(iter)')
|
||||
wgn.local.get(adr_var)
|
||||
wgn.i32.load()
|
||||
wgn.local.set(len_var)
|
||||
|
||||
wgn.add_statement('nop', comment='i = 0')
|
||||
idx_var = wgn.temp_var_i32(f'fold_{codestyle.type_(inp.type)}_idx')
|
||||
wgn.i32.const(0)
|
||||
wgn.local.set(idx_var)
|
||||
|
||||
wgn.add_statement('nop', comment='if i < len')
|
||||
wgn.local.get(idx_var)
|
||||
wgn.local.get(len_var)
|
||||
wgn.i32.lt_u()
|
||||
with wgn.if_():
|
||||
wgn.add_statement('nop', comment='while True')
|
||||
with wgn.loop():
|
||||
wgn.add_statement('nop', comment='acu = func(acu, iter[i])')
|
||||
wgn.local.get(acu_var)
|
||||
wgn.local.get(adr_var)
|
||||
wgn.local.get(idx_var)
|
||||
wgn.call(stdlib_types.__subscript_bytes__)
|
||||
wgn.add_statement('call', f'${inp.func.name}')
|
||||
wgn.local.set(acu_var)
|
||||
|
||||
wgn.add_statement('nop', comment='i = i + 1')
|
||||
wgn.local.get(idx_var)
|
||||
wgn.i32.const(1)
|
||||
wgn.i32.add()
|
||||
wgn.local.set(idx_var)
|
||||
|
||||
wgn.add_statement('nop', comment='if i >= len: break')
|
||||
wgn.local.get(idx_var)
|
||||
wgn.local.get(len_var)
|
||||
wgn.i32.lt_u()
|
||||
wgn.br_if(0)
|
||||
|
||||
# return acu
|
||||
wgn.local.get(acu_var)
|
||||
|
||||
# FIXME: Do a loop
|
||||
wgn.unreachable(comment='Not implemented yet')
|
||||
return
|
||||
|
||||
raise NotImplementedError(expression, inp)
|
||||
|
||||
def statement_return(wgn: WasmGenerator, inp: ourlang.StatementReturn) -> None:
|
||||
"""
|
||||
Compile: Return statement
|
||||
|
||||
@ -244,6 +244,8 @@ class OurVisitor:
|
||||
operator = '-'
|
||||
elif isinstance(node.op, ast.Mult):
|
||||
operator = '*'
|
||||
elif isinstance(node.op, ast.BitOr):
|
||||
operator = '|'
|
||||
elif isinstance(node.op, ast.BitXor):
|
||||
operator = '^'
|
||||
else:
|
||||
|
||||
@ -35,6 +35,7 @@ class Generator_i32i64:
|
||||
self.eq = functools.partial(self.generator.add_statement, f'{prefix}.eq')
|
||||
self.ne = functools.partial(self.generator.add_statement, f'{prefix}.ne')
|
||||
self.lt_u = functools.partial(self.generator.add_statement, f'{prefix}.lt_u')
|
||||
self.ge_u = functools.partial(self.generator.add_statement, f'{prefix}.ge_u')
|
||||
|
||||
# 2.4.4. Memory Instructions
|
||||
self.load = functools.partial(self.generator.add_statement, f'{prefix}.load')
|
||||
@ -128,15 +129,18 @@ class Generator:
|
||||
self.nop = functools.partial(self.add_statement, 'nop')
|
||||
self.unreachable = functools.partial(self.add_statement, 'unreachable')
|
||||
# block
|
||||
# loop
|
||||
self.loop = functools.partial(GeneratorBlock, self, 'loop')
|
||||
self.if_ = functools.partial(GeneratorBlock, self, 'if')
|
||||
# br
|
||||
# br_if
|
||||
# br_if - see below
|
||||
# br_table
|
||||
self.return_ = functools.partial(self.add_statement, 'return')
|
||||
# call
|
||||
# call - see below
|
||||
# call_indirect
|
||||
|
||||
def br_if(self, idx: int) -> None:
|
||||
self.add_statement('br_if', f'{idx}')
|
||||
|
||||
def call(self, function: wasm.Function) -> None:
|
||||
self.add_statement('call', f'${function.name}')
|
||||
|
||||
|
||||
65
tests/integration/test_builtins.py
Normal file
65
tests/integration/test_builtins.py
Normal file
@ -0,0 +1,65 @@
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
from .helpers import Suite, write_header
|
||||
from .runners import RunnerPywasm
|
||||
|
||||
def setup_interpreter(phash_code: str) -> RunnerPywasm:
|
||||
runner = RunnerPywasm(phash_code)
|
||||
|
||||
runner.parse()
|
||||
runner.compile_ast()
|
||||
runner.compile_wat()
|
||||
runner.compile_wasm()
|
||||
runner.interpreter_setup()
|
||||
runner.interpreter_load()
|
||||
|
||||
write_header(sys.stderr, 'Phasm')
|
||||
runner.dump_phasm_code(sys.stderr)
|
||||
write_header(sys.stderr, 'Assembly')
|
||||
runner.dump_wasm_wat(sys.stderr)
|
||||
|
||||
return runner
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_foldl_1():
|
||||
code_py = """
|
||||
def u8_or(l: u8, r: u8) -> u8:
|
||||
return l | r
|
||||
|
||||
@exported
|
||||
def testEntry(b: bytes) -> u8:
|
||||
return foldl(u8_or, 128, b)
|
||||
"""
|
||||
suite = Suite(code_py)
|
||||
|
||||
result = suite.run_code(b'')
|
||||
assert 128 == result.returned_value
|
||||
|
||||
result = suite.run_code(b'\x80', runtime='pywasm')
|
||||
assert 128 == result.returned_value
|
||||
|
||||
result = suite.run_code(b'\x80\x40', runtime='pywasm')
|
||||
assert 192 == result.returned_value
|
||||
|
||||
result = suite.run_code(b'\x80\x40\x20\x10', runtime='pywasm')
|
||||
assert 240 == result.returned_value
|
||||
|
||||
result = suite.run_code(b'\x80\x40\x20\x10\x08\x04\x02\x01', runtime='pywasm')
|
||||
assert 255 == result.returned_value
|
||||
|
||||
@pytest.mark.integration_test
|
||||
def test_foldl_2():
|
||||
code_py = """
|
||||
def xor(l: u8, r: u8) -> u8:
|
||||
return l ^ r
|
||||
|
||||
@exported
|
||||
def testEntry(a: bytes, b: bytes) -> u8:
|
||||
return foldl(xor, 0, a) ^ foldl(xor, 0, b)
|
||||
"""
|
||||
suite = Suite(code_py)
|
||||
|
||||
result = suite.run_code(b'\x55\x0F', b'\x33\x80')
|
||||
assert 233 == result.returned_value
|
||||
Loading…
x
Reference in New Issue
Block a user