dashbitco / nimble_parsec

A simple and fast library for text-based parser combinators

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

`repeat_while` passing the wrong `context` in nested context

Kabie opened this issue · comments

commented

An example:

Mix.install([{:nimble_parsec, "~> 1.2"}])

defmodule Bug do
  import NimbleParsec

  def n_times(_, context, _, _) do
    context
    |> IO.inspect()

    if context.count > 0 do
      {:cont, %{context | count: context.count - 1}}
    else
      {:halt, context}
    end
  end

  # If remove `repeat` below, it works fine
  value = repeat(ascii_char([?0..?9]))

  key = ascii_char([?a..?z])

  segment = key |> concat(value)

  defparsec :parse, repeat_while(segment, {:n_times, []}), debug: true
end

Bug.parse("a1b2c3d4e5f6g7h8", context: %{count: 3})

The output:

defp parse__0(rest, acc, stack, context, line, offset) do
  case n_times(rest, context, line, offset) do
  {:cont, context} ->
    parse__2(rest, [], [{rest, acc, context, line, offset} | stack], context, line, offset)

  {:halt, context} ->
    parse__6(rest, acc, stack, context, line, offset)
end
end

defp parse__2(<<x0, rest::binary>>, acc, stack, context, comb__line, comb__offset) when x0 >= 97 and x0 <= 122 do
  parse__3(rest, [x0] ++ acc, stack, context, comb__line, comb__offset + 1)
end

defp parse__2(rest, acc, stack, context, line, offset) do
  parse__1(rest, acc, stack, context, line, offset)
end

defp parse__3(<<x0, rest::binary>>, acc, stack, context, comb__line, comb__offset) when x0 >= 48 and x0 <= 57 do
  parse__5(rest, [x0] ++ acc, stack, context, comb__line, comb__offset + 1)
end

defp parse__3(rest, acc, stack, context, line, offset) do
  parse__4(rest, acc, stack, context, line, offset)
end

defp parse__5(rest, acc, stack, context, line, offset) do
  parse__3(rest, acc, stack, context, line, offset)
end

defp parse__1(_, _, [{rest, acc, context, line, offset} | stack], _, _, _) do
  parse__6(rest, acc, stack, context, line, offset)
end

defp parse__4(
  inner_rest,
  inner_acc,
  [{rest, acc, context, line, offset} | stack],
  inner_context,
  inner_line,
  inner_offset
) do
  case n_times(rest, context, line, offset) do
  {:cont, context} ->
    parse__2(
      inner_rest,
      [],
      [{inner_rest, inner_acc ++ acc, inner_context, inner_line, inner_offset} | stack],
      inner_context,
      inner_line,
      inner_offset
    )

  {:halt, context} ->
    parse__6(rest, acc, stack, context, line, offset)
end
end

defp parse__6(rest, acc, _stack, context, line, offset) do
  {:ok, acc, rest, context, line, offset}
end


%{count: 3}
%{count: 2}
%{count: 2}
%{count: 2}
%{count: 2}
%{count: 2}
%{count: 2}
%{count: 2}
%{count: 2}

You can see in defp parse__4, it passes inner_context instead of the returned context to next iteration.