MVP #1

Merged
jbwdevries merged 73 commits from idea_crc32 into master 2022-08-21 12:59:21 +00:00
9 changed files with 223 additions and 20 deletions
Showing only changes of commit b6fb0d45b6 - Show all commits

View File

@ -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
View 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
View 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)

View File

@ -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>

View File

@ -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:

View File

@ -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,25 +255,81 @@ def expression(wgn: WasmGenerator, inp: ourlang.Expression) -> None:
return
if isinstance(inp, ourlang.Fold):
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
# as members of struct or tuples
raise NotImplementedError(expression, inp, inp.base)
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)}')
expression(wgn, inp.base)
wgn.local.set(tmp_var)
# FIXME: Do a loop
wgn.unreachable(comment='Not implemented yet')
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
# as members of struct or tuples
raise NotImplementedError(expression, inp, inp.base)
if inp.iter.type.__class__.__name__ != 'TypeBytes':
raise NotImplementedError(expression, inp, inp.iter.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(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)
return
def statement_return(wgn: WasmGenerator, inp: ourlang.StatementReturn) -> None:
"""
Compile: Return statement

View File

@ -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:

View File

@ -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}')

View 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