Predicator.Parser (predicator v2.2.0)
View SourceRecursive descent parser for predicator expressions.
The parser converts a stream of tokens from the lexer into an Abstract Syntax Tree (AST) with comprehensive error reporting including exact position information.
Grammar
The parser implements this grammar with proper operator precedence:
expression → logical_or
logical_or → logical_and ( "OR" | "||" logical_and )*
logical_and → logical_not ( "AND" | "&&" logical_not )*
logical_not → "NOT" | "!" logical_not | equality
equality → comparison ( ( "==" | "!=" ) comparison )*
comparison → addition ( ( ">" | "<" | ">=" | "<=" | "=" | "in" | "contains" ) addition )?
addition → multiplication ( ( "+" | "-" ) multiplication )*
multiplication → unary ( ( "*" | "/" | "%" ) unary )*
unary → ( "-" | "!" ) unary | postfix
postfix → primary ( "[" expression "]" )*
primary → NUMBER | STRING | BOOLEAN | DATE | DATETIME | IDENTIFIER | function_call | list | "(" expression ")"
function_call → FUNCTION_NAME "(" ( expression ( "," expression )* )? ")"
list → "[" ( expression ( "," expression )* )? "]"
Examples
iex> {:ok, tokens} = Predicator.Lexer.tokenize("score > 85")
iex> Predicator.Parser.parse(tokens)
{:ok, {:comparison, :gt, {:identifier, "score"}, {:literal, 85}}}
iex> {:ok, tokens} = Predicator.Lexer.tokenize("(age >= 18)")
iex> Predicator.Parser.parse(tokens)
{:ok, {:comparison, :gte, {:identifier, "age"}, {:literal, 18}}}
iex> {:ok, tokens} = Predicator.Lexer.tokenize("score > 85 AND age >= 18")
iex> Predicator.Parser.parse(tokens)
{:ok, {:logical_and, {:comparison, :gt, {:identifier, "score"}, {:literal, 85}}, {:comparison, :gte, {:identifier, "age"}, {:literal, 18}}}}
Summary
Types
Arithmetic operators in the AST.
Abstract Syntax Tree node types.
Comparison operators in the AST.
Equality operators in the AST.
Membership operators in the AST.
Internal parser state for tracking position and tokens.
Parser result - either success with AST or error with details.
Unary operators in the AST.
A value that can appear in literals.
Functions
Parses a list of tokens into an Abstract Syntax Tree.
Types
@type arithmetic_op() :: :add | :subtract | :multiply | :divide | :modulo
Arithmetic operators in the AST.
@type ast() :: {:literal, value()} | {:string_literal, binary(), :double | :single} | {:identifier, binary()} | {:comparison, comparison_op(), ast(), ast()} | {:equality, equality_op(), ast(), ast()} | {:arithmetic, arithmetic_op(), ast(), ast()} | {:unary, unary_op(), ast()} | {:membership, membership_op(), ast(), ast()} | {:logical_and, ast(), ast()} | {:logical_or, ast(), ast()} | {:logical_not, ast()} | {:list, [ast()]} | {:function_call, binary(), [ast()]} | {:bracket_access, ast(), ast()}
Abstract Syntax Tree node types.
{:literal, value}
- A literal value (number, boolean, list, date, datetime){:string_literal, value, quote_type}
- A string literal with quote type information{:identifier, name}
- A variable reference{:comparison, operator, left, right}
- A comparison expression{:equality, operator, left, right}
- An equality expression (== !=){:arithmetic, operator, left, right}
- An arithmetic expression (+, -, *, /, %){:unary, operator, operand}
- A unary expression (-, !){:logical_and, left, right}
- A logical AND expression{:logical_or, left, right}
- A logical OR expression{:logical_not, operand}
- A logical NOT expression{:list, elements}
- A list literal{:membership, operator, left, right}
- A membership operation (in/contains){:function_call, name, arguments}
- A function call with arguments{:bracket_access, object, key}
- A bracket access expression (obj[key])
@type comparison_op() :: :gt | :lt | :gte | :lte | :eq
Comparison operators in the AST.
@type equality_op() :: :equal_equal | :ne
Equality operators in the AST.
@type membership_op() :: :in | :contains
Membership operators in the AST.
@type parser_state() :: %{ tokens: [Predicator.Lexer.token()], position: non_neg_integer() }
Internal parser state for tracking position and tokens.
@type result() :: {:ok, ast()} | {:error, binary(), pos_integer(), pos_integer()}
Parser result - either success with AST or error with details.
@type unary_op() :: :minus | :bang
Unary operators in the AST.
A value that can appear in literals.
Functions
@spec parse([Predicator.Lexer.token()]) :: result()
Parses a list of tokens into an Abstract Syntax Tree.
Parameters
tokens
- List of tokens from the lexer
Returns
{:ok, ast}
- Successfully parsed expression{:error, message, line, column}
- Parse error with position
Examples
iex> {:ok, tokens} = Predicator.Lexer.tokenize("score > 85")
iex> Predicator.Parser.parse(tokens)
{:ok, {:comparison, :gt, {:identifier, "score"}, {:literal, 85}}}
iex> {:ok, tokens} = Predicator.Lexer.tokenize("name = \"John\"")
iex> Predicator.Parser.parse(tokens)
{:ok, {:comparison, :eq, {:identifier, "name"}, {:string_literal, "John", :double}}}
iex> {:ok, tokens} = Predicator.Lexer.tokenize("active = true")
iex> Predicator.Parser.parse(tokens)
{:ok, {:comparison, :eq, {:identifier, "active"}, {:literal, true}}}