Skip to content
This repository was archived by the owner on Jun 10, 2025. It is now read-only.

[WIP] S-expression parsing of text modules #104

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,4 @@ prof/**

# WASM Spec submodule
./spec/**
./tests/spec/fixtures/**
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"numpy>=1.16.0,<2",
"toolz>0.9.0,<1;implementation_name=='pypy'",
"cytoolz>=0.9.0,<1.0.0;implementation_name=='cpython'",
"parsimonious>=0.8.1,<0.9",
],
setup_requires=['setuptools-markdown'],
python_requires='>=3.5, <4',
Expand Down
2 changes: 1 addition & 1 deletion tests/core/parsers/test_blocktype_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from wasm.exceptions import (
ParseError,
)
from wasm.parsers.blocks import (
from wasm.binary.blocks import (
parse_blocktype,
)

Expand Down
2 changes: 1 addition & 1 deletion tests/core/parsers/test_floating_point_parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
)
import pytest

from wasm.parsers.floats import (
from wasm.binary.floats import (
parse_f32,
parse_f64,
)
Expand Down
2 changes: 1 addition & 1 deletion tests/core/parsers/test_integer_parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from wasm.exceptions import (
MalformedModule,
)
from wasm.parsers.integers import (
from wasm.binary.integers import (
parse_i32,
parse_i64,
parse_s32,
Expand Down
2 changes: 1 addition & 1 deletion tests/core/parsers/test_leb128_parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import pytest

from wasm.parsers.leb128 import (
from wasm.binary.leb128 import (
parse_signed_leb128,
parse_unsigned_leb128,
)
Expand Down
2 changes: 1 addition & 1 deletion tests/core/parsers/test_numeric_parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from wasm.opcodes import (
BinaryOpcode,
)
from wasm.parsers.numeric import (
from wasm.binary.numeric import (
parse_numeric_constant_instruction,
)

Expand Down
15 changes: 15 additions & 0 deletions tests/core/sexpressions/test_memory_type_parsing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import pytest

from wasm.text import parse
from wasm.datatypes import MemoryType


@pytest.mark.parametrize(
'sexpr,expected',
(
#('(memory 1)', MemoryType(1)),
),
)
def test_sexpression_memory_type_parsing(sexpr, expected):
actual = parse(sexpr)
assert actual == expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import pytest

import numpy

from wasm.text import parse
from wasm.datatypes import ValType
from wasm.opcodes import BinaryOpcode
from wasm.instructions.control import (
Block,
End,
Nop,
Return,
)
from wasm.instructions.numeric import (
UnOp,
I32Const,
)
from wasm.text.ir import NamedBlock


i32 = ValType.i32


@pytest.mark.parametrize(
'sexpr,expected',
(
("(block)", Block((), End.as_tail())),
("(block $blk)", NamedBlock('$blk', Block((), End.as_tail()))),
("(block (nop))", Block((), (Nop(),))),
(
"(block (result i32) (i32.ctz (return (i32.const 1))))",
Block(
(i32,),
(
I32Const(numpy.uint32(1)),
UnOp.from_opcode(BinaryOpcode.I32_CTZ),
Return(),
)
),
),
),
)
def test_sexpression_block_instructions_parsing(sexpr, expected):
actual = parse(sexpr)
assert actual == expected
43 changes: 43 additions & 0 deletions tests/core/sexpressions/test_sexpression_br_ops_parsing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import pytest

from wasm.text import parse
from wasm.datatypes import (
LabelIdx,
)
from wasm.instructions.control import (
Br,
BrIf,
BrTable,
)
from wasm.text.ir import (
UnresolvedBr,
UnresolvedBrIf,
UnresolvedBrTable,
UnresolvedLabelIdx,
)


@pytest.mark.parametrize(
'sexpr,expected',
(
("(br 0)", Br(LabelIdx(0))),
("(br $i)", UnresolvedBr(UnresolvedLabelIdx('$i'))),
("(br_if 0)", BrIf(LabelIdx(0))),
("(br_if $i)", UnresolvedBrIf(UnresolvedLabelIdx('$i'))),
("(br_table 1 2 3)", BrTable((LabelIdx(1), LabelIdx(2)), LabelIdx(3))),
(
"(br_table 1 2 $default)",
UnresolvedBrTable((LabelIdx(1), LabelIdx(2)), UnresolvedLabelIdx('$default')),
),
(
"(br_table 1 2 $three 4)",
UnresolvedBrTable(
(LabelIdx(1), LabelIdx(2), UnresolvedLabelIdx('$three')),
LabelIdx(4),
),
),
),
)
def test_sexpression_br_instructions_parsing(sexpr, expected):
actual, = parse(sexpr)
assert actual == expected
58 changes: 58 additions & 0 deletions tests/core/sexpressions/test_sexpression_call_op_parsing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import pytest

from wasm.text import parse
from wasm.instructions.control import (
Call,
)
from wasm.datatypes import (
ValType,
FunctionIdx,
)
from wasm.text.ir import (
Param,
UnresolvedFunctionType,
UnresolvedCallIndirect,
UnresolvedTypeIdx,
UnresolvedFunctionIdx,
UnresolvedCall,
)

i32 = ValType.i32
i64 = ValType.i64
f32 = ValType.f32
f64 = ValType.f64


@pytest.mark.parametrize(
'sexpr,expected',
(
("(call_indirect (result))", UnresolvedCallIndirect(UnresolvedFunctionType((), ()))),
(
"(call_indirect (param i64))",
UnresolvedCallIndirect(UnresolvedFunctionType((Param(i64),), ())),
),
(
"(call_indirect (param i64) (result i32))",
UnresolvedCallIndirect(UnresolvedFunctionType((Param(i64),), (i32,))),
),
(
"(call_indirect (type $check))",
UnresolvedCallIndirect(UnresolvedTypeIdx('$check')),
),
),
)
def test_sexpression_call_indirect_instruction_parsing(sexpr, expected):
actual, = parse(sexpr)
assert actual == expected


@pytest.mark.parametrize(
'sexpr,expected',
(
("(call $func-name)", UnresolvedCall(UnresolvedFunctionIdx('$func-name'))),
("(call 1)", Call(FunctionIdx(1))),
),
)
def test_sexpression_call_instruction_parsing(sexpr, expected):
actual, = parse(sexpr)
assert actual == expected
43 changes: 43 additions & 0 deletions tests/core/sexpressions/test_sexpression_func_type_parsing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import pytest

from wasm.text import parse
from wasm.datatypes import (
ValType,
)
from wasm.text.ir import (
Param,
UnresolvedFunctionType,
)

i32 = ValType.i32
i64 = ValType.i64
f32 = ValType.f32
f64 = ValType.f64


@pytest.mark.parametrize(
'sexpr,expected',
(
("(func (param))", UnresolvedFunctionType((), ())),
("(func (param) (param))", UnresolvedFunctionType((), ())),
("(func (param i32))", UnresolvedFunctionType((Param(i32),), ())),
("(func (param $x i32))", UnresolvedFunctionType((Param(i32, '$x'),), ())),
(
"(func (param i32 f64 i64))",
UnresolvedFunctionType((Param(i32), Param(f64), Param(i64)), ()),
),
("(func (param i32) (param f64))", UnresolvedFunctionType((Param(i32), Param(f64)), ())),
(
"(func (param i32 f32) (param $x i64) (param) (param i32 f64))",
UnresolvedFunctionType(
(Param(i32), Param(f32), Param(i64, '$x'), Param(i32), Param(f64)),
(),
),
),

# ("(func (result i32) (unreachable)) # TODO: this is not a raw functype but rather a `function` definition.
),
)
def test_sexpression_parametric_instruction_parsing(sexpr, expected):
actual = parse(sexpr)
assert actual == expected
33 changes: 33 additions & 0 deletions tests/core/sexpressions/test_sexpression_locals_parsing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import pytest

from wasm.text import parse
from wasm.text.ir import Local
from wasm.datatypes import ValType


i32 = ValType.i32
i64 = ValType.i64
f32 = ValType.f32
f64 = ValType.f64


@pytest.mark.parametrize(
'sexpr,expected',
(
# unnamed
('(local i32)', (Local(i32),)),
('(local i64)', (Local(i64),)),
('(local f32)', (Local(f32),)),
('(local f64)', (Local(f64),)),
('(local i32 i64)', (Local(i32), Local(i64))),
('(local f32 f64)', (Local(f32), Local(f64))),
('(local f32 f64 i32 i64)', (Local(f32), Local(f64), Local(i32), Local(i64))),
# named
('(local $i i32)', (Local(i32, '$i'),)),
# multi
('(local f32 f64)\n(local $i i32)', (Local(f32), Local(f64), Local(i32, '$i'))),
),
)
def test_sexpression_locals_parsing(sexpr, expected):
actual = parse(sexpr)
assert actual == expected
55 changes: 55 additions & 0 deletions tests/core/sexpressions/test_sexpression_memory_op_parsing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import pytest

from wasm.text import parse
from wasm.instructions.memory import (
MemoryArg,
MemoryOp,
MemorySize,
MemoryGrow,
)
from wasm.opcodes import BinaryOpcode


@pytest.mark.parametrize(
'sexpr,expected',
(
("(i32.load)", MemoryOp.from_opcode(BinaryOpcode.I32_LOAD, MemoryArg(0, 4))),
("(i64.load)", MemoryOp.from_opcode(BinaryOpcode.I64_LOAD, MemoryArg(0, 8))),
("(f32.load)", MemoryOp.from_opcode(BinaryOpcode.F32_LOAD, MemoryArg(0, 4))),
("(f64.load)", MemoryOp.from_opcode(BinaryOpcode.F64_LOAD, MemoryArg(0, 8))),
("(i32.load8_s)", MemoryOp.from_opcode(BinaryOpcode.I32_LOAD8_S, MemoryArg(0, 1))),
("(i32.load8_u)", MemoryOp.from_opcode(BinaryOpcode.I32_LOAD8_U, MemoryArg(0, 1))),
("(i32.load16_s)", MemoryOp.from_opcode(BinaryOpcode.I32_LOAD16_S, MemoryArg(0, 2))),
("(i32.load16_u)", MemoryOp.from_opcode(BinaryOpcode.I32_LOAD16_U, MemoryArg(0, 2))),
("(i64.load8_s)", MemoryOp.from_opcode(BinaryOpcode.I64_LOAD8_S, MemoryArg(0, 1))),
("(i64.load8_u)", MemoryOp.from_opcode(BinaryOpcode.I64_LOAD8_U, MemoryArg(0, 1))),
("(i64.load16_s)", MemoryOp.from_opcode(BinaryOpcode.I64_LOAD16_S, MemoryArg(0, 2))),
("(i64.load16_u)", MemoryOp.from_opcode(BinaryOpcode.I64_LOAD16_U, MemoryArg(0, 2))),
("(i64.load32_s)", MemoryOp.from_opcode(BinaryOpcode.I64_LOAD32_S, MemoryArg(0, 4))),
("(i64.load32_u)", MemoryOp.from_opcode(BinaryOpcode.I64_LOAD32_U, MemoryArg(0, 4))),
("(i32.store)", MemoryOp.from_opcode(BinaryOpcode.I32_STORE, MemoryArg(0, 4))),
("(i64.store)", MemoryOp.from_opcode(BinaryOpcode.I64_STORE, MemoryArg(0, 8))),
("(f32.store)", MemoryOp.from_opcode(BinaryOpcode.F32_STORE, MemoryArg(0, 4))),
("(f64.store)", MemoryOp.from_opcode(BinaryOpcode.F64_STORE, MemoryArg(0, 8))),
("(i32.store8)", MemoryOp.from_opcode(BinaryOpcode.I32_STORE8, MemoryArg(0, 1))),
("(i32.store16)", MemoryOp.from_opcode(BinaryOpcode.I32_STORE16, MemoryArg(0, 2))),
("(i64.store8)", MemoryOp.from_opcode(BinaryOpcode.I64_STORE8, MemoryArg(0, 1))),
("(i64.store16)", MemoryOp.from_opcode(BinaryOpcode.I64_STORE16, MemoryArg(0, 2))),
("(i64.store32)", MemoryOp.from_opcode(BinaryOpcode.I64_STORE32, MemoryArg(0, 4))),
),
)
def test_sexpression_memory_load_and_store_parsing(sexpr, expected):
actual, = parse(sexpr)
assert actual == expected


@pytest.mark.parametrize(
'sexpr,expected',
(
("(memory.size)", MemorySize()),
("(memory.grow)", MemoryGrow()),
),
)
def test_sexpression_memory_size_and_grow_parsing(sexpr, expected):
actual, = parse(sexpr)
assert actual == expected
17 changes: 17 additions & 0 deletions tests/core/sexpressions/test_sexpression_nop_op_parsing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import pytest

from wasm.text import parse
from wasm.instructions.control import (
Nop
)


@pytest.mark.parametrize(
'sexpr,expected',
(
("(nop)", Nop()),
),
)
def test_sexpression_nop_instructions_parsing(sexpr, expected):
actual, = parse(sexpr)
assert actual == expected
Loading