View Source Alembic.Parser.Expression (alembic v0.2.0)

Parses the raw string content of a Liquid expression — everything that can appear inside {{ ... }} output tags and {% if ... %} / {% for ... in ... %} conditions — into an Alembic.AST.expr() node.

Grammar (see docs/grammar.md):

expr             = or_expr ;
or_expr          = and_expr , { "or" , and_expr } ;
and_expr         = not_expr , { "and" , not_expr } ;
not_expr         = [ "not" ] , comparison ;
comparison       = filtered_primary , [ compare_op , filtered_primary ] ;
filtered_primary = primary , { filter } ;
primary          = variable | literal ;
filter           = "|" , IDENT , [ ":" , expr , { "," , expr } ] ;

The barewords empty and blank are contextual keywords: a bare empty/blank used as a comparison operand (for any comparison operator) parses to {:keyword, :empty} / {:keyword, :blank}. They are still equality-only at render time (==/!=); other operators return {:keyword_requires_equality, ...}. Outside comparison operands, bare empty/blank stay ordinary variable paths, so {{ empty }} and {% assign empty = 1 %} still resolve a variable named empty.

Filters bind tighter than comparison and logical operators — this lets a bare "name | upcase" parse on its own (used for output tags) while still allowing filtered operands inside a condition, e.g. x | size > 0.

Summary

Functions

Parses the raw string content of an output tag or a tag condition into an Alembic.AST.expr().

Like parse/1, but allow_ranges: true permits (from..to) range literals. Ranges are restricted to the {% for %} iterable position; calling parse/1 (or parse/2 without the flag) on a range returns {:error, :range_not_allowed}.

Like parse_list/1, but bare empty/blank values become keyword operands. Used for {% when %} values, which are compared against the case subject with == semantics and therefore accept the keywords.

Parses a comma-separated list of expressions (the expr_list production) — the values of {% when a, b, c %} and {% cycle a, b %}.

Types

@type reason() ::
  :empty_expression
  | :unterminated_string
  | :missing_filter_name
  | {:unknown_operator, String.t()}
  | {:unexpected_token, term()}
  | :range_not_allowed

Functions

@spec parse(String.t()) :: {:ok, Alembic.AST.expr()} | {:error, reason()}

Parses the raw string content of an output tag or a tag condition into an Alembic.AST.expr().

Examples

iex> Alembic.Parser.Expression.parse("user.name")
{:ok, {:variable, ["user", "name"]}}

iex> Alembic.Parser.Expression.parse("name | upcase")
{:ok, {:filter_chain, {:variable, ["name"]}, [{:filter, "upcase", []}]}}

iex> Alembic.Parser.Expression.parse("x > 0 and not skip")
{:ok,
 {:logical, :and, {:compare, :gt, {:variable, ["x"]}, {:literal, 0}},
  {:not, {:variable, ["skip"]}}}}
Link to this function

parse(source, allow_ranges)

View Source
@spec parse(String.t(), boolean()) :: {:ok, Alembic.AST.expr()} | {:error, reason()}

Like parse/1, but allow_ranges: true permits (from..to) range literals. Ranges are restricted to the {% for %} iterable position; calling parse/1 (or parse/2 without the flag) on a range returns {:error, :range_not_allowed}.

Link to this function

parse_keyword_list(source)

View Source
@spec parse_keyword_list(String.t()) ::
  {:ok, [Alembic.AST.expr()]} | {:error, reason()}

Like parse_list/1, but bare empty/blank values become keyword operands. Used for {% when %} values, which are compared against the case subject with == semantics and therefore accept the keywords.

@spec parse_list(String.t()) :: {:ok, [Alembic.AST.expr()]} | {:error, reason()}

Parses a comma-separated list of expressions (the expr_list production) — the values of {% when a, b, c %} and {% cycle a, b %}.

Commas that belong to a filter's own argument list are consumed by the filter parser, so splitting happens only at expression-list boundaries: x | append: "a", "b" is one filtered expression (two filter arguments), while 1, 2, 3 is three values.

Examples

iex> Alembic.Parser.Expression.parse_list("1, 2, 3")
{:ok, [{:literal, 1}, {:literal, 2}, {:literal, 3}]}

iex> Alembic.Parser.Expression.parse_list(~s(x | append: "a", "b"))
{:ok,
 [{:filter_chain, {:variable, ["x"]},
   [{:filter, "append", [{:literal, "a"}, {:literal, "b"}]}]}]}