Skip to content

Commit

Permalink
Added parsing of Block, Loop and If/Else instructions
Browse files Browse the repository at this point in the history
  • Loading branch information
BrendoCosta committed Sep 28, 2024
1 parent 95f6868 commit c27f8f3
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 2 deletions.
62 changes: 62 additions & 0 deletions src/gwr/parser/instruction_parser.gleam
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import gleam/option
import gleam/bit_array
import gleam/bool
import gleam/int
Expand Down Expand Up @@ -28,6 +29,45 @@ pub fn parse_instruction(from reader: byte_reader.ByteReader) -> Result(#(byte_r
// https://webassembly.github.io/spec/core/binary/instructions.html#control-instructions
0x00 -> Ok(#(reader, instruction.Unreachable))
0x01 -> Ok(#(reader, instruction.NoOp))
0x02 ->
{
use #(reader, block_type) <- result.try(parse_block_type(from: reader))
use #(reader, expression) <- result.try(parse_expression(from: reader))
Ok(#(reader, instruction.Block(block_type: block_type, instructions: expression)))
}
0x03 ->
{
use #(reader, block_type) <- result.try(parse_block_type(from: reader))
use #(reader, expression) <- result.try(parse_expression(from: reader))
Ok(#(reader, instruction.Loop(block_type: block_type, instructions: expression)))
}
0x04 ->
{
use #(reader, block_type) <- result.try(parse_block_type(from: reader))
use #(reader, body) <- result.try(
parse_instructions_until(
from: reader,
with: fn (inst) {
case inst
{
instruction.End -> True
instruction.Else(_) -> True
_ -> False
}
})
)
case list.last(body)
{
Ok(instruction.End) -> Ok(#(reader, instruction.If(block_type: block_type, instructions: body, else_: option.None)))
Ok(instruction.Else(_) as els) -> Ok(#(reader, instruction.If(block_type: block_type, instructions: list.take(from: body, up_to: list.length(body) - 1), else_: option.Some(els))))
_ -> Error("gwr/parser/instruction_parser.parse_instruction: expected the If instruction's block to end either with an End instruction or an Else instruction")
}
}
0x05 ->
{
use #(reader, expression) <- result.try(parse_expression(from: reader))
Ok(#(reader, instruction.Else(instructions: expression)))
}
// Variable Instructions
// https://webassembly.github.io/spec/core/binary/instructions.html#variable-instructions
0x20 ->
Expand Down Expand Up @@ -82,6 +122,28 @@ pub fn parse_expression(from reader: byte_reader.ByteReader) -> Result(#(byte_re
Ok(#(reader, expression))
}

pub fn do_parse_instructions_until(reader: byte_reader.ByteReader, predicate: fn (instruction.Instruction) -> Bool, accumulator: List(instruction.Instruction)) -> Result(#(byte_reader.ByteReader, List(instruction.Instruction)), String)
{
case byte_reader.can_read(reader)
{
True ->
{
use #(reader, instruction) <- result.try(parse_instruction(from: reader))
case predicate(instruction)
{
True -> Ok(#(reader, list.append(accumulator, [instruction])))
False -> do_parse_instructions_until(reader, predicate, list.append(accumulator, [instruction]))
}
}
False -> Error("gwr/parser/instruction_parser.do_parse_instructions_until: reached the end of the data yet couldn't find the instruction matching the given predicate")
}
}

pub fn parse_instructions_until(from reader: byte_reader.ByteReader, with predicate: fn (instruction.Instruction) -> Bool) -> Result(#(byte_reader.ByteReader, List(instruction.Instruction)), String)
{
do_parse_instructions_until(reader, predicate, [])
}

pub fn parse_block_type(from reader: byte_reader.ByteReader) -> Result(#(byte_reader.ByteReader, instruction.BlockType), String)
{
//use #(reader, flag) <- result.try(byte_reader.read(from: reader, take: 1))
Expand Down
6 changes: 6 additions & 0 deletions src/gwr/syntax/instruction.gleam
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import gleam/option.{type Option}

import gwr/syntax/index
import gwr/syntax/types

Expand All @@ -14,6 +16,10 @@ pub type Instruction
/// https://webassembly.github.io/spec/core/binary/instructions.html#control-instructions
Unreachable
NoOp
Block(block_type: BlockType, instructions: List(Instruction))
Loop(block_type: BlockType, instructions: List(Instruction))
If(block_type: BlockType, instructions: List(Instruction), else_: Option(Instruction))
Else(instructions: Expression)
/// https://webassembly.github.io/spec/core/binary/instructions.html#variable-instructions
LocalGet(index: index.LocalIndex)
/// https://webassembly.github.io/spec/core/binary/instructions.html#numeric-instructions
Expand Down
91 changes: 89 additions & 2 deletions test/gwr/parser/instruction_parser_test.gleam
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import gleam/option
import gwr/syntax/types
import gwr/parser/instruction_parser
import gwr/parser/byte_reader
Expand Down Expand Up @@ -50,7 +51,93 @@ pub fn parse_block_type___type_index_block___test()
)
}

pub fn i32_const_test()
pub fn parse_instruction___block___test()
{
let reader = byte_reader.create(from: <<0x02, 0x01, 0x41, 0x80, 0x80, 0xc0, 0x00, 0x41, 0x2, 0x0b>>)
instruction_parser.parse_instruction(reader)
|> should.be_ok
|> should.equal(
#(
byte_reader.ByteReader(..reader, current_position: 10),
instruction.Block(
block_type: instruction.TypeIndexBlock(index: 1),
instructions: [
instruction.I32Const(value: 1048576),
instruction.I32Const(value: 2),
instruction.End
]
)
)
)
}

pub fn parse_instruction___loop___test()
{
let reader = byte_reader.create(from: <<0x03, 0x7f, 0x41, 0x08, 0x41, 0x80, 0x80, 0x04, 0x0b>>)
instruction_parser.parse_instruction(reader)
|> should.be_ok
|> should.equal(
#(
byte_reader.ByteReader(..reader, current_position: 9),
instruction.Loop(
block_type: instruction.ValueTypeBlock(type_: types.Number(types.Integer32)),
instructions: [
instruction.I32Const(value: 8),
instruction.I32Const(value: 65536),
instruction.End
]
)
)
)
}

pub fn parse_instruction___if___test()
{
let reader = byte_reader.create(from: <<0x04, 0x7f, 0x41, 0x80, 0x80, 0x04, 0x0b>>)
instruction_parser.parse_instruction(reader)
|> should.be_ok
|> should.equal(
#(
byte_reader.ByteReader(..reader, current_position: 7),
instruction.If(
block_type: instruction.ValueTypeBlock(type_: types.Number(types.Integer32)),
instructions: [
instruction.I32Const(value: 65536),
instruction.End
],
else_: option.None
)
)
)
}

pub fn parse_instruction___if_else___test()
{
let reader = byte_reader.create(from: <<0x04, 0x7f, 0x41, 0x80, 0x80, 0x04, 0x05, 0x41, 0x02, 0x0b>>)
instruction_parser.parse_instruction(reader)
|> should.be_ok
|> should.equal(
#(
byte_reader.ByteReader(..reader, current_position: 10),
instruction.If(
block_type: instruction.ValueTypeBlock(type_: types.Number(types.Integer32)),
instructions: [
instruction.I32Const(value: 65536),
],
else_: option.Some(
instruction.Else(
instructions: [
instruction.I32Const(value: 2),
instruction.End
]
)
)
)
)
)
}

pub fn parse_instruction___i32_const___test()
{
let reader = byte_reader.create(from: <<0x41, 0x80, 0x80, 0xc0, 0x00>>)
instruction_parser.parse_instruction(reader)
Expand All @@ -63,7 +150,7 @@ pub fn i32_const_test()
)
}

pub fn local_get_test()
pub fn parse_instruction___local_get___test()
{
let reader = byte_reader.create(from: <<0x20, 0xff, 0x01>>)
instruction_parser.parse_instruction(reader)
Expand Down

0 comments on commit c27f8f3

Please sign in to comment.