Predicator.Compiler (predicator v9.0.1)

Copy Markdown View Source

Compiler that converts AST to various representations using visitors.

The compiler orchestrates different visitors to transform Abstract Syntax Trees into executable instructions, string representations, or other formats.

Examples

iex> ast = {:comparison, :gt, {:identifier, "score", nil}, {:literal, 85, nil}, nil}
iex> Predicator.Compiler.to_instructions(ast)
[["load", "score"], ["lit", 85], ["compare", "GT"]]

# Future visitors will enable:
# iex> Predicator.Compiler.to_string(ast)
# "score > 85"

# iex> Predicator.Compiler.to_dot(ast)
# "digraph {...}"

Summary

Functions

Converts an AST to stack machine instructions.

Converts an AST to stack machine instructions plus a source-position side table.

Converts an AST to stack machine instructions, a source-position side table, and a per-store segment-position side table.

Converts an AST to a string representation.

Functions

to_instructions(ast, opts \\ [])

@spec to_instructions(
  Predicator.Parser.visitable(),
  keyword()
) :: [[binary() | term()]] | {:error, struct()}

Converts an AST to stack machine instructions.

Uses the InstructionsVisitor to generate a list of instructions that can be executed by the stack-based evaluator.

Parameters

  • ast - The Abstract Syntax Tree to compile - a bare expression, or a Predicator.Parser.program/0, which compiles to a flat statement program (each statement followed by a store or a pop)
  • opts - Optional compiler options

Returns

List of instructions in the format [["operation", ...args]].

Examples

iex> ast = {:literal, 42, nil}
iex> Predicator.Compiler.to_instructions(ast)
[["lit", 42]]

iex> ast = {:comparison, :eq, {:identifier, "name", nil}, {:literal, "John", nil}, nil}
iex> Predicator.Compiler.to_instructions(ast)
[["load", "name"], ["lit", "John"], ["compare", "EQ"]]

iex> {:ok, program} = Predicator.parse_program("x = 1")
iex> Predicator.Compiler.to_instructions(program)
[["lit", "x"], ["lit", 1], ["store", 1]]

to_instructions_with_positions(ast, opts \\ [])

@spec to_instructions_with_positions(
  Predicator.Parser.visitable(),
  keyword()
) ::
  {[[binary() | term()]],
   Predicator.Types.position_table() | Predicator.Types.span_table()}
  | {:error, struct()}

Converts an AST to stack machine instructions plus a source-position side table.

The instruction list is identical to to_instructions/2's - the side table is a separate Elixir-side value, so the interchange format and any stored compiled artifacts are unaffected (ADR-0001). The table maps each instruction's 0-based index to the {line, column} of the AST node that emitted it; nodes with no position contribute no entry, so a caller-supplied position-free AST yields an empty table. An AST parsed with spans: true yields a Predicator.Types.span_table/0 instead - see Predicator.compile_with_spans/1.

Examples

iex> ast = {:comparison, :gt, {:identifier, "score", {1, 1}}, {:literal, 85, {1, 9}}, {1, 7}}
iex> Predicator.Compiler.to_instructions_with_positions(ast)
{[["load", "score"], ["lit", 85], ["compare", "GT"]],
 %{0 => {1, 1}, 1 => {1, 9}, 2 => {1, 7}}}

iex> Predicator.Compiler.to_instructions_with_positions({:literal, 42, nil})
{[["lit", 42]], %{}}

iex> {:ok, program} = Predicator.parse_program("x = 1", spans: false)
iex> Predicator.Compiler.to_instructions_with_positions(program)
{[["lit", "x"], ["lit", 1], ["store", 1]], %{0 => {1, 1}, 1 => {1, 5}, 2 => {1, 1}}}

to_instructions_with_segment_positions(ast, opts \\ [])

@spec to_instructions_with_segment_positions(
  Predicator.Parser.visitable(),
  keyword()
) ::
  {[[binary() | term()]],
   Predicator.Types.position_table() | Predicator.Types.span_table(),
   Predicator.Types.segment_position_table()}
  | {:error, struct()}

Converts an AST to stack machine instructions, a source-position side table, and a per-store segment-position side table.

The instruction list and position table are identical to what to_instructions_with_positions/2 returns for the same AST - this function adds a third table mapping each ["store", n] instruction's 0-based index to one source annotation per location segment in its lhs chain, root-first (Predicator.Types.segment_position_table/0). An assignment-free AST contributes no entry, so an expression compiles to an empty segment table.

Examples

iex> {:ok, program} = Predicator.parse_program("a.b = 1", spans: false)
iex> Predicator.Compiler.to_instructions_with_segment_positions(program)
{[["lit", "a"], ["lit", "b"], ["lit", 1], ["store", 2]],
 %{0 => {1, 1}, 1 => {1, 3}, 2 => {1, 7}, 3 => {1, 1}},
 %{3 => [{1, 1}, {1, 3}]}}

to_string(ast, opts \\ [])

@spec to_string(
  Predicator.Parser.visitable(),
  keyword()
) :: binary()

Converts an AST to a string representation.

Uses the StringVisitor to generate a readable string representation of the Abstract Syntax Tree. This is useful for debugging, documentation, and displaying expressions to users.

Parameters

  • ast - The Abstract Syntax Tree to convert - a bare expression, or a Predicator.Parser.program/0, which renders as its statements joined by "; "
  • opts - Optional formatting options:
    • :parentheses - :minimal (default) | :explicit | :none

    • :spacing - :normal (default) | :compact | :verbose

Returns

String representation of the AST.

Examples

iex> ast = {:literal, 42, nil}
iex> Predicator.Compiler.to_string(ast)
"42"

iex> ast = {:comparison, :gt, {:identifier, "score", nil}, {:literal, 85, nil}, nil}
iex> Predicator.Compiler.to_string(ast)
"score > 85"

iex> ast = {:comparison, :gt, {:identifier, "age", nil}, {:literal, 21, nil}, nil}
iex> Predicator.Compiler.to_string(ast, parentheses: :explicit)
"(age > 21)"