Elex.Parser (Elex v0.2.0)
View SourceParses Elex expression strings into AST tuples.
The parser is built with NimbleParsec and supports operator precedence, function calls,
literals, and variables. Parsing optionally validates the AST against a
Elex.Context via Elex.Validator.
Syntax overview
Literals: decimal numbers (3.14, -5), booleans (true, false, yes, no),
null (null), strings ("hello")
Variables: lowercase identifiers (x, my_var)
Operators (by precedence, lowest to highest):
orand==,!=,<,>,<=,>=(operands must share a type; strings use lexicographic order)+,-*,/,%not(unary)-(unary)
Functions: name(arg1, arg2) — built-ins include abs, between, ceil,
clamp, coalesce, concat, contains, ends_with, floor, if,
length, lower, max, min, mod, pi, pow, rem, round, sqrt,
starts_with, trim, and upper. min, max, and coalesce accept two
or more arguments. See modules under Elex.Functions.*.
Short-circuit: and, or, and if(condition, a, b) skip evaluating
operands or branches that cannot affect the result.
Parentheses group sub-expressions: (1 + 2) * 3
Examples
context = Elex.new_context() |> Elex.add_variable("x", 10)
Elex.Parser.parse("x + 5", context)
#=> {:ok, {:+, [{:var, "x"}, #Decimal<5>]}, :decimal}
Summary
Functions
Parses an expression and returns the raw, low-level parser state.
Parses an expression string and optionally validates the resulting AST against a context.
Types
@type debug_info() :: %{ expression: String.t(), status: :ok | :error, ast: term() | nil, reason: String.t() | nil, consumed: String.t(), rest: String.t(), line: pos_integer(), column: non_neg_integer(), byte_offset: non_neg_integer() }
Low-level parse details returned by debug/1.
:expression- the original input string:status-:okwhen the whole input parsed,:errorotherwise:ast- the parsed AST on success,nilon error:reason- the raw NimbleParsec error message on error,nilon success:consumed- the leading portion of the input the parser accepted:rest- the unconsumed remainder at the furthest position reached:line/:column- 1-based line and 0-based column of that position. NimbleParsec does not track intra-line columns for this grammar, so:columnis0for single-line expressions; use:byte_offsetto locate the position instead.:byte_offset- byte offset of that position into the input
Functions
@spec debug( String.t(), keyword() ) :: debug_info()
Parses an expression and returns the raw, low-level parser state.
This is a development and debugging aid: it exposes exactly what the underlying NimbleParsec parser produced (including the raw error message, how far it got, and what input was left over) without validating the AST against a context or translating errors into human-readable messages.
Use parse/3 for normal parsing.
Options
:max_depth- Maximum parenthesis/function-call nesting depth. Expressions nested more deeply are rejected before parsing (mirroringparse/3), guarding against resource exhaustion from pathologically nested input. The returned map hasstatus: :errorwith the same reasonparse/3reports. Must be a non-negative integer; an invalid value yields an error map rather than silently disabling the guard. Defaults to16.
Returns
A debug_info map. See its documentation for the fields.
Examples
iex> info = Elex.Parser.debug("( 1 + 2")
iex> info.status
:error
iex> info.rest
"( 1 + 2"
iex> info = Elex.Parser.debug("1 + 2")
iex> info.status
:ok
iex> info.rest
""
@spec parse(String.t(), Elex.Context.t(), keyword()) :: {:ok, term(), atom()} | {:error, String.t()}
Parses an expression string and optionally validates the resulting AST against a context.
Parameters
expression- The expression string to parsecontext- AElex.Contextwith variables and functionsopts- Keyword list of options (see below)
Options
:validate- Whether to validate the AST against the context. Defaults totrue. Whenfalse, the returned type isnil.:max_depth- Maximum parenthesis/function-call nesting depth. Expressions nested more deeply are rejected with an error before parsing, guarding against resource exhaustion from pathologically nested input. Defaults to16.
Returns
{:ok, ast, type}- Parsed (and optionally validated) AST with result type{:error, reason}- Parse or validation error message
Examples
context = Elex.new_context() |> Elex.add_variable("x", 10)
Elex.Parser.parse("x + 5", context)
#=> {:ok, {:+, [{:var, "x"}, #Decimal<5>]}, :decimal}
Elex.Parser.parse("unknown_var", context)
#=> {:error, "variable 'unknown_var' does not exist"}