Skip to content

Function

Muqsit Rayyan edited this page Jul 12, 2024 · 23 revisions

A function is identified by its unique name. Every function has any non-negative number of inputs and a numeric or boolean output. The inputs and outputs are either a float type, an int type or a bool type (i.e., int|float|bool).

Function Definition

A function may be registered for a given parser before expressions are parsed by invoking FunctionRegistry::registerFunction().

$registry = $parser->function_registry;
$registry->registerFunction("intp1", static fn(int $x) : int => $x + 1);
$registry->registerFunction("num2", static fn(float $x) : float => $x * 2);
$registry->registerFunction("dist2d", static fn(float $x1, float $y1, float $x2, float $y2) : float => sqrt($x1 * $y1 + $x2 * $y2));
$registry->registerFunction("is_valid_level", static fn(int $level) : bool => $level >= 1 && $level <= 5);

$parser->parse("intp1(2)")->evaluate(); // 3
$parser->parse("num2(num2(2))")->evaluate(); // 8
$parser->parse("dist2d(2, 3, 4, 5)")->evaluate(); // 5.0990195135927845
$parser->parse("is_valid_level(4)")->evaluate(); // true

Functions accept any number of numeric or boolean inputs (including a varying number of inputs (variadic)), and return a numeric or a boolean output. Below are some examples of valid function signatures:

  • fn() : int
  • fn() : float
  • fn() : int|float
  • fn() : int|float|bool
  • fn(int $x, int|float $y) : int
  • fn(int ...$values) : float
  • fn(int $x, int $y, float ...$values) : float
  • fn(int|bool $value) : float

Commutative Functions

A function may be registered as commutative, meaning it yields the same output regardless of the order of input parameters. For example, the built-in function min is registered as commutative because min(1, 2, 3) === min(3, 1, 2) === min(1, 3, 2).... The commutative property is an optimization hint which allows the optimizer to precompute a value such as min(x, y) - min(y, x) into 0 during the parsing stage. To define a commutative function, the flag FunctionFlags::COMMUTATIVE must be set at the time of registration.

$registry->registerFunction("sum", static fn(int|float $x, int|float $y) : int|float => $x + $y, FunctionFlags::COMMUTATIVE);

Deterministic Functions

A function may be registered as deterministic so that it is expected to return the same result for the same set of inputs (the meaning of deterministic here is the same as it's meaning in the context of SQL). To define a deterministic function, the flag FunctionFlags::DETERMINISTIC must be set at the time of registration.

$registry->registerFunction("sum", static fn(int|float $x, int|float$y) : int|float=> $x + $y, FunctionFlags::COMMUTATIVE | FunctionFlags::DETERMINISTIC);

Function calls to deterministic functions having deterministic inputs are computed during the parsing stage rather than the evaluation stage. Some of the example instances of this optimization are listed below:

  • The expression max(sum(2, 3), sum(4, 5)) is pre-computed as 9. The functions max() and sum() are invoked only during the parsing stage and no function call occurs during evaluation.
  • The expression max(sum(x, 3), sum(4, 5)) is pre-computed as max(sum(x, 3), 9). The function call sum(4, 5) is resolved during the parsing stage, while the other function calls are resolved during evaluation.
  • No optimization transformation occurs on the expression max(sum(x, y), sum(z, w)).

Idempotent Functions

A function may be registered as idempotent so that applying the same function multiple times on the same parameter has no change on the initially computed result (i.e., f(f(x)) === f(x)). To define an idempotent function, the flag FunctionFlags::IDEMPOTENT must be set at the time of registration.

$registry->registerFunction("modulus", static fn(int|float $x) : int|float => abs($x), FunctionFlags::IDEMPOTENT);

Multiple function calls to idempotent functions are reduced based on their commutative property. If an idempotent function is non-commutative, only instances of f(f(x)) are transformed to f(x) (this also includes f(f(f(x))), f(f(f(f(x)))), ...). Below are some examples on the behaviour of optimizing a non-commutative idempotent function — round():

  • round(round(x)), round(round(round(x))), ... are transformed to round(x)
  • round(round(round(x, 2))) is transformed to round(round(x, 2))
  • No transformation occurs on the expression round(round(x, 2))
  • No transformation occurs on the expression round(round(x), 2)

If an idempotent function is commutative, calls to the same function have their parameter list expanded during transformation. Below are some examples on the behaviour of optimizing a commutative idempotent function — min() (FunctionFlags::COMMUTATIVE | FunctionFlags::IDEMPOTENT):

  • min(min(x)), min(min(min(x))), ... are transformed to min(x)
  • min(min(x, y)) is transformed to min(x, y)
  • min(min(x, y), z, w) is transformed to min(x, y, z, w)
  • min(min(x, y), min(z, w)) is transformed to min(x, y, z, w)

Function Call Syntax

A function call is made by mentioning the function name followed by an open and a close curve bracket containing the function arguments. Function arguments must be separated by a comma, such as fn(), fn(x), fn(x, y), etc. If no argument is supplied following a comma, the function's default value for the parameter is used.

For example, for the given function fn(int $x = 1, int $y = 2) : int => $x + $y:

  • fn() resolves to 3.
  • fn(2) resolves to 4.
  • fn(2, 3) resolves to 5.
  • fn(2, ) resolves to 4.
  • fn(,) resolves to 3.
  • fn(, 1) resolves to 2.

For the given function fn(int $x = 1, int $y) : int => $x + $y:

  • fn() throws a ParseException as the parameter $y does not have a default value.
  • fn(1) throws a ParseException as the parameter $y does not have a default value.
  • fn(1,) throws a ParseException as the parameter $y does not have a default value.
  • fn(, 1) resolves to 2.

List of available functions

Below is a list of functions provided by arithmexp out-of-the-box. These functions are directly ported from PHP's list of math functions and behave the same way.

1. Quick Start

1.1. Installation

2. Documentation Notes

2.1. Constant
2.2. Function
2.3. Macro
2.4. Operator
2.5. Variable
2.6. Expression Optimization
2.7. Implementation Internals

Clone this wiki locally