Expr.pm
NAME
Communiware::Expr - Communiware expression interpreter
SYNOPSIS
($code, $type, $is_constant) = new Communiware::Expr( atomparser=>sub{$tpl_frag->param(@_)} )->compile_expr(\@expr);
$result = eval $code;
DESCRIPTION
This module compiles and evaluates arithmetic and logical expressions used in Communiware templates. Expressions are supported in the dynamic elements which explicitly allow them.
Each operand and operator of expression must be passed to dynamic element as separate parameter.
Expressions are interpreted as lists of strings, so if you want to use attribute values, you should use either visualisation time '@' or submit time (in Use, Check, Item) '%' substitution.
Type of the value is derived either from the ontology at compile time, or from
its format. Any data can participate in operation, where string operand is
required, data should look like number to participate in number operation, and
should be in standard Communiware date format ``YYYY.MM.DD HH:MI:SS
''.
Date operations are guaranteed to work only in the range between 1.01.1970 and 17.01.2038.
Some dynamic elements (i.e. If) can do some preprocessing on expressions thus allowing for example compare statuses for greater/less.
ARITHMETIC EXPRESSIONS
Arithmetic expressions are allowed in the dynamic elements Subst, Use and Define.
OPERATIONS
Following operations are supported by subst
- .
- string concatenation
- x
- string multiplication. Left operand should be a string, and right operand should be an integer. String would be repeated n times
- %
-
Operand formatting. Left argument is value of type number, date, string or
richtext, and right is format specification. For numbers and strings any of
printf formats are acceptable, for dates - any of formats, acceptable for
strftime, for strings - any of formats, acceptable by
Communiware::Format::Text::ToInternal
. For richtext the options allowed byCommuniware::Format::untag_html
are provided. Values are html-escaped after formatting. - /
- Numeric (floating-point) division
- mod
- Modulo operation. Returns reminder of division
- div
- Integer division
- +
- Numeric/date addition
-
For date addition left operand should be date, and right - real number, interpreted as number of days. Date is distinguished from number by presence of more than one point (YYYY.MM.DD HH:MI:SS).
- -
- Numeric/date substraction
- ()
- Used for grouping of operations
-
Numeric multiplication. Both operands should be numbers.
Operation priorities
1: * x 2: + - 3: % 4: . =head1 LOGICAL EXPRESSIONS
Logical expressions evaluate to the true and false value, and used in the dynamic elements where certain decision have to be made, i.e If, Check, Item.
They consist of list of elementary condition, joined by logical and and or operators (with possible grouping with parentheses)
Logical expressions are short-evaluated. I.e. if in the some point of evaluation it becomes clear, that following condition wouldn't affect result (i.e. left operand of && is evaluated to false or left operand of || is evaluated to true), rest of expression is not evaluated.
This allows to speed up complex conditions if they contain expensive operations, such as filter invocation.
LOGICAL OPERATORS
There are three logical operators supported (listed in order of priority):
- !
- Logical not. Negates following condition
- &&
- Logical and. True if and only if both operands evaluate to true
- ||
- Logical or. False if and only if both operands evaluate to false
Operands of logical operators can be ether elementary conditions, or parenthized logical expressions.
Conditions
There are following types of conditions:
- value
- False if value is empty string or numeric zero. True otherwise
- value = value
- True if both values are equal. Numeric comparation is performed if both operats look like number, string comparation otherwise
- value != value
- value <> value
- True if values are not equal.
- value < value
- value <= value
- value > value
- value >= value
- Various froms of numeric and string comparation
- value ~ value
- Regular expression matching. True if left operand matches regular expression in right operand. No special synax like // required for regular expression. Matching modifiers are not supported.
-
Such features of perl regular expressions as character classes \w \s etc and zero-width assertions are supported.
- value !~ value
- Negative matching. True if left operand doesn't match regular expression in the right operand.
- value IN filter specification
- True if value is among set of values returned by filter. By default, value of ITEM_ID attribute returned by filter is checked.
-
<value> can have form
-
attrvalue:ATTRNAME
-
In this case
attrvalue
will compare not with ITEM_ID, but with ATTRNAME where ATTRNAME is one of possible items attributes. For example if we want to check if there are already an author with given real name we can use expression -
%TITLE:TITLE IN {ITEM_TYPE='AUTHOR'}
- value :=: value
- List comparasion. Both operands should be comma-separated lists of values. Evaluates to true if lists contain same elements, regardless of order. Duplicates and empty elements are not counted.
INTERNALS
new
$expr = new Communiware::Expr(atomparser => sub { $tpl->param(@_) });
Creates new expression object. 'atomparser'
parameter is used to provide it
with an atom parser callback (usually to template fragment's param()
or to
posting context's subst_param()
method).
throw
throw("sprintf format", @args);
Throws an exception in a form suitable for handling both in runtime and while compilation. Please use english messages in format.
compile_expr
$code = compile_expr(\@list); ($code, $type, $is_constant) = compile_expr(\@list);
Compiles Communiware expression into perl expression. Compilation stops at first place where operand is followed by non-operator, and remaining list elements are left in list.
Operands are compiled using atomparser closure passed to constructor.
If some of expression operands is scalar reference rather than scalar, it is assumed that it contains already compiled perl expression, not an argument for param method. If it is an array reference, it is assumed to have all the three components - code, type and constantness.
compile_operator
($code, $type, $is_constant) = $expr->compile_operator(\@expr, $level);
Compiles start of @expr as an operand of operator of level $level (lower
precedence means lower $level). Uses @OPERATORS for a list of operators on each
level. Each element of @OPERATORS is an anonymous hash. Its key may be either
empty string, or 'operator'
, or 'operator(type)(type)'
. The
first form determines arity. 'unary'
is interpreted as unary operator,
defaults to binary. The third form (number of types corresponds to arity)
determines behavior of this operator if operands are of specified Communiware
types (use 'UNDEF'
for type that is unknown at compile time). The second
form is a fallback. The value of second and third forms is an anonymous array
whose first element is either a sprintf format (code is generated by
sprintf($this_format, @operand_codes)
) or a function reference (code
is generated by $this_ref-
(@operand_codes)>), and whose second element
is result type.
gen_polymorph_op2
@list = gen_polymorph_op2($op);
Returns list for @OPERATORS' element, describing operator whose meaning varies depending on types of arguments, such as '<lt>' or '='. These operators are interpreted as number comparison if both arguments are numbers, string comparison otherwise.
At compilation time we sometimes don't know types in advance. If we cannot determine perl operator at compile time, we generate call to ``intellectual comparison'' function.
That is
gen_polymorph_op2('=')
returns something like
'=(NUMBER)(NUMBER)' => ['%s == %s', 'STRING'], '=(UNDEF)(NUMBER)' => [q(Communiware::Expr::compare2('=',%s,%s)), 'STRING'], '=(NUMBER)(UNDEF)' => [q(Communiware::Expr::compare2('=',%s,%s)), 'STRING'], '=(UNDEF)(UNDEF)' => [q(Communiware::Expr::compare2('=',%s,%s)), 'STRING'], '=' => ['%s eq %s', 'STRING'],
compare2
Communiware::Expr::compare2($operator, $arg1, $arg2)
Makes ``intellectual compare'' of its arguments with given operator. That is, if both arguments look like numbers, it is numeric comparison, otherwise it is string comparison.
add_date
adds given number of days to the date
subtract_dates
returns number of day between two dates
quote
$string = quote($string); $string = quote($string,qr/\\\@/);
Converts string into properly-quoted perl string constant. If second-argument is non null, it should be regular expression, which matches thing, which should be displayed as at-sign.