AQQ7DH2OU4OKCVRQECS427ICA4OAT5NLQBE4TLKFBJ3TAUMQHXKAC
QOY4V3F5WXC4HJ3EP5F4CQ5NYJ6QJF6REKTME3RQS7AQT3RMXZ7AC
O2DTNJ3ZOJJKDHGPNE2DZSZF7CFU4G5D65RIDOYZHYYBH3JZAAIAC
YNPPEQEWMXS4B2EKRR54WUZMXO222YOWC2I5NPXPDG2ABOIBH7RAC
ZR7U7TRCUHXMBXQFA3XXNGJPPYFACMOWPQKGUH7E22KGFM35HSNAC
7VZF66GPW6DKQB4FU4PTU3CEE32MTYNP4MH6GY4SMICRERO76WDQC
Y7IBP4INTUL42R7TC7O4DXGR27CKTD6VEEL6BHJNGCVIFVUVGCMQC
Q62EXKAKNM5FMIW3UZBUAJFKOZLE2O2VIRTJAVDOTTSWX5JSMX4QC
XMQU7NVKDFNL5MN2BECGAQI4LXQQX3GCIESGJM54GYDDLBVODLWAC
UVNP57VT7S7OSGWK5XXKEBAVZMHDGGPMFGSSCT25GGSK5QFYNMFQC
def new() do
%__MODULE__{
parsers: [
s(:magic, bytes(<<0x00, 0x61, 0x73, 0x6D>>)),
s(:version, bytes(<<0x01, 0x00, 0x00, 0x00>>)),
s(
:type_section,
section([
s(:id, bytes(<<0x01>>)),
&u32/1,
s(:content, &get_bytes/1)
])
)
]
}
def parse(<<0x00, 0x61, 0x73, 0x6D, rest::binary>>) do
{:ok, {{:magic, <<0x00, 0x61, 0x73, 0x6D>>}, rest, &parse_version/1}}
def parse_one(chunk, state = %__MODULE__{parsers: [parser | rest_parsers]}) do
results = parser.(chunk)
new_state = %{state | parsers: rest_parsers}
for result <- results, reduce: {new_state, chunk} do
{state, chunk} ->
case result do
{:add_tag, tag} ->
{%{state | tags: [tag | state.tags]}, chunk}
{:cont, cont} ->
{%{state | parsers: [cont | state.parsers]}, chunk}
{:add_acc, result, tail} ->
{%{state | acc: [result | state.acc]}, tail}
{:add_acc, result} ->
{%{state | acc: [result | state.acc]}, chunk}
:keep_result ->
[last | acc] = state.acc
{new_acc, new_tags} =
case state.tags do
[tag] -> {[{tag, last} | acc], []}
[_tag | rest_tags] = tags -> {[{tags |> Enum.reverse(), last} | acc], rest_tags}
end
{%{state | tags: new_tags, acc: new_acc}, chunk}
{:pop_and_cont, cont} ->
[top | acc] = state.acc
cont = cont.(top)
{%{state | parsers: [cont | state.parsers], acc: acc}, chunk}
{:error, err} ->
{%{state | stop: true, acc: [{:error, err} | state.acc]}, chunk}
end
defp do_parse_section(id) do
fn chunk ->
do_parse_section_len(id, chunk)
def parse_all(chunk, state) do
{state, tail} = parse_one(chunk, state)
if state.stop do
{state, tail}
else
parse_all(tail, state)
end
defp do_parse_section_len(id, <<0::1, _::bitstring>> = chunk) do
<<len::binary-size(1), rest::binary>> = chunk
length = WaParser.LEB128.decode_unsigned(len)
{:ok, {{:section_length, length}, rest, parse_section_body(id, length)}}
def get_results({state, chunk}) do
{state.acc |> Enum.reverse(), state, chunk}
end
defp s(name, cont) do
fn _chunk ->
[
{:add_tag, name},
{:cont, &keep_result/1},
{:cont, cont}
]
end
end
defp keep_result(_chunk) do
[
:keep_result
]
end
def bytes(expected) do
expected_size = :erlang.size(expected)
defp parse_section_body(id, length) do
case chunk do
<<^expected::binary-size(expected_size), rest::binary>> = ^chunk ->
[
{:add_acc, expected, rest}
]
_ ->
[
{:error, :not_matched}
]
end
do_parse_section_body(id, length, chunk)
def section([]) do
fn _chunk ->
[
{:add_acc, :section_end}
]
end
end
def section(parts) when is_list(parts) do
[next | rest] = parts
fn _chunk ->
[
{:cont, section(rest)},
{:cont, next}
]
end
defp do_parse_section_body(_id, len, chunk) do
<<section::binary-size(len), rest::binary>> = chunk
{:ok, {{:section_body, section}, rest, &parse_section/1}}
defp read_bytes(count) do
fn <<bytes::binary-size(count), rest::binary>> ->
[
{:add_acc, bytes, rest}
]
defp parse_reducer(data, parser) do
case parser.(data) do
{:ok, {result, rest, next}} ->
IO.inspect(result)
parse_reducer(rest, next)