Ergo.Context (Ergo v0.9.9)
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
status
When a parser returns it either sets status
to :ok
to indicate that it was successful or to a tuple
{:error, reasons}
where reasons is a list of tuples representing a sequence of errors. Each tuple is of
the form {code, message}
where code is an atom indiciting the specific type of error and the message
contains additional human-focused information that may be helpful in diagnosing the problem.
input
The binary input being parsed.
consumed
The binary input that has already been consumed by parsing.
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.
entry_points
A list of Parser
entry points
data
Map containing user-data that the parsers can use to pass information between them.
ast
Represents the current data structure being built from the input.
parser
The current parser.
tracks
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.
depth
As nested parsers are called the depth field will be updated to reflect the number of levels of nesting for the current parser.
Link to this section Summary
Functions
Examples
iex> alias Ergo.Context
iex> context =
...> Context.new("Hello World")
...> |> Context.add_error(:unexpected_char, "Expected: |e| Actual: |.|")
iex> assert is_nil(context.ast)
iex> assert {:error, [{:unexpected_char, {1, 1}, "Expected: |e| Actual: |.|"}]} = context.status
iex> alias Ergo.Context
iex> context =
...> Context.new("Hello World")
...> |> Context.add_error(:unexpected_char, "Expected: |e| Actual: |.|")
...> |> Context.add_error(:literal_failed, "Expected 'end'")
iex> assert is_nil(context.ast)
iex> assert {:error, [{:literal_failed, {1, 1}, "Expected 'end'"}, {:unexpected_char, {1, 1}, "Expected: |e| Actual: |.|"}]} = 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("Hello")
iex> assert %Context{status: :ok, ast: ?H, input: "ello", index: 1, line: 1, col: 2} = Context.peek(context)
iex> context = Context.new("")
iex> assert %Context{status: {:error, [{:unexpected_eoi, {1, 1}, "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, error_id, message \\ "")
Examples
iex> alias Ergo.Context
iex> context =
...> Context.new("Hello World")
...> |> Context.add_error(:unexpected_char, "Expected: |e| Actual: |.|")
iex> assert is_nil(context.ast)
iex> assert {:error, [{:unexpected_char, {1, 1}, "Expected: |e| Actual: |.|"}]} = context.status
iex> alias Ergo.Context
iex> context =
...> Context.new("Hello World")
...> |> Context.add_error(:unexpected_char, "Expected: |e| Actual: |.|")
...> |> Context.add_error(:literal_failed, "Expected 'end'")
iex> assert is_nil(context.ast)
iex> assert {:error, [{:literal_failed, {1, 1}, "Expected 'end'"}, {:unexpected_char, {1, 1}, "Expected: |e| Actual: |.|"}]} = context.status
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("", 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("", 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("", ast: "Hello World")
iex> assert %Context{ast: "Hello World"} = Context.ast_transform(context, &Function.identity/1)
iex> alias Ergo.Context
iex> context = Context.new("", ast: "Hello World")
iex> assert %Context{ast: 11} = Context.ast_transform(context, &String.length/1)
iex> alias Ergo.Context
iex> context = Context.new("", 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("", ast: ["Hello", nil, "World", nil])
iex> assert %Context{ast: ["Hello", "World"]} = Context.ast_without_ignored(context)
clip(context, length \\ 40)
commit(ctx)
dec_depth(ctx)
inc_depth(ctx)
new(input \\ "", options \\ [])
new
returns a newly initialised Context
with input
set to the string passed in.
Examples:
iex> alias Ergo.Context iex> assert %Context{status: :ok, input: "Hello World", line: 1, col: 1, index: 0, tracks: %{}} = Context.new("Hello World")
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("")) iex> assert %Context{status: {:error, [{:unexpected_eoi, {1, 1}, "Unexpected end of input"}] }} = context
iex> context = Context.next_char(Context.new("Hello World")) iex> assert %Context{status: :ok, input: "ello World", ast: ?H, index: 1, line: 1, col: 2} = context
parent_parser(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("Hello World") |> Context.track_parser(parser_ref, :foo) iex> assert Context.parser_tracked?(context, parser_ref)
peek(ctx)
Examples
iex> context = Context.new("Hello")
iex> assert %Context{status: :ok, ast: ?H, input: "ello", index: 1, line: 1, col: 2} = Context.peek(context)
iex> context = Context.new("")
iex> assert %Context{status: {:error, [{:unexpected_eoi, {1, 1}, "Unexpected end of input"}]}, index: 0, line: 1, col: 1} = Context.peek(context)
pop_parser(ctx)
push_parser(ctx, next_parser)
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("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
set_ast(ctx, new_ast)
track_parser(ctx, ref, data)
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("Hello World")
iex> parser = literal("Hello")
iex> context2 = Context.track_parser(context, parser.ref, :foo)
iex> assert Map.has_key?(context2.tracks, {0, parser.ref})