Expression.Callbacks (expression v0.2.1)
The function callbacks for the standard function set available in FLOIP expressions.
This should be relatively swappable with another implementation.
The only requirement is the handle/3
function.
FLOIP functions are case insensitive. All functions in this callback module are implemented as lowercase names.
Some functions accept a variable amount of arguments. Elixir doesn't support variable arguments in functions.
If a function accepts a variable number of arguments the convention
is to call the <function_name>_vargs/2
callback where the context
is given as the first argument and the argument list as a second
argument.
Reserved names such as and
, if
, and or
are suffixed with an
underscore.
Link to this section Summary
Functions
Returns the absolute value of a number
Returns TRUE if and only if all its arguments evaluate to TRUE
Convert a string function name into an atom meant to handle that function
Returns the character specified by a number
Removes all non-printable characters from a text string
Returns a numeric code for the first character in a text string
Joins text strings into one text string
Defines a new date value
Converts date stored in text to an actual date,
using strftime
formatting.
Returns only the day of the month of a date (1 to 31)
Moves a date by the given number of months
Returns the first word in the given text - equivalent to WORD(text, 1)
Formats the given number in decimal format using a period and commas
Handle a function call while evaluating the AST.
Returns only the hour of a datetime (0 to 23)
Returns one value if the condition evaluates to TRUE, and another value if it evaluates to FALSE
Returns TRUE if the argument is a boolean.
Returns TRUE if the argument is a number.
Returns TRUE if the argument is a string.
Returns the first characters in a text string
Returns the number of characters in a text string
Converts a text string to lowercase
Returns the maximum value of all arguments
Returns the minimum value of all arguments
Returns only the minute of a datetime (0 to 59)
Returns only the month of a date (1 to 12)
Returns the current date time as UTC
Returns TRUE if any argument is TRUE
Formats a number as a percentage
Returns the result of a number raised to a power - equivalent to the ^ operator
Capitalizes the first letter of every word in a text string
Formats digits in text for reading in TTS
Removes the first word from the given text. The remaining text will be unchanged
Repeats text a given number of times
Returns the last characters in a text string
Returns only the second of a datetime (0 to 59)
Substitutes new_text for old_text in a text string. If instance_num is given, then only that instance will be substituted
Returns the sum of all arguments, equivalent to the + operator
Defines a time value which can be used for time arithmetic
Converts time stored in text to an actual time
Returns the current date
Returns the unicode character specified by a number
Returns a numeric code for the first character in a text string
Converts a text string to uppercase
Returns the day of the week of a date (1 for Sunday to 7 for Saturday)
Extracts the nth word from the given text string. If stop is a negative number, then it is treated as count backwards from the end of the text. If by_spaces is specified and is TRUE then the function splits the text into words only by spaces. Otherwise the text is split by punctuation characters as well
Returns the number of words in the given text string. If by_spaces is specified and is TRUE then the function splits the text into words only by spaces. Otherwise the text is split by punctuation characters as well
Extracts a substring of the words beginning at start, and up to but not-including stop. If stop is omitted then the substring will be all words from start until the end of the text. If stop is a negative number, then it is treated as count backwards from the end of the text. If by_spaces is specified and is TRUE then the function splits the text into words only by spaces. Otherwise the text is split by punctuation characters as well
Returns only the year of a date
Link to this section Functions
abs(ctx, number)
Returns the absolute value of a number
The absolute value of -1 is @ABS(-1)
Example
iex> Expression.Callbacks.abs(%{}, -1)
1
and_vargs(ctx, arguments)
Returns TRUE if and only if all its arguments evaluate to TRUE
@AND(contact.gender = "F", contact.age >= 18)
Example
iex> Expression.Callbacks.handle("and", [true, true], %{})
{:ok, true}
iex> Expression.Callbacks.and_vargs(%{}, [true, true])
true
iex> Expression.Callbacks.and_vargs(%{}, [true, false])
false
iex> Expression.Callbacks.and_vargs(%{}, [false, false])
false
atom_function_name(function_name)
Convert a string function name into an atom meant to handle that function
Reserved words such as and
, if
, and or
are automatically suffixed
with an _
underscore.
char(ctx, code)
Returns the character specified by a number
As easy as @CHAR(65), @CHAR(66), @CHAR(67)
Example
iex> Expression.Callbacks.char(%{}, 65)
"A"
clean(ctx, binary)
Removes all non-printable characters from a text string
You entered @CLEAN(step.value)
Example
iex> Expression.Callbacks.clean(%{}, <<65, 0, 66, 0, 67>>)
"ABC"
code(ctx, arg)
Returns a numeric code for the first character in a text string
The numeric code of A is @CODE("A")
Example
iex> Expression.Callbacks.code(%{}, "A")
65
concatenate_vargs(ctx, arguments)
Joins text strings into one text string
Your name is @CONCATENATE(contact.first_name, " ", contact.last_name)
Example
iex> Expression.Callbacks.handle("concatenate", ["name", " ", "surname"], %{})
{:ok, "name surname"}
date(ctx, year, month, day)
Defines a new date value
This is a date @DATE(2012, 12, 25)
Example
iex> to_string(Expression.Callbacks.date(%{}, 2012, 12, 25))
"2012-12-25 00:00:00Z"
datevalue(ctx, date, format \\ "%Y-%m-%d %H:%M:%S")
Converts date stored in text to an actual date,
using strftime
formatting.
It will fallback to "%Y-%m-%d %H:%M:%S" if no formatting is supplied
You joined on @DATEVALUE(contact.joined_date, "%Y-%m%-d")
Example
iex> date = Expression.Callbacks.date(%{}, 2020, 12, 20)
iex> Expression.Callbacks.datevalue(%{}, date)
"2020-12-20 00:00:00"
iex> Expression.Callbacks.datevalue(%{}, date, "%Y-%m-%d")
"2020-12-20"
day(ctx, date)
Returns only the day of the month of a date (1 to 31)
The current day is @DAY(contact.joined_date)
Example
iex> now = DateTime.utc_now()
iex> day = Expression.Callbacks.day(%{}, now)
iex> day == now.day
true
edate(ctx, date, months)
Moves a date by the given number of months
Next month's meeting will be on @EDATE(date.today, 1)
Example
iex> now = DateTime.utc_now()
iex> future = Timex.shift(now, months: 1)
iex> date = Expression.Callbacks.edate(%{}, now, 1)
iex> future == date
true
first_word(ctx, binary)
Returns the first word in the given text - equivalent to WORD(text, 1)
The first word you entered was @FIRST_WORD(step.value)
Example
iex> Expression.Callbacks.first_word(%{}, "foo bar baz")
"foo"
fixed(ctx, number, precision, no_commas \\ false)
Formats the given number in decimal format using a period and commas
You have @FIXED(contact.balance, 2) in your account
Example
iex> Expression.Callbacks.fixed(%{}, 4.209922, 2, false)
"4.21"
iex> Expression.Callbacks.fixed(%{}, 4000.424242, 4, true)
"4,000.4242"
iex> Expression.Callbacks.fixed(%{}, 3.7979, 2, false)
"3.80"
iex> Expression.Callbacks.fixed(%{}, 3.7979, 2)
"3.80"
handle(function_name, arguments, context)
Specs
handle(function_name :: binary(), arguments :: [any()], context :: map()) :: {:ok, any()} | {:error, :not_implemented}
Handle a function call while evaluating the AST.
Handlers in this module are either:
- The function name as is
- The function name with an underscore suffix if the function name is a reserved word
- The function name suffixed with
_vargs
if the takes a variable set of arguments
hour(ctx, date)
Returns only the hour of a datetime (0 to 23)
The current hour is @HOUR(NOW())
Example
iex> now = DateTime.utc_now()
iex> hour = Expression.Callbacks.hour(%{}, now)
iex> now.hour == hour
true
if_(ctx, condition, yes, no)
Returns one value if the condition evaluates to TRUE, and another value if it evaluates to FALSE
Dear @IF(contact.gender = "M", "Sir", "Madam")
Example
iex> Expression.Callbacks.handle("if", [true, "Yes", "No"], %{})
{:ok, "Yes"}
iex> Expression.Callbacks.handle("if", [false, "Yes", "No"], %{})
{:ok, "No"}
isbool(ctx, var)
Returns TRUE if the argument is a boolean.
@ISBOOL(block.value) will return TRUE if the block returned a boolean value.
Example
iex> Expression.Callbacks.isbool(%{}, true)
true
iex> Expression.Callbacks.isbool(%{}, false)
true
iex> Expression.Callbacks.isbool(%{}, 1)
false
iex> Expression.Callbacks.isbool(%{}, 0)
false
iex> Expression.Callbacks.isbool(%{}, "true")
false
iex> Expression.Callbacks.isbool(%{}, "false")
false
isnumber(ctx, var)
Returns TRUE if the argument is a number.
@ISNUMBER(contact.age) will return TRUE if the contact's age is a number.
Example
iex> Expression.Callbacks.isnumber(%{}, 1)
true
iex> Expression.Callbacks.isnumber(%{}, 1.0)
true
iex> Expression.Callbacks.isnumber(%{}, Decimal.new("1.0"))
true
iex> Expression.Callbacks.isnumber(%{}, "1.0")
true
iex> Expression.Callbacks.isnumber(%{}, "a")
false
isstring(ctx, binary)
Returns TRUE if the argument is a string.
@ISSTRING(contact.name) will return TRUE if the contact's name is a string.
Example
iex> Expression.Callbacks.isstring(%{}, "hello")
true
iex> Expression.Callbacks.isstring(%{}, false)
false
iex> Expression.Callbacks.isstring(%{}, 1)
false
iex> Expression.Callbacks.isstring(%{}, Decimal.new("1.0"))
false
left(ctx, binary, size)
Returns the first characters in a text string
You entered PIN @LEFT(step.value, 4)
Example
iex> Expression.Callbacks.left(%{}, "foobar", 4)
"foob"
len(ctx, binary)
Returns the number of characters in a text string
You entered @LEN(step.value) characters
Example
iex> Expression.Callbacks.len(%{}, "foo")
3
iex> Expression.Callbacks.len(%{}, "zoë")
3
lower(ctx, binary)
Converts a text string to lowercase
Welcome @LOWER(contact)
```
# Example
iex> Expression.Callbacks.lower(%{}, "Foo Bar")
"foo bar"
max_vargs(ctx, arguments)
Returns the maximum value of all arguments
Please complete at most @MAX(flow.questions, 10) questions
Example
iex> Expression.Callbacks.handle("max", [1, 2, 3], %{})
{:ok, 3}
min_vargs(ctx, arguments)
Returns the minimum value of all arguments
Please complete at least @MIN(flow.questions, 10) questions
Example
iex> Expression.Callbacks.handle("min", [1, 2, 3], %{})
{:ok, 1}
minute(ctx, date)
Returns only the minute of a datetime (0 to 59)
The current minute is @MINUTE(NOW())
Example
iex> now = DateTime.utc_now()
iex> minute = Expression.Callbacks.minute(%{}, now)
iex> now.minute == minute
true
month(ctx, date)
Returns only the month of a date (1 to 12)
The current month is @MONTH(NOW())
Example
iex> now = DateTime.utc_now()
iex> month = Expression.Callbacks.month(%{}, now)
iex> now.month == month
true
now(ctx)
Returns the current date time as UTC
It is currently @NOW()
Example
iex> DateTime.utc_now() == Expression.Callbacks.now(%{})
or_vargs(ctx, arguments)
Returns TRUE if any argument is TRUE
@OR(contact.state = "GA", contact.state = "WA", contact.state = "IN")
Example
iex> Expression.Callbacks.handle("or", [true, false], %{})
{:ok, true}
iex> Expression.Callbacks.handle("or", [true, true], %{})
{:ok, true}
iex> Expression.Callbacks.handle("or", [false, false], %{})
{:ok, false}
percent(ctx, float)
Specs
percent(Expression.Context.t(), float()) :: binary()
percent(Expression.Context.t(), binary()) :: binary()
percent(Expression.Context.t(), Decimal.t()) :: binary()
Formats a number as a percentage
You've completed @PERCENT(contact.reports_done / 10) reports
Example
iex> Expression.Callbacks.percent(%{}, 2/10)
"20%"
iex> Expression.Callbacks.percent(%{}, "0.2")
"20%"
iex> Expression.Callbacks.percent(%{}, Decimal.new("0.2"))
"20%"
power(ctx, a, b)
Returns the result of a number raised to a power - equivalent to the ^ operator
2 to the power of 3 is @POWER(2, 3)
proper(ctx, binary)
Capitalizes the first letter of every word in a text string
Your name is @PROPER(contact)
Example
iex> Expression.Callbacks.proper(%{}, "foo bar")
"Foo Bar"
read_digits(ctx, binary)
Formats digits in text for reading in TTS
Your number is @READ_DIGITS(contact.tel_e164)
Example
iex> Expression.Callbacks.read_digits(%{}, "+271")
"plus two seven one"
remove_first_word(ctx, binary, separator \\ " ")
Removes the first word from the given text. The remaining text will be unchanged
You entered @REMOVE_FIRST_WORD(step.value)
Example
iex> Expression.Callbacks.remove_first_word(%{}, "foo bar")
"bar"
iex> Expression.Callbacks.remove_first_word(%{}, "foo-bar", "-")
"bar"
rept(ctx, value, amount)
Repeats text a given number of times
Stars! @REPT("*", 10)
Example
iex> Expression.Callbacks.rept(%{}, "*", 10)
"**********"
right(ctx, binary, size)
Returns the last characters in a text string
Your input ended with ...@RIGHT(step.value, 3)
Example
iex> Expression.Callbacks.right(%{}, "testing", 3)
"ing"
second(ctx, date)
Returns only the second of a datetime (0 to 59)
The current second is @SECOND(NOW())
Example
iex> now = DateTime.utc_now()
iex> second = Expression.Callbacks.second(%{}, now)
iex> now.second == second
true
substitute(map, subject, pattern, replacement)
Substitutes new_text for old_text in a text string. If instance_num is given, then only that instance will be substituted
@SUBSTITUTE(step.value, "can't", "can")
Example
iex> Expression.Callbacks.substitute(%{}, "I can't", "can't", "can do") "I can do"
sum_vargs(ctx, arguments)
Returns the sum of all arguments, equivalent to the + operator
You have @SUM(contact.reports, contact.forms) reports and forms
Example
iex> Expression.Callbacks.handle("sum", [1, 2, 3], %{})
{:ok, 6}
time(ctx, hours, minutes, seconds)
Defines a time value which can be used for time arithmetic
2 hours and 30 minutes from now is @(date.now + TIME(2, 30, 0))
Example
iex> Expression.Callbacks.time(%{}, 12, 13, 14)
%Time{hour: 12, minute: 13, second: 14}
timevalue(ctx, expression)
Converts time stored in text to an actual time
Your appointment is at @(date.today + TIME("2:30"))
Example
iex> Expression.Callbacks.timevalue(%{}, "2:30")
%Time{hour: 2, minute: 30, second: 0}
iex> Expression.Callbacks.timevalue(%{}, "2:30:55")
%Time{hour: 2, minute: 30, second: 55}
today(ctx)
Returns the current date
Today's date is @TODAY()
Example
iex> today = Date.utc_today()
iex> today == Expression.Callbacks.today(%{})
true
unichar(ctx, code)
Returns the unicode character specified by a number
As easy as @UNICHAR(65), @UNICHAR(66) , @UNICHAR(67)
Example
iex> Expression.Callbacks.unichar(%{}, 65) "A" iex> Expression.Callbacks.unichar(%{}, 233) "é"
unicode(ctx, arg)
Returns a numeric code for the first character in a text string
The numeric code of A is @UNICODE("A")
Example
iex> Expression.Callbacks.unicode(%{}, "A")
65
iex> Expression.Callbacks.unicode(%{}, "é")
233
upper(ctx, binary)
Converts a text string to uppercase
WELCOME @UPPER(contact)!!
Example
iex> Expression.Callbacks.upper(%{}, "foo")
"FOO"
weekday(ctx, date)
Returns the day of the week of a date (1 for Sunday to 7 for Saturday)
Today is day no. @WEEKDAY(TODAY()) in the week
Example
iex> today = DateTime.utc_now()
iex> expected = Timex.weekday(today)
iex> weekday = Expression.Callbacks.weekday(%{}, today)
iex> weekday == expected
true
word(ctx, binary, n, by_spaces \\ false)
Extracts the nth word from the given text string. If stop is a negative number, then it is treated as count backwards from the end of the text. If by_spaces is specified and is TRUE then the function splits the text into words only by spaces. Otherwise the text is split by punctuation characters as well
Example
iex> Expression.Callbacks.word(%{}, "hello cow-boy", 2)
"cow"
iex> Expression.Callbacks.word(%{}, "hello cow-boy", 2, true)
"cow-boy"
iex> Expression.Callbacks.word(%{}, "hello cow-boy", -1)
"boy"
word_count(ctx, binary, by_spaces \\ false)
Returns the number of words in the given text string. If by_spaces is specified and is TRUE then the function splits the text into words only by spaces. Otherwise the text is split by punctuation characters as well
You entered @WORD_COUNT(step.value) words
Example
iex> Expression.Callbacks.word_count(%{}, "hello cow-boy")
3
iex> Expression.Callbacks.word_count(%{}, "hello cow-boy", true)
2
word_slice(ctx, binary, start)
Extracts a substring of the words beginning at start, and up to but not-including stop. If stop is omitted then the substring will be all words from start until the end of the text. If stop is a negative number, then it is treated as count backwards from the end of the text. If by_spaces is specified and is TRUE then the function splits the text into words only by spaces. Otherwise the text is split by punctuation characters as well
Example
iex> Expression.Callbacks.word_slice(%{}, "RapidPro expressions are fun", 2, 4)
"expressions are"
iex> Expression.Callbacks.word_slice(%{}, "RapidPro expressions are fun", 2)
"expressions are fun"
iex> Expression.Callbacks.word_slice(%{}, "RapidPro expressions are fun", 1, -2)
"RapidPro expressions"
iex> Expression.Callbacks.word_slice(%{}, "RapidPro expressions are fun", -1)
"fun"
word_slice(ctx, binary, start, stop, by_spaces \\ false)
year(ctx, date)
Returns only the year of a date
The current year is @YEAR(NOW())
Example
iex> %{year: year} = now = DateTime.utc_now()
iex> year == Expression.Callbacks.year(%{}, now)