NAME
Data::Annotation::Expression
SYNOPSIS
use Data::Annotation::Expression qw< evaluator_factory >;
my $evaluator = evaluator_factory(\%definition, \%parse_context);
my $retval = $evaluator->($data);
DESCRIPTION
This module exports "evaluator_factory", a factory function that produces sub references based on an expression definition and additional context. These sub references can later be used to evaluate the expression based on additional data that is provided as input.
Example
To make an example, suppose that we have the following target function (from a behavioural point of view, at least):
my $expression = sub ($data) {
return ($data->{foo} eq 'bar')
&& ($data->{baz} ne $data->{galook});
}
It's a boolean expression that makes use of some parts taken from $data
(keys foo
, baz
, and baz
) as well as a constant (string bar
), also leveraging boolean operators like &&
and comparison operators like eq
and ne
.
The first step is to represent the expression as data, e.g. in YAML it would be:
and:
- eq: [ '.foo', '=bar' ]
- ne: [ '.baz', '.galook' ]
In this module's conventions, strings starting with a dot represent access to the input data (like .foo
), while strings starting with an equal sign represent verbatim data (like =baz
). When we have this expression parsed as a Perl data structure, we can generate our equivalent function:
my $equivalent_expression = evaluator_factory($definition);
Definition format(s)
This section will use YAML format to represent data structure, although this module does only accept Perl data structures (i.e. parsing of YAML or any other representation format is supposed to be performed elsewhere).
A definition is a hierarchical data structure composed of nodes, that are hash references, with some specific fields that indicate what the node does. Some of these nodes represent functions/operators that take parameters, in the form of array references.
In its most verbose form, the representation is very explicit, like this that represents $input->{foo} eq 'bar'
:
type: sub
name: eq
args:
- type: context
path: run.foo
- type: data
value: bar
There are three node type
s:
sub
: indicate a sub or an operator. This will be looked around based on itsname
and invoked with the providedargs
, after they have been evaluated. It is also possible to set apackage
where thename
function will be searched, otherwise the "Parse context" will be used.data
: indicate verbatim data, provided asvalue
. This can itself be some complicated data structure, but it will be used as-is.context
: indicate data that has to be taken from the available context, using a path that is evaluated using Data::Annotation::Traverse functions. The top-most key in the path can berun
, meaning whatever is passed to the evaluation function at runtime, as well asparse
, meaning the "Parse context", as well asdefinition
, which means the expression definition itself. Most of the times you will probably want to userun
, possiblyparse
(to keep some less-dynamic data like configurations), almost neverdefinition
(it's there mostly for debugging reasons).
The fully explicit form above is the normal form. As it is quite verbose, it's possible to use shortcuts as we already saw in the example. First of all, plain strings can serve as expressions, like this:
&sub_name --> { type: sub, name: sub_name, args: [] }
=foo_bar --> { type: data, value: foo_bar }
run.place --> { type: context, path: run.place }
.place --> { type: context, path: run.place }
Then, we can have hash-references that contain a single key/value pair (like in the first examples). Keys data
, sub
, and context
do the right thing much like the string versions above; other keys always lead to sub
nodes, where it's possible to also specify the package
by setting the value to a fully qualified Perl function (i.e. including the package with the usual ::
notation).
Parse context
When calling "evalutor_function" it's possible to pass an optional second parameter with a hash reference of options. These are used to set the behaviour of parsing, as well as some runtime behaviour (e.g. where to find functions or retrieving values from the "context").
The normalization process described in the previous section can be completely overridden by passing option definition-normalizer
, which is a sub reference with the following signature:
sub normalizer ($parse_ctx, $definition) --> $normalized_definition
You can find the default one in the Data::Annotation::Expression code (function default_definition_normalizer
), which does what explained before.
The parse context can be used at runtime to retrieve values using a context
node type. In this case, the path must start with string parse
.
The parse context is also used in sub
node types. In particular, key locator-relative-prefixes
is (if present) an array reference holding a list of package prefixes that will be tried to find each function. As an example, suppose that it is set like this:
locator-relative-prefixes:
- Foo::Bar
- Baz::Galook
The following applies:
{ type: sub, name: blorg }
candidates: package Foo::Bar function blorg
package Baz::Galook function blorg
{ type: sub, name: blorg, package: Barf }
candidates: package Foo::Bar::Barf function blorg
package Baz::Galook::Barf function blorg
{ type: sub, name: blorg, package: '/Barf' }
candidates: package Barf function blorg
Builtin functions
This module comes with a set of wrappers around Perl most common operators which are used as default built-ins available in expressions. These are contained in Data::Annotation::Expression::Builtin and this module is injected by default inside "Parse context" option locator-relative-prefixes
.
It is possible to avoid the injection of these built-ins by passing a true value to "Parse context" option no-builtin
when calling the factory function.
INTERFACE
evaluator_factory
my $sub1 = evaluator_factory(\%definition); # OR
my $sub2 = evaluator_factory(\%definition, \%parse_context);
Generate an evaluator function based on a %definition
and optional %parse_context
. See "DESCRIPTION" for everything.
ANYTHING ELSE (INCLUDING AUTHOR, COPYRIGHT AND LICENSE)
See documentation for Data::Annotation.