-
Notifications
You must be signed in to change notification settings - Fork 1
Macro
A macro is a function that maps a set of inputs to an output. Macros work in ways similar to a function (in writing, a macro call is indistinguishable from a function call). In arithmexp
, a macro consists of the following attributes:
- Identifier: A unique name given to the macro
-
Base: A
Closure
that defines the macro call's signature and acts as a fallback function call -
Resolver: A
Closure
that takes in an array of tokens as its input arguments, and returns an array of tokens, or null to fallback to a function call -
Flags: A
FunctionFlags
bitmask (learn more about function flags on the function page)
Similar to a function call, the identifier property is used when executing a macro call. A macro's resolver is executed during the parsing stage, and returns an array of replacement tokens to substitute the macro call with, or alternatively null
to substitute the macro call with a function call to its base function.
A macro may be registered for a given parser before expressions are parsed by invoking FunctionRegistry::registerMacro()
.
$registry = $parser->getFunctionRegistry();
$registry->registerMacro(
"sum",
fn(int|float ...$nums) : int|float => array_sum($nums),
function(Parser $parser, string $expression, FunctionCallToken $token, array $args) : ?array{
if(count($args) === 0){
throw ParseException::unresolvableFcallTooLessParams($expression, $token->getPos(), 1, 0);
}
if(count($args) === 1){ // resolve "sum(x)" to "x"
return [$args[0]];
}
if(count($args) === 2){ // resolve "sum(x, y)" to "x + y"
return [
$args[0],
$args[1],
new BinaryOperatorToken($token->getPos(), "+")
];
}
return null; // replace macro with an fcall to the base function (`array_sum($nums)`)
}
);
$vars = ["x" => 1, "y" => 2];
$parser->parse("sum(x, y)")->evaluate($vars); // int(3)
$parser->parse("sum(sum(x, y))")->evaluate($vars); // int(3)
$parser->parse("sum(sum(x, y), sum(x), sum(y))")->evaluate($vars); // int(6)
The capability of macros to manipulate tokens within a macro-call make macros powerful. As such, macros can reduce the evaluation overhead by preprocessing tokens and shaping how they must be evaluated. Furthermore, optimizers can more effectively apply their methods when a macro such as sqrt(x) => x ** 0.5
is executed (with optimizations, sqrt(x) / (x ** 0.5)
gets resolved to a ConstantExpression(value: 1.0)
). However, defining a macro is more complex than a simple function.
- The return value of the resolver must either be
null
or a non-empty array. - The return value of the resolver must be a postfix-formatted array of tokens (all available tokens are listed under the muqsit/arithmexp/token namespace)
// return: 2 * 3 return [ new NumericLiteralToken($token->getPos(), 2), new NumericLiteralToken($token->getPos(), 3), new BinaryOperatorToken($token->getPos(), "*") ];
- Parenthesis token must not be used. As the resulting array uses a postfix notation, parentheses are not needed.
Try out arithmexp
on the demo site!
1.1. Installation
2.1. Constant
2.2. Function
2.3. Macro
2.4. Operator
2.5. Variable
2.6. Expression Optimization
2.7. Implementation Internals