diff --git a/src/Language/Wasm/Parser.y b/src/Language/Wasm/Parser.y index 78d3c48..0db71d8 100644 --- a/src/Language/Wasm/Parser.y +++ b/src/Language/Wasm/Parser.y @@ -553,12 +553,25 @@ foldedinstr :: { [Instruction] } foldedinst1 :: { [Instruction] } : plaininstr list(foldedinstr) ')' { concat $2 ++ [PlainInstr $1] } | 'call_indirect' folded_call_indirect { $2 } - | 'block' opt(ident) opt(resulttype) list(instr) ')' { [BlockInstr $2 (fromMaybe [] $3) $4] } - | 'loop' opt(ident) opt(resulttype) list(instr) ')' { [LoopInstr $2 (fromMaybe [] $3) $4] } + | 'block' opt(ident) folded_block { [$3 $2] } + | 'loop' opt(ident) folded_loop { [$3 $2] } | 'if' opt(ident) '(' folded_if_result { $4 $2 } - -- opt(resulttype) list(foldedinstr) - -- '(' 'then' list(instr) ')' - -- '(' 'else' list(instr) opt(')') ')' { concat $4 ++ [IfInstr $2 (fromMaybe [] $3) $7 $11] } + +folded_block :: { Maybe Ident -> Instruction } + : ')' { \ident -> BlockInstr ident [] [] } + | '(' folded_block1 { $2 } + +folded_block1 :: { Maybe Ident -> Instruction } + : 'result' valtype ')' list(foldedinstr) ')' { \ident -> BlockInstr ident [$2] (concat $4) } + | foldedinst1 list(foldedinstr) ')' { \ident -> BlockInstr ident [] ($1 ++ concat $2) } + +folded_loop :: { Maybe Ident -> Instruction } + : ')' { \ident -> LoopInstr ident [] [] } + | '(' folded_loop1 { $2 } + +folded_loop1 :: { Maybe Ident -> Instruction } + : 'result' valtype ')' list(foldedinstr) ')' { \ident -> LoopInstr ident [$2] (concat $4) } + | foldedinst1 list(foldedinstr) ')' { \ident -> LoopInstr ident [] ($1 ++ concat $2) } folded_if_result :: { Maybe Ident -> [Instruction] } : 'result' valtype ')' '(' folded_then_else { \ident -> [IfInstr ident [$2] (fst $5) (snd $5)] } diff --git a/tests/Test.hs b/tests/Test.hs index ca8a657..3ae6d67 100644 --- a/tests/Test.hs +++ b/tests/Test.hs @@ -12,6 +12,6 @@ import qualified Language.Wasm.Parser as Parser main :: IO () main = do - file <- LBS.readFile "tests/samples/if.wast" + file <- LBS.readFile "tests/samples/loop.wast" print $ Parser.parseModule <$> Lexer.scanner file defaultMain $ testGroup "Test Suite" [] diff --git a/tests/samples/block.wast b/tests/samples/block.wast new file mode 100644 index 0000000..cec7c2d --- /dev/null +++ b/tests/samples/block.wast @@ -0,0 +1,138 @@ +;; Test `block` operator + +(module + ;; Auxiliary definition + (func $dummy) + + (func (export "empty") + (block) + (block $l) + ) + + (func (export "singular") (result i32) + (block (nop)) + (block (result i32) (i32.const 7)) + ) + + (func (export "multi") (result i32) + (block (call $dummy) (call $dummy) (call $dummy) (call $dummy)) + (block (result i32) (call $dummy) (call $dummy) (call $dummy) (i32.const 8)) + ) + + (func (export "nested") (result i32) + (block (result i32) + (block (call $dummy) (block) (nop)) + (block (result i32) (call $dummy) (i32.const 9)) + ) + ) + + (func (export "deep") (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (block (result i32) (block (result i32) + (call $dummy) (i32.const 150) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + ) + + (func (export "as-unary-operand") (result i32) + (i32.ctz (block (result i32) (call $dummy) (i32.const 13))) + ) + (func (export "as-binary-operand") (result i32) + (i32.mul + (block (result i32) (call $dummy) (i32.const 3)) + (block (result i32) (call $dummy) (i32.const 4)) + ) + ) + (func (export "as-test-operand") (result i32) + (i32.eqz (block (result i32) (call $dummy) (i32.const 13))) + ) + (func (export "as-compare-operand") (result i32) + (f32.gt + (block (result f32) (call $dummy) (f32.const 3)) + (block (result f32) (call $dummy) (f32.const 3)) + ) + ) + + (func (export "break-bare") (result i32) + (block (br 0) (unreachable)) + (block (br_if 0 (i32.const 1)) (unreachable)) + (block (br_table 0 (i32.const 0)) (unreachable)) + (block (br_table 0 0 0 (i32.const 1)) (unreachable)) + (i32.const 19) + ) + (func (export "break-value") (result i32) + (block (result i32) (br 0 (i32.const 18)) (i32.const 19)) + ) + (func (export "break-repeated") (result i32) + (block (result i32) + (br 0 (i32.const 18)) + (br 0 (i32.const 19)) + (drop (br_if 0 (i32.const 20) (i32.const 0))) + (drop (br_if 0 (i32.const 20) (i32.const 1))) + (br 0 (i32.const 21)) + (br_table 0 (i32.const 22) (i32.const 4)) + (br_table 0 0 0 (i32.const 23) (i32.const 1)) + (i32.const 21) + ) + ) + (func (export "break-inner") (result i32) + (local i32) + (set_local 0 (i32.const 0)) + (set_local 0 (i32.add (get_local 0) (block (result i32) (block (result i32) (br 1 (i32.const 0x1)))))) + (set_local 0 (i32.add (get_local 0) (block (result i32) (block (br 0)) (i32.const 0x2)))) + (set_local 0 + (i32.add (get_local 0) (block (result i32) (i32.ctz (br 0 (i32.const 0x4))))) + ) + (set_local 0 + (i32.add (get_local 0) (block (result i32) (i32.ctz (block (result i32) (br 1 (i32.const 0x8)))))) + ) + (get_local 0) + ) + + (func (export "effects") (result i32) + (local i32) + (block + (set_local 0 (i32.const 1)) + (set_local 0 (i32.mul (get_local 0) (i32.const 3))) + (set_local 0 (i32.sub (get_local 0) (i32.const 5))) + (set_local 0 (i32.mul (get_local 0) (i32.const 7))) + (br 0) + (set_local 0 (i32.mul (get_local 0) (i32.const 100))) + ) + (i32.eq (get_local 0) (i32.const -14)) + ) +) \ No newline at end of file diff --git a/tests/samples/if.wast b/tests/samples/if.wast new file mode 100644 index 0000000..b827c5e --- /dev/null +++ b/tests/samples/if.wast @@ -0,0 +1,132 @@ +;; Test `if` operator + +(module + ;; Auxiliary definition + (func $dummy) + + (func (export "empty") (param i32) + (if (get_local 0) (then)) + (if (get_local 0) (then) (else)) + (if $l (get_local 0) (then)) + (if $l (get_local 0) (then) (else)) + ) + + (func (export "singular") (param i32) (result i32) + (if (get_local 0) (then (nop))) + (if (get_local 0) (then (nop)) (else (nop))) + (if (result i32) (get_local 0) (then (i32.const 7)) (else (i32.const 8))) + ) + + (func (export "multi") (param i32) (result i32) + (if (get_local 0) (then (call $dummy) (call $dummy) (call $dummy))) + (if (get_local 0) (then) (else (call $dummy) (call $dummy) (call $dummy))) + (if (result i32) (get_local 0) + (then (call $dummy) (call $dummy) (i32.const 8)) + (else (call $dummy) (call $dummy) (i32.const 9)) + ) + ) + + (func (export "nested") (param i32 i32) (result i32) + (if (result i32) (get_local 0) + (then + (if (get_local 1) (then (call $dummy) (block) (nop))) + (if (get_local 1) (then) (else (call $dummy) (block) (nop))) + (if (result i32) (get_local 1) + (then (call $dummy) (i32.const 9)) + (else (call $dummy) (i32.const 10)) + ) + ) + (else + (if (get_local 1) (then (call $dummy) (block) (nop))) + (if (get_local 1) (then) (else (call $dummy) (block) (nop))) + (if (result i32) (get_local 1) + (then (call $dummy) (i32.const 10)) + (else (call $dummy) (i32.const 11)) + ) + ) + ) + ) + + (func (export "as-unary-operand") (param i32) (result i32) + (i32.ctz + (if (result i32) (get_local 0) + (then (call $dummy) (i32.const 13)) + (else (call $dummy) (i32.const -13)) + ) + ) + ) + (func (export "as-binary-operand") (param i32 i32) (result i32) + (i32.mul + (if (result i32) (get_local 0) + (then (call $dummy) (i32.const 3)) + (else (call $dummy) (i32.const -3)) + ) + (if (result i32) (get_local 1) + (then (call $dummy) (i32.const 4)) + (else (call $dummy) (i32.const -5)) + ) + ) + ) + (func (export "as-test-operand") (param i32) (result i32) + (i32.eqz + (if (result i32) (get_local 0) + (then (call $dummy) (i32.const 13)) + (else (call $dummy) (i32.const 0)) + ) + ) + ) + (func (export "as-compare-operand") (param i32 i32) (result i32) + (f32.gt + (if (result f32) (get_local 0) + (then (call $dummy) (f32.const 3)) + (else (call $dummy) (f32.const -3)) + ) + (if (result f32) (get_local 1) + (then (call $dummy) (f32.const 4)) + (else (call $dummy) (f32.const -4)) + ) + ) + ) + + (func (export "break-bare") (result i32) + (if (i32.const 1) (then (br 0) (unreachable))) + (if (i32.const 1) (then (br 0) (unreachable)) (else (unreachable))) + (if (i32.const 0) (then (unreachable)) (else (br 0) (unreachable))) + (if (i32.const 1) (then (br_if 0 (i32.const 1)) (unreachable))) + (if (i32.const 1) (then (br_if 0 (i32.const 1)) (unreachable)) (else (unreachable))) + (if (i32.const 0) (then (unreachable)) (else (br_if 0 (i32.const 1)) (unreachable))) + (if (i32.const 1) (then (br_table 0 (i32.const 0)) (unreachable))) + (if (i32.const 1) (then (br_table 0 (i32.const 0)) (unreachable)) (else (unreachable))) + (if (i32.const 0) (then (unreachable)) (else (br_table 0 (i32.const 0)) (unreachable))) + (i32.const 19) + ) + + (func (export "break-value") (param i32) (result i32) + (if (result i32) (get_local 0) + (then (br 0 (i32.const 18)) (i32.const 19)) + (else (br 0 (i32.const 21)) (i32.const 20)) + ) + ) + + (func (export "effects") (param i32) (result i32) + (local i32) + (if + (block (result i32) (set_local 1 (i32.const 1)) (get_local 0)) + (then + (set_local 1 (i32.mul (get_local 1) (i32.const 3))) + (set_local 1 (i32.sub (get_local 1) (i32.const 5))) + (set_local 1 (i32.mul (get_local 1) (i32.const 7))) + (br 0) + (set_local 1 (i32.mul (get_local 1) (i32.const 100))) + ) + (else + (set_local 1 (i32.mul (get_local 1) (i32.const 5))) + (set_local 1 (i32.sub (get_local 1) (i32.const 7))) + (set_local 1 (i32.mul (get_local 1) (i32.const 3))) + (br 0) + (set_local 1 (i32.mul (get_local 1) (i32.const 1000))) + ) + ) + (get_local 1) + ) +) \ No newline at end of file diff --git a/tests/samples/import.wast b/tests/samples/import.wast new file mode 100644 index 0000000..ebc0392 --- /dev/null +++ b/tests/samples/import.wast @@ -0,0 +1,67 @@ +(module + (type $func_i32 (func (param i32))) + (type $func_i64 (func (param i64))) + (type $func_f32 (func (param f32))) + (type $func_f64 (func (param f64))) + + (import "spectest" "print" (func (param i32))) + ;; JavaScript can't handle i64 yet. + ;; (func (import "spectest" "print") (param i64)) + (import "spectest" "print" (func $print_i32 (param i32))) + ;; JavaScript can't handle i64 yet. + ;; (import "spectest" "print" (func $print_i64 (param i64))) + (import "spectest" "print" (func $print_f32 (param f32))) + (import "spectest" "print" (func $print_f64 (param f64))) + (import "spectest" "print" (func $print_i32_f32 (param i32 f32))) + (import "spectest" "print" (func $print_f64_f64 (param f64 f64))) + (func $print_i32-2 (import "spectest" "print") (param i32)) + (func $print_f64-2 (import "spectest" "print") (param f64)) + (import "test" "func-i64->i64" (func $i64->i64 (param i64) (result i64))) + + (func (export "p1") (import "spectest" "print") (param i32)) + (func $p (export "p2") (import "spectest" "print") (param i32)) + (func (export "p3") (export "p4") (import "spectest" "print") (param i32)) + (func (export "p5") (import "spectest" "print") (type 0)) + (func (export "p6") (import "spectest" "print") (type 0) (param i32) (result)) + + (import "spectest" "print" (func (type $forward))) + (func (import "spectest" "print") (type $forward)) + (type $forward (func (param i32))) + + (table anyfunc (elem $print_i32 $print_f64)) + (table (export "my-table") anyfunc (elem $print_i32 $print_f64)) + (table (export "my-table") (export "my-table2") anyfunc (elem $print_i32 $print_f64)) + (table (import "external-mod" "external-table") 0 anyfunc) + (table (export "my-table") (import "external-mod" "external-table") 0 anyfunc) + (table (export "my-table") (export "my-table2") (import "external-mod" "external-table") 0 anyfunc) + + (func (export "print32") (param $i i32) + (local $x f32) + (set_local $x (f32.convert_s/i32 (get_local $i))) + (call 0 (get_local $i)) + (call $print_i32_f32 + (i32.add (get_local $i) (i32.const 1)) + (f32.const 42) + ) + (call $print_i32 (get_local $i)) + (call $print_i32-2 (get_local $i)) + (call $print_f32 (get_local $x)) + (call_indirect (type $func_i32) (get_local $i) (i32.const 0)) + ) + + (func (export "print64") (param $i i64) + (local $x f64) + (set_local $x (f64.convert_s/i64 (call $i64->i64 (get_local $i)))) + ;; JavaScript can't handle i64 yet. + ;; (call 1 (get_local $i)) + (call $print_f64_f64 + (f64.add (get_local $x) (f64.const 1)) + (f64.const 53) + ) + ;; JavaScript can't handle i64 yet. + ;; (call $print_i64 (get_local $i)) + (call $print_f64 (get_local $x)) + (call $print_f64-2 (get_local $x)) + (call_indirect (type $func_f64) (get_local $x) (i32.const 1)) + ) +) \ No newline at end of file diff --git a/tests/samples/loop.wast b/tests/samples/loop.wast new file mode 100644 index 0000000..3cb586d --- /dev/null +++ b/tests/samples/loop.wast @@ -0,0 +1,202 @@ +;; Test `loop` opcode + +(module + (func $dummy) + + (func (export "empty") + (loop) + (loop $l) + ) + + (func (export "singular") (result i32) + (loop (nop)) + (loop (result i32) (i32.const 7)) + ) + + (func (export "multi") (result i32) + (loop (call $dummy) (call $dummy) (call $dummy) (call $dummy)) + (loop (result i32) (call $dummy) (call $dummy) (call $dummy) (i32.const 8)) + ) + + (func (export "nested") (result i32) + (loop (result i32) + (loop (call $dummy) (block) (nop)) + (loop (result i32) (call $dummy) (i32.const 9)) + ) + ) + + (func (export "deep") (result i32) + (loop (result i32) (block (result i32) + (loop (result i32) (block (result i32) + (loop (result i32) (block (result i32) + (loop (result i32) (block (result i32) + (loop (result i32) (block (result i32) + (loop (result i32) (block (result i32) + (loop (result i32) (block (result i32) + (loop (result i32) (block (result i32) + (loop (result i32) (block (result i32) + (loop (result i32) (block (result i32) + (loop (result i32) (block (result i32) + (loop (result i32) (block (result i32) + (loop (result i32) (block (result i32) + (loop (result i32) (block (result i32) + (loop (result i32) (block (result i32) + (loop (result i32) (block (result i32) + (loop (result i32) (block (result i32) + (loop (result i32) (block (result i32) + (loop (result i32) (block (result i32) + (loop (result i32) (block (result i32) + (call $dummy) (i32.const 150) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + )) + ) + + (func (export "as-unary-operand") (result i32) + (i32.ctz (loop (result i32) (call $dummy) (i32.const 13))) + ) + (func (export "as-binary-operand") (result i32) + (i32.mul + (loop (result i32) (call $dummy) (i32.const 3)) + (loop (result i32) (call $dummy) (i32.const 4)) + ) + ) + (func (export "as-test-operand") (result i32) + (i32.eqz (loop (result i32) (call $dummy) (i32.const 13))) + ) + (func (export "as-compare-operand") (result i32) + (f32.gt + (loop (result f32) (call $dummy) (f32.const 3)) + (loop (result f32) (call $dummy) (f32.const 3)) + ) + ) + + (func (export "break-bare") (result i32) + (block (loop (br 1) (br 0) (unreachable))) + (block (loop (br_if 1 (i32.const 1)) (unreachable))) + (block (loop (br_table 1 (i32.const 0)) (unreachable))) + (block (loop (br_table 1 1 1 (i32.const 1)) (unreachable))) + (i32.const 19) + ) + (func (export "break-value") (result i32) + (block (result i32) + (loop (result i32) (br 1 (i32.const 18)) (br 0) (i32.const 19)) + ) + ) + (func (export "break-repeated") (result i32) + (block (result i32) + (loop (result i32) + (br 1 (i32.const 18)) + (br 1 (i32.const 19)) + (drop (br_if 1 (i32.const 20) (i32.const 0))) + (drop (br_if 1 (i32.const 20) (i32.const 1))) + (br 1 (i32.const 21)) + (br_table 1 (i32.const 22) (i32.const 0)) + (br_table 1 1 1 (i32.const 23) (i32.const 1)) + (i32.const 21) + ) + ) + ) + (func (export "break-inner") (result i32) + (local i32) + (set_local 0 (i32.const 0)) + (set_local 0 (i32.add (get_local 0) (block (result i32) (loop (result i32) (block (result i32) (br 2 (i32.const 0x1))))))) + (set_local 0 (i32.add (get_local 0) (block (result i32) (loop (result i32) (loop (result i32) (br 2 (i32.const 0x2))))))) + (set_local 0 (i32.add (get_local 0) (block (result i32) (loop (result i32) (block (result i32) (loop (result i32) (br 1 (i32.const 0x4)))))))) + (set_local 0 (i32.add (get_local 0) (block (result i32) (loop (result i32) (i32.ctz (br 1 (i32.const 0x8))))))) + (set_local 0 (i32.add (get_local 0) (block (result i32) (loop (result i32) (i32.ctz (loop (result i32) (br 2 (i32.const 0x10)))))))) + (get_local 0) + ) + (func (export "cont-inner") (result i32) + (local i32) + (set_local 0 (i32.const 0)) + (set_local 0 (i32.add (get_local 0) (loop (result i32) (loop (result i32) (br 1))))) + (set_local 0 (i32.add (get_local 0) (loop (result i32) (i32.ctz (br 0))))) + (set_local 0 (i32.add (get_local 0) (loop (result i32) (i32.ctz (loop (result i32) (br 1)))))) + (get_local 0) + ) + + (func $fx (export "effects") (result i32) + (local i32) + (block + (loop + (set_local 0 (i32.const 1)) + (set_local 0 (i32.mul (get_local 0) (i32.const 3))) + (set_local 0 (i32.sub (get_local 0) (i32.const 5))) + (set_local 0 (i32.mul (get_local 0) (i32.const 7))) + (br 1) + (set_local 0 (i32.mul (get_local 0) (i32.const 100))) + ) + ) + (i32.eq (get_local 0) (i32.const -14)) + ) + + (func (export "while") (param i64) (result i64) + (local i64) + (set_local 1 (i64.const 1)) + (block + (loop + (br_if 1 (i64.eqz (get_local 0))) + (set_local 1 (i64.mul (get_local 0) (get_local 1))) + (set_local 0 (i64.sub (get_local 0) (i64.const 1))) + (br 0) + ) + ) + (get_local 1) + ) + + (func (export "for") (param i64) (result i64) + (local i64 i64) + (set_local 1 (i64.const 1)) + (set_local 2 (i64.const 2)) + (block + (loop + (br_if 1 (i64.gt_u (get_local 2) (get_local 0))) + (set_local 1 (i64.mul (get_local 1) (get_local 2))) + (set_local 2 (i64.add (get_local 2) (i64.const 1))) + (br 0) + ) + ) + (get_local 1) + ) + + (func (export "nesting") (param f32 f32) (result f32) + (local f32 f32) + (block + (loop + (br_if 1 (f32.eq (get_local 0) (f32.const 0))) + (set_local 2 (get_local 1)) + (block + (loop + (br_if 1 (f32.eq (get_local 2) (f32.const 0))) + (br_if 3 (f32.lt (get_local 2) (f32.const 0))) + (set_local 3 (f32.add (get_local 3) (get_local 2))) + (set_local 2 (f32.sub (get_local 2) (f32.const 2))) + (br 0) + ) + ) + (set_local 3 (f32.div (get_local 3) (get_local 0))) + (set_local 0 (f32.sub (get_local 0) (f32.const 1))) + (br 0) + ) + ) + (get_local 3) + ) +) \ No newline at end of file