Expression (expression v3.0.0-rc.0)

Copy Markdown

Documentation for Expression, a library to parse and evaluate Floip compatible expressions

Expression is an expression language which consists of the functions provided by Excel with a few additions.

Function and variable names are not case-sensitive so UPPER is equivalent to upper:

contact.name -> Marshawn Lynch
FIRST_WORD(contact.name) -> Marshawn
first_word(CONTACT.NAME) -> Marshawn

For templating, RapidPro uses the @ character to denote either a single variable substitution or the beginning of an Expression block. @ was chosen as it is known how to type by a broad number of users regardless of keyboard. It does have the disadvantage of being used in email addresses and Twitter handles, but these are rarely ambiguous and escaping can be done easily via doubling of the character (@@).

Functions are called by using the block syntax:

10 plus 4 is @(SUM(10, 4))

Within a block, @ is not required to refer to variable in the context:

Hello @(contact.name)

A template can contain more than one substitution or block:

Hello @contact.name, you were born in @(YEAR(contact.birthday))

v2-compat mode

All evaluation entry points accept a trailing options list. Passing mode: :v2 restores the v2 evaluation semantics that changed in v3 (context key lowercasing and string value coercion):

Expression.evaluate_as_string!("@date", %{"date" => "2020-12-13T23:34:45"}, mod, mode: :v2)

See evaluation_opts/0 for the individual flags.

Summary

Types

Options accepted by the evaluation entry points.

Types

evaluation_opts()

@type evaluation_opts() :: [
  mode: :v2 | :v3,
  lowercase_keys: boolean(),
  coerce_strings: boolean()
]

Options accepted by the evaluation entry points.

mode: :v2 expands to the v2-compat flags: lowercase_keys: true, coerce_strings: true. Explicitly passed flags win over the expansion. mode: :v3 (or omitting :mode) keeps the v3 defaults.

Note: v2's operator semantics need no compat flags — the parser collapses = and == into the same operator in both v2 and v3, so the documented v2 "date-only =" behavior was unreachable dead code and evaluation semantics beyond context normalization are unchanged.

expression_type()

@type expression_type() ::
  String.t() | number() | map() | DateTime.t() | Date.t() | Time.t()

Functions

error_map(message)

@spec error_map(message :: term()) :: %{required(String.t()) => term()}

Build the legacy error map shape used by callbacks to signal recoverable errors that should flow through evaluation as a value.

New code should raise Expression.Error instead. This helper exists for callbacks that need to return an error sentinel without halting evaluation.

escape(expression)

@spec escape(String.t()) :: String.t()

evaluate(expression, context \\ %{}, mod \\ Expression.Callbacks, opts \\ [])

evaluate!(expression, context \\ %{}, mod \\ Expression.Callbacks, opts \\ [])

evaluate_as_boolean!(expression, context \\ %{}, mod \\ Expression.Callbacks, opts \\ [])

evaluate_as_string!(expression, context \\ %{}, mod \\ Expression.Callbacks, opts \\ [])

@spec evaluate_as_string!(
  String.t() | Number.t() | nil,
  map(),
  module(),
  evaluation_opts()
) :: String.t()

evaluate_block(expression, context \\ %{}, mod \\ Expression.Callbacks, opts \\ [])

evaluate_block!(expression, context \\ %{}, mod \\ Expression.Callbacks, opts \\ [])

evaluate_template!(template, context \\ %{}, mod \\ Expression.Callbacks, opts \\ [])

@spec evaluate_template!(String.t(), map(), module(), evaluation_opts()) :: String.t()

Evaluate a string as an expression template, resolving any @variable references and @(expression) blocks within it.

This is the explicit version of the behavior that occurs implicitly when a string literal appears inside an expression (e.g., "hello @name" as an argument to a function). Use this when you need template resolution and want to be explicit about it.

Raises Expression.Error on parse or evaluation failures.

Examples

iex> Expression.evaluate_template!("hello @name", %{"name" => "world"})
"hello world"

iex> Expression.evaluate_template!("1 + 1 = @(1 + 1)", %{})
"1 + 1 = 2"

parse!(expression)

@spec parse!(String.t() | Number.t() | Time.t() | boolean()) :: Keyword.t()

parse_expression(expression_block)

@spec parse_expression(String.t()) :: {:ok, Keyword.t()} | {:error, String.t()}

parse_expression!(expression_block)

@spec parse_expression!(String.t()) :: Keyword.t()

prewalk(ast, fun)

See Macro.prewalk/2.

stringify(items)

@spec stringify([expression_type()] | expression_type()) :: String.t()

Convert an Expression type into a string.

This function is applied to all values when Expression.evaluate_as_string!/3 is called.

time_struct?(value)

@spec time_struct?(String.t() | Time.t()) :: boolean()

traverse(ast, acc, pre, post)

See Macro.traverse/4.