combine v0.10.0 Combine.Helpers View Source

Helpers for building custom parsers.

Link to this section Summary

Functions

Macro helper for building a custom parser

Link to this section Functions

Link to this macro defparser(call, list) View Source (macro)

Macro helper for building a custom parser.

A custom parser validates the next input against some rules. If the validation succeeds, the parser should:

  • add one term to the result
  • update the position
  • remove the parsed part from the input

Otherwise, the parser should return a corresponding error message.

For example, let’s take a look at the implementation of Combine.Parsers.Text.string/2, which matches a required string and outputs it:

defparser string(%ParserState{status: :ok, line: line, column: col, input: input, results: results} = state, expected)
  when is_binary(expected)
do
  byte_size = :erlang.size(expected)
  case input do
    <<^expected::binary-size(byte_size), rest::binary>> ->
      # string has been matched -> add the term, and update the position
      new_col = col + byte_size
      %{state | :column => new_col, :input => rest, :results => [expected|results]}

    _ ->
      # no match -> report an error
      %{state | :status => :error, :error => "Expected `#{expected}`, but was not found at line #{line}, column #{col}."}
  end
end

The macro above will generate a function which takes two arguments. The first argument (parser state) can be omitted (i.e. you can use the macro as string(expected_string)). In this case, you’re just creating a basic parser specification.

However, you can also chain parsers by providing the first argument:

parser1()
|> string(expected_string)

In this example, the state produced by parser1 is used when invoking the string parser. In other words, string parser parses the remaining output. On success, the final result will contain terms emitted by both parsers.

Note: if your parser doesn’t output exactly one term it might not work properly with other parsers which rely on this property, especially those from Combine.Parsers.Base. As a rule, try to always output exactly one term. If you need to produce more terms, you can group them in a list, a tuple, or a map. If you don’t want to produce anything, you can produce the atom :__ignore, which will be later removed from the output.