Ergo.Context (Ergo v0.6.2)
Ergo.Context
defines the Context
struct that is used to maintain parser state as the various
parsers work, and various functions for creating & manipulating contexts.
Fields
invoke_fn
The invoke_fn
defines the parsing entry point used for calling parsers.
status
When a parser returns it either sets status
to :ok
to indicate that it was successful or to a tuple
{:error, :error_atom}
where error_atom
is an atom indiciting the specific type of error. It may optionally
set the message
field to a human readable message.
input
The binary input being parsed.
index
Represents the position in the input which has been read so far. Initially 0 it increments for each character processed.
line
Represents the current line of the input. Initially 1 it increments whenever a is read from the input.
col
Represents the current column of the input. Initially 1 it is incremented every time a character is read from the input and automatically resets whenever a is read.
ast
Represents the current data structure being built from the input.
tracks
Parsers for which track: true
is specified (by default this is most of the
combinator parsers but not the terminal parsers) will add themselves to the
Context
tracks in the form of {ref, index}
. If the same parser attempts
to add itself a second time at the same index an error is thrown because a
cycle has been detected.
Link to this section Summary
Functions
Examples
iex> alias Ergo.Context
iex> context =
...> Context.new(&Ergo.Parser.call/2, "Hello World")
...> |> Context.add_error(:unexpected_char, "Expected 'e' got '.'")
iex> assert is_nil(context.ast)
iex> assert {:error, [{:unexpected_char, "Expected 'e' got '.'"}]} = context.status
iex> alias Ergo.Context
iex> context =
...> Context.new(&Ergo.Parser.call/2, "Hello World")
...> |> Context.add_error(:unexpected_char, "Expected 'e' got '.'")
...> |> Context.add_error(:literal_failed, "Expected 'end'")
iex> assert is_nil(context.ast)
iex> assert {:error, [{:literal_failed, "Expected 'end'"}, {:unexpected_char, "Expected 'e' got '.'"}]} = context.status
Because we build ASTs using lists they end up in reverse order. This method reverses the AST back to in-parse-order
Where an AST has been built from individual characters and needs to be converted to a string
Called to perform an arbitrary transformation on the AST value of a Context.
The ignore
parser matches but returns a nil for the AST. Parsers like sequence
accumulate these nil values.
Call this function to remove them
new
returns a newly initialised Context
with input
set to the string passed in.
Reads the next character from the input
of the passed in Context
.
Returns truthy value if the parser referred to by ref
has already been called for the index index
.
Examples
iex> context = Context.new(&Ergo.Parser.call/2, "Hello")
iex> assert %Context{status: :ok, ast: ?H, input: "ello", index: 1, line: 1, col: 2} = Context.peek(context)
iex> context = Context.new(&Ergo.Parser.call/2, "")
iex> assert %Context{status: {:error, [{:unexpected_eoi, "Unexpected end of input"}]}, index: 0, line: 1, col: 1} = Context.peek(context)
Clears the value of the status and ast fields to ensure that the wrong status information cannot be returned from a child parser.
Updates the Context
to track that the parser referred to by ref
has been called for the index index
.
Link to this section Functions
add_error(ctx, code, message)
Examples
iex> alias Ergo.Context
iex> context =
...> Context.new(&Ergo.Parser.call/2, "Hello World")
...> |> Context.add_error(:unexpected_char, "Expected 'e' got '.'")
iex> assert is_nil(context.ast)
iex> assert {:error, [{:unexpected_char, "Expected 'e' got '.'"}]} = context.status
iex> alias Ergo.Context
iex> context =
...> Context.new(&Ergo.Parser.call/2, "Hello World")
...> |> Context.add_error(:unexpected_char, "Expected 'e' got '.'")
...> |> Context.add_error(:literal_failed, "Expected 'end'")
iex> assert is_nil(context.ast)
iex> assert {:error, [{:literal_failed, "Expected 'end'"}, {:unexpected_char, "Expected 'e' got '.'"}]} = context.status
add_errors(ctx, errors)
ast_in_parsed_order(ctx)
Because we build ASTs using lists they end up in reverse order. This method reverses the AST back to in-parse-order
Examples
iex> context = Ergo.Context.new(&Ergo.Parser.call/2, "", ast: [4, 3, 2, 1])
iex> assert %Context{ast: [1, 2, 3, 4]} = Context.ast_in_parsed_order(context)
ast_to_string(ctx)
Where an AST has been built from individual characters and needs to be converted to a string
Examples
iex> context = Ergo.Context.new(&Ergo.Parser.call/2, "", ast: [?H, ?e, ?l, ?l, ?o])
iex> assert %Context{ast: "Hello"} = Context.ast_to_string(context)
ast_transform(ctx, fun)
Called to perform an arbitrary transformation on the AST value of a Context.
Examples
iex> alias Ergo.Context
iex> context = Context.new(&Ergo.Parser.call/2, "", ast: "Hello World")
iex> assert %Context{ast: "Hello World"} = Context.ast_transform(context, &Function.identity/1)
iex> alias Ergo.Context
iex> context = Context.new(&Ergo.Parser.call/2, "", ast: "Hello World")
iex> assert %Context{ast: 11} = Context.ast_transform(context, &String.length/1)
iex> alias Ergo.Context
iex> context = Context.new(&Ergo.Parser.call/2, "", ast: "Hello World")
iex> assert %Context{ast: "Hello World"} = Context.ast_transform(context, nil)
ast_without_ignored(ctx)
The ignore
parser matches but returns a nil for the AST. Parsers like sequence
accumulate these nil values.
Call this function to remove them
Examples
iex> context = Ergo.Context.new(&Ergo.Parser.call/2, "", ast: ["Hello", nil, "World", nil])
iex> assert %Context{ast: ["Hello", "World"]} = Context.ast_without_ignored(context)
clip(context, length \\ 40)
dec_depth(ctx)
inc_depth(ctx)
new(invoke_fn, input \\ "", options \\ [])
new
returns a newly initialised Context
with input
set to the string passed in.
Examples:
iex> Context.new(&Ergo.Parser.call/2, "Hello World") %Context{status: :ok, input: "Hello World", line: 1, col: 1, index: 0, tracks: %MapSet{}, invoke_fn: &Ergo.Parser.call/2}
next_char(context)
Reads the next character from the input
of the passed in Context
.
If the input
is empty returns status: {:error, :unexpected_eoi}
.
Otherwise returns a new Context
setting ast
to the character read and incrementing positional variables such as index
, line
, and column
appropriately.
Examples
iex> context = Context.next_char(Context.new(&Ergo.Parser.call/2, "")) iex> assert %Context{status: {:error, [{:unexpected_eoi, "Unexpected end of input"}] }} = context
iex> context = Context.next_char(Context.new(&Ergo.Parser.call/2, "Hello World")) iex> assert %Context{status: :ok, input: "ello World", ast: ?H, index: 1, line: 1, col: 2} = context
parser_tracked?(context, ref)
Returns truthy value if the parser referred to by ref
has already been called for the index index
.
Examples
iex> alias Ergo.Context iex> parser_ref = 123 iex> context = Context.new(&Ergo.Parser.call/2, "Hello World") |> Context.track_parser(parser_ref) iex> assert Context.parser_tracked?(context, parser_ref)
peek(ctx)
Examples
iex> context = Context.new(&Ergo.Parser.call/2, "Hello")
iex> assert %Context{status: :ok, ast: ?H, input: "ello", index: 1, line: 1, col: 2} = Context.peek(context)
iex> context = Context.new(&Ergo.Parser.call/2, "")
iex> assert %Context{status: {:error, [{:unexpected_eoi, "Unexpected end of input"}]}, index: 0, line: 1, col: 1} = Context.peek(context)
reset_status(ctx)
Clears the value of the status and ast fields to ensure that the wrong status information cannot be returned from a child parser.
Examples
iex> context = Context.new(&Ergo.Parser.call/2, "Hello World")
iex> context = %{context | status: {:error, [{:inexplicable_error, "What the…"}]}, ast: true}
iex> context = Context.reset_status(context)
iex> assert %Context{status: :ok, ast: nil} = context
trace(ctx, bool, message)
trace_match(ctx, debug, type, label)
track_parser(ctx, ref)
Updates the Context
to track that the parser referred to by ref
has been called for the index index
.
Examples:
iex> alias Ergo.Context
iex> import Ergo.Terminals
iex> context = Context.new(&Ergo.Parser.call/2, "Hello World")
iex> parser = literal("Hello")
iex> context2 = Context.track_parser(context, parser.ref)
iex> assert MapSet.member?(context2.tracks, {parser.ref, 0})