Ergo.Combinators (ergo v0.3.5)

Ergo.Combinators is the key set of parsers used for combining together other parsers.

Parsers

  • choice
  • sequence
  • many
  • optional
  • ignore
  • transform
  • lookeahead
  • not_lookahead

Link to this section Summary

Functions

The choice/1 parser takes a list of parsers. It tries each in order attempting to match one. Once a match has been made choice returns the result of the matching parser.

The ignore/1 parser matches but ignores the AST of its child parser.

The lookahead parser accepts a parser and matches it but does not update the context when it succeeds.

Examples

iex> Logger.disable(self())
iex> alias Ergo.Context
iex> import Ergo.{Combinators, Terminals}
iex> parser = many(wc(), label: "Chars")
iex> context = Ergo.parse(parser, "Hello World", debug: true)
iex> assert %Context{status: :ok, debug: true, ast: [?H, ?e, ?l, ?l, ?o], input: " World", index: 5, col: 6, char: ?o} = context

iex> alias Ergo.Context
iex> import Ergo.{Combinators, Terminals}
iex> parser = many(wc(), min: 6)
iex> context = Ergo.parse(parser, "Hello World")
iex> assert %Context{status: {:error, :many_less_than_min}, ast: nil, input: " World", char: ?o, index: 5, col: 6} = context

iex> alias Ergo.{Context, Parser}
iex> import Ergo.{Combinators, Terminals}
iex> parser = many(wc(), max: 3)
iex> context = Ergo.parse(parser, "Hello World")
iex> assert %Context{status: :ok, ast: [?H, ?e, ?l], input: "lo World", char: ?l, index: 3, col: 4} = context

iex> alias Ergo.{Context, Parser}
iex> import Ergo.{Combinators, Terminals}
iex> parser = many(wc(), map: &Enum.count/1)
iex> context = Ergo.parse(parser, "Hello World")
iex> %Context{status: :ok, ast: 5, input: " World", index: 5, col: 6, char: ?o} = context

The not_lookahead parser accepts a parser and attempts to match it. If the match fails the not_lookahead parser returns status: :ok but does not affect the context otherwise.

Examples

iex> alias Ergo.Context
iex> import Ergo.{Terminals, Combinators}
iex> context = Ergo.parse(optional(literal("Hello")), "Hello World")
iex> assert %Context{status: :ok, ast: "Hello", input: " World", index: 5, col: 6, char: ?o} = context

In this example we deliberately ensure that the Context ast is not nil
iex> alias Ergo.{Context, Parser}
iex> import Ergo.{Terminals, Combinators}
iex> context = %{Context.new(" World") | ast: []}
iex> parser = optional(literal("Hello"))
iex> new_context = Parser.call(parser, context)
iex> assert %Context{status: :ok, ast: nil, input: " World", index: 0, col: 1, char: 0} = new_context

Examples

iex> alias Ergo.Context
iex> import Ergo.{Terminals, Combinators}
iex> parser = sequence([literal("Hello"), ws(), literal("World")])
iex> context = Ergo.parse(parser, "Hello World")
%Context{status: :ok, ast: ["Hello", ?\s, "World"], char: ?d, index: 11, line: 1, col: 12} = context

iex> Logger.disable(self())
iex> alias Ergo.Context
iex> import Ergo.{Terminals, Combinators}
iex> parser = sequence([literal("Hello"), ws(), literal("World")], label: "HelloWorld")
iex> context = Ergo.parse(parser, "Hello World", debug: true)
iex> assert %Context{status: :ok, debug: true, ast: ["Hello", ?\s, "World"], char: ?d, index: 11, line: 1, col: 12} = context

iex> alias Ergo.Context
iex> import Ergo.{Terminals, Combinators}
iex> parser = sequence([literal("Hello"), ws(), literal("World")], map: fn ast -> Enum.join(ast, " ") end)
iex> context = Ergo.parse(parser, "Hello World")
iex> assert %Context{status: :ok, ast: "Hello 32 World", char: ?d, index: 11, line: 1, col: 12} = context

iex> Logger.disable(self())
iex> alias Ergo.Context
iex> import Ergo.{Terminals, Combinators}
iex> parser = sequence([literal("Hello"), ws(), literal("World")], label: "HelloWorld", map: fn ast -> Enum.join(ast, " ") end)
iex> context = Ergo.parse(parser, "Hello World", debug: true)
iex> assert %Context{status: :ok, debug: true, ast: "Hello 32 World", char: ?d, index: 11, line: 1, col: 12} = context

iex> alias Ergo.Context
iex> import Ergo.{Combinators, Terminals}
iex> parser = sequence([literal("foo"), ws(), literal("bar")])
iex> assert %Context{status: {:error, :unexpected_char}} = Ergo.parse(parser, "Hello World")

The transform/2 parser runs a transforming function on the AST of its child parser.

Link to this section Functions

Link to this function

choice(parsers, opts \\ [])

The choice/1 parser takes a list of parsers. It tries each in order attempting to match one. Once a match has been made choice returns the result of the matching parser.

Examples

iex> alias Ergo.Context
iex> import Ergo.{Terminals, Combinators}
iex> parser = choice([literal("Foo"), literal("Bar"), literal("Hello"), literal("World")], label: "Foo|Bar|Hello|World")
iex> context = Ergo.parse(parser, "Hello World")
iex> assert %Context{status: :ok, ast: "Hello", input: " World", char: ?o, index: 5, col: 6} = context

iex> alias Ergo.Context
iex> import Ergo.{Terminals, Combinators}
iex> parser = choice([literal("Foo"), literal("Bar")])
iex> context = Ergo.parse(parser, "Hello World")
iex> %Context{status: {:error, :no_valid_choice}, message: "No valid choice", ast: nil, input: "Hello World"} = context
Link to this function

ignore(parser, opts \\ [])

The ignore/1 parser matches but ignores the AST of its child parser.

Examples

iex> alias Ergo.Context
iex> import Ergo.{Terminals, Combinators}
iex> parser = sequence([literal("Hello"), ignore(ws()), literal("World")])
iex> context = Ergo.parse(parser, "Hello World")
iex> assert %Context{status: :ok, ast: ["Hello", "World"], index: 11, col: 12, char: ?d} = context
Link to this function

lookahead(parser, opts \\ [])

The lookahead parser accepts a parser and matches it but does not update the context when it succeeds.

Example

iex> alias Ergo.Context
iex> import Ergo.{Combinators, Terminals}
iex> parser = lookahead(literal("Hello"))
iex> Ergo.parse(parser, "Hello World")
%Context{status: :ok, ast: nil, input: "Hello World", char: 0, index: 0, line: 1, col: 1}

iex> alias Ergo.Context
iex> import Ergo.{Combinators, Terminals}
iex> parser = lookahead(literal("Helga"))
iex> Ergo.parse(parser, "Hello World")
%Context{status: {:error, :lookahead_fail}, ast: [?l, ?e, ?H], char: ?l, index: 3, col: 4, input: "lo World"}
Link to this function

many(parser, opts \\ [])

Examples

iex> Logger.disable(self())
iex> alias Ergo.Context
iex> import Ergo.{Combinators, Terminals}
iex> parser = many(wc(), label: "Chars")
iex> context = Ergo.parse(parser, "Hello World", debug: true)
iex> assert %Context{status: :ok, debug: true, ast: [?H, ?e, ?l, ?l, ?o], input: " World", index: 5, col: 6, char: ?o} = context

iex> alias Ergo.Context
iex> import Ergo.{Combinators, Terminals}
iex> parser = many(wc(), min: 6)
iex> context = Ergo.parse(parser, "Hello World")
iex> assert %Context{status: {:error, :many_less_than_min}, ast: nil, input: " World", char: ?o, index: 5, col: 6} = context

iex> alias Ergo.{Context, Parser}
iex> import Ergo.{Combinators, Terminals}
iex> parser = many(wc(), max: 3)
iex> context = Ergo.parse(parser, "Hello World")
iex> assert %Context{status: :ok, ast: [?H, ?e, ?l], input: "lo World", char: ?l, index: 3, col: 4} = context

iex> alias Ergo.{Context, Parser}
iex> import Ergo.{Combinators, Terminals}
iex> parser = many(wc(), map: &Enum.count/1)
iex> context = Ergo.parse(parser, "Hello World")
iex> %Context{status: :ok, ast: 5, input: " World", index: 5, col: 6, char: ?o} = context
Link to this function

not_lookahead(parser, opts \\ [])

The not_lookahead parser accepts a parser and attempts to match it. If the match fails the not_lookahead parser returns status: :ok but does not affect the context otherwise.

If the match succeeds the not_lookahead parser fails with {:error, :lookahead_fail}

Examples

iex> alias Ergo.Context iex> import Ergo.{Combinators, Terminals} iex> parser = not_lookahead(literal("Foo")) iex> Ergo.parse(parser, "Hello World") %Context{status: :ok, input: "Hello World"}

iex> alias Ergo.{Context, Parser} iex> import Ergo.{Combinators, Terminals} iex> parser = not_lookahead(literal("Hello")) iex> Ergo.parse(parser, "Hello World") %Context{status: {:error, :lookahead_fail}, input: "Hello World"}

Link to this function

optional(parser, opts \\ [])

Examples

iex> alias Ergo.Context
iex> import Ergo.{Terminals, Combinators}
iex> context = Ergo.parse(optional(literal("Hello")), "Hello World")
iex> assert %Context{status: :ok, ast: "Hello", input: " World", index: 5, col: 6, char: ?o} = context

In this example we deliberately ensure that the Context ast is not nil
iex> alias Ergo.{Context, Parser}
iex> import Ergo.{Terminals, Combinators}
iex> context = %{Context.new(" World") | ast: []}
iex> parser = optional(literal("Hello"))
iex> new_context = Parser.call(parser, context)
iex> assert %Context{status: :ok, ast: nil, input: " World", index: 0, col: 1, char: 0} = new_context
Link to this function

parse_many(parser, ctx, min, max, count)

Link to this function

sequence(parsers, opts \\ [])

Examples

iex> alias Ergo.Context
iex> import Ergo.{Terminals, Combinators}
iex> parser = sequence([literal("Hello"), ws(), literal("World")])
iex> context = Ergo.parse(parser, "Hello World")
%Context{status: :ok, ast: ["Hello", ?\s, "World"], char: ?d, index: 11, line: 1, col: 12} = context

iex> Logger.disable(self())
iex> alias Ergo.Context
iex> import Ergo.{Terminals, Combinators}
iex> parser = sequence([literal("Hello"), ws(), literal("World")], label: "HelloWorld")
iex> context = Ergo.parse(parser, "Hello World", debug: true)
iex> assert %Context{status: :ok, debug: true, ast: ["Hello", ?\s, "World"], char: ?d, index: 11, line: 1, col: 12} = context

iex> alias Ergo.Context
iex> import Ergo.{Terminals, Combinators}
iex> parser = sequence([literal("Hello"), ws(), literal("World")], map: fn ast -> Enum.join(ast, " ") end)
iex> context = Ergo.parse(parser, "Hello World")
iex> assert %Context{status: :ok, ast: "Hello 32 World", char: ?d, index: 11, line: 1, col: 12} = context

iex> Logger.disable(self())
iex> alias Ergo.Context
iex> import Ergo.{Terminals, Combinators}
iex> parser = sequence([literal("Hello"), ws(), literal("World")], label: "HelloWorld", map: fn ast -> Enum.join(ast, " ") end)
iex> context = Ergo.parse(parser, "Hello World", debug: true)
iex> assert %Context{status: :ok, debug: true, ast: "Hello 32 World", char: ?d, index: 11, line: 1, col: 12} = context

iex> alias Ergo.Context
iex> import Ergo.{Combinators, Terminals}
iex> parser = sequence([literal("foo"), ws(), literal("bar")])
iex> assert %Context{status: {:error, :unexpected_char}} = Ergo.parse(parser, "Hello World")
Link to this function

transform(parser, t_fn, opts \\ [])

The transform/2 parser runs a transforming function on the AST of its child parser.

Examples

# Sum the digits
iex> alias Ergo.Context
iex> import Ergo.{Combinators, Terminals}
iex> digit_to_int = fn d -> List.to_string([d]) |> String.to_integer() end
iex> t_fn = fn ast -> ast |> Enum.map(digit_to_int) |> Enum.sum() end
iex> parser = sequence([digit(), digit(), digit(), digit()]) |> transform(t_fn)
iex> context = Ergo.parse(parser, "1234")
iex> %Context{status: :ok, ast: 10, char: ?4, index: 4, line: 1, col: 5} = context