Expressions
An expression is the part of a tag that works out a value: 1 + 2, $price * $qty, $name | upper, $state in ('open', 'pending'). PAD's evaluator reads it - numbers, strings, fields, lists, operators, functions and pipes - and answers one value. This page goes through all of it; every example is live, its template beside what PAD made of it.
Where expressions stand
An expression is read wherever a tag takes a value: the parameter of {echo} and of {if} and {elseif}, the right side of {set $x = ...}, the value of {case} and its {when}s, the condition of {while} and {until}, a quoted option such as where='...', and an occurrence assignment %name = ... on a tag that loops:
|
PHP |
HTML fragments/expressions_1.pad |
Result |
|---|---|---|
|
Total: 37.5 Free shipping |
|
PHP |
HTML fragments/expressions_2.pad |
Result |
|---|---|---|
|
pen: 12 in stock, order 8 pad: 5 in stock, order 15 |
|
PHP |
HTML fragments/expressions_3.pad |
Result |
|---|---|---|
|
7 is odd 1 2 4 8 16 |
A field tag takes a pipe as it stands - {$name | trim} - and so does the closing tag of a pair, over everything the pair rendered. The pipes page has the opening and closing tag pipes in full:
|
PHP |
HTML fragments/expressions_4.pad |
Result |
|---|---|---|
|
Ada Lovelace PHP SQL PAD |
Values
Numbers
Whole numbers, decimals, a decimal that starts with its point (.5) and exponents (1e3, 2.5E-1). A minus before a number belongs to it. 0x followed by hexadecimal digits is not a number but a string of bytes: 0x41 is the letter A.
| HTML | RESULT |
|---|---|
|
42
|
|
-5
|
|
5
|
|
0.75
|
|
1001
|
|
1
|
|
A
|
|
PAD
|
Strings
Text stands between single or double quotes, which mean the same: a $name inside double quotes stays text, it is not filled in as PHP would. A backslash starts an escape - \n, \r, \t, \\, \' and \"; any other letter after it is an error. The dot joins strings, and numbers with them:
| HTML | RESULT |
|---|---|
|
single quotes
|
|
double quotes
|
|
it's
|
|
say "hi"
|
|
back\slash
|
|
one
|
|
3
|
|
Order 42!
|
Bare words
A word without quotes and without a sigil is looked up. An operator word - eq, and, not ... - is the operator; a PHP constant - TRUE, FALSE, NULL, PHP_EOL, M_PI - is its value; a word that names nothing stands for its own text.
| HTML | RESULT |
|---|---|
|
1
|
|
[] []
|
|
8
|
|
3.1416
|
|
hello
|
|
1
|
Quote text. A bare word that names something PAD knows - a function, a tag, a property, a data set, a field - is that thing: count, name and first are properties, so {echo count} does not write the word count. Text in quotes is always text.
Fields, paths and indirection
$name reads a field: a variable the page's PHP set, one a {set} made, a field of the row a loop stands on. A dot walks into an array - $user.address.city, and $items.0 by position.
|
PHP fragments/expressions_8.php |
HTML fragments/expressions_8.pad |
Result |
|---|---|---|
|
|
Ada from London PHP and PAD LONDON, W1 |
$$name reads the field whose name another field holds - a path as well - the way {$$name} does as a tag. The name must be one of the application's fields; the engine's own pad... variables are never read this way.
|
PHP fragments/expressions_9.php |
HTML fragments/expressions_9.pad |
Result |
|---|---|---|
|
|
Hallo, Ada London |
A field that holds a list is not a value: $items + 1 is an error. Walk the list with a loop, or hand it to a function with the array: prefix, below.
Options, properties and the placeholder
-
#name- a parameter or option of the tag; in a{define}or a custom tag, the ones the tag was called with -
&name- a property of the tag,name@taga property of the tag named -first@items,current@items,count@items(see properties) -
@- the value piped into this part of the expression (see pipes below)
|
PHP |
HTML fragments/expressions_10.pad |
Result |
|---|---|---|
|
121.00 and 109.00 1 of 3: plan 2 of 3: build 3 of 3: ship - the last one |
Lists
[ ] makes a list, its items separated by commas, with keys written key => value where wanted, mixed with plain items as PHP mixes them. A list is what {set} stores, what a loop walks and what a PHP function takes:
|
PHP |
HTML fragments/expressions_11.pad |
Result |
|---|---|---|
|
Minutes: 240 pages 2 3 5 7 1 + 2 + 3 = 6 |
Type prefixes
A prefix says what a name is, so nothing else of that name can be meant: field:, array: (a list as a value), data: (a {data} set), constant:, php: (a PHP function), pad: and app: (a built-in or an application pipe function), function:, tag: and the others the prefix page lists:
|
PHP |
HTML fragments/expressions_12.pad |
Result |
|---|---|---|
|
8 3 3 numbers, together 27 ===== PREFIXES |
Operators
The operators bind in this order, strongest first. Those of one row bind equally and are applied left to right - 10 - 2 + 3 is 11 - except **, which binds right to left. Round brackets group as usual.
| operators | what they do |
|---|---|
**
|
power, right to left: 2 ** 3 ** 2 is 2 ** 9
|
!, a sign - + before a value
|
not, and the signs - weaker than **: -3 ** 2 is -9
|
* / %
|
multiply, divide, remainder |
+ -
|
add, subtract |
.
|
join as text |
lt le gt ge - < <= > >=
|
order |
eq ne - == = != <>
|
equal, not equal |
not
|
denies what follows, a comparison included |
and - &&
|
both hold |
xor
|
exactly one holds |
or - ||
|
either holds |
??
|
the left side, or the right one when the left is empty or missing |
? : and ?:
|
the inline ternary, weakest of all |
The tests in, like, matches, range, between and contains read infix as well; they have a section of their own.
Arithmetic
As in PHP: / answers a decimal when it does not divide evenly, % works on whole numbers. A text that is a number counts as one - '5' + '5' is 10 - a text that is not, 'a' + 1, is an error, and so is a division by zero.
| HTML | RESULT |
|---|---|
|
10
|
|
4
|
|
21
|
|
3.5
|
|
1
|
|
1024
|
|
-5
|
|
3.3333333333333
|
Precedence
| HTML | RESULT |
|---|---|
|
7
|
|
9
|
|
11
|
|
18
|
|
512
|
|
-9
|
|
9
|
|
0.5
|
Joining text
The dot binds weaker than + and -: in 1 + 2 . 3 the sum comes first and is then joined with the 3, and in 1 . 5 + 5 the 1 is joined with 10. After a value a dot is the join, also right against it - 'a'.1 - and only where no value stands before it does a dot start a number.
| HTML | RESULT |
|---|---|
|
Order 42
|
|
Total: 12
|
|
33
|
|
110
|
|
20
|
|
a1
|
Comparing
Each comparison has a word and a symbol: eq or == (a single = too), ne, != or <>, and lt le gt ge or < <= > >=. The words read better in a template full of HTML. Numbers, and texts that are numbers, compare as numbers - '2' lt '10' holds; other texts compare character by character, case counting. PHP's === and !== are not there. A comparison answers 1 or nothing:
| HTML | RESULT |
|---|---|
|
[1] [1] []
|
|
[1] [1] []
|
|
[1] [1] [1]
|
|
[] [1]
|
|
[1] [1]
|
|
[] []
|
Logic
and, or, xor and not, with &&, || and ! as their symbols. The word not binds weaker than the comparisons, so not $a eq $b denies the comparison; ! binds as tightly as a sign, so !$a eq $b compares !$a with $b. and and or stop at a left side that decides: $n gt 0 and 10 / $n gt 1 never divides by zero.
| HTML | RESULT |
|---|---|
|
[] [1]
|
|
[1] []
|
|
[] [1]
|
|
[] [1]
|
|
[1]
|
|
[1]
|
|
[1]
|
A default: ??
a ?? b answers a unless it is empty - '', NULL, FALSE, an empty list - or a field that does not exist; then it answers b. A 0 is a value and stays. A field in front of ?? may be missing, also under the strict check: giving it a default is what the operator is for. The right side is worked out only when it is wanted. In a pipe it takes the piped value, and the default function does the same:
|
PHP fragments/expressions_18.php |
HTML fragments/expressions_18.pad |
Result |
|---|---|---|
|
|
no nickname 0 no surname nothing at all anonymous - anonymous |
The inline ternary
cond ? then : else binds weakest of all: everything before the ? is the condition, and each branch runs to its : or to the end - 0 ? 'yes' : 'no' . '!' joins the '!' to the no. Only the branch chosen is worked out. Ternaries in a row read from the right, a ? b : c ? d : e being a ? b : (c ? d : e). The short form a ?: b answers a itself when it holds, and with nothing before the ? the piped value is the condition:
| HTML | RESULT |
|---|---|
|
many
|
|
M
|
|
M
|
|
Untitled
|
|
Draft
|
|
none
|
|
no!
|
|
PHP |
HTML fragments/expressions_20.pad |
Result |
|---|---|---|
|
1 pen 3 books 0 lamps - sold out |
Truth
Where a condition is wanted - {if}, where=, the ternary, and and or - a value is tested as PHP tests it: '', '0', 0, NULL, FALSE and an empty list are false, every other value is true - the texts '0.0', 'false' and a space among them.
| HTML | RESULT |
|---|---|
|
false
|
|
false
|
|
false
|
|
false
|
|
false
|
|
true
|
|
true
|
|
true
|
|
true
|
|
true
|
The comparisons, the tests and the logic operators answer 1 for true and '' for false: written by {echo} a truth is a 1 or nothing at all, and truths add up as numbers.
| HTML | RESULT |
|---|---|
|
[1] []
|
|
2
|
|
1/
|
|
holds
|
Tests: in, like, matches, range, between, contains
| test | holds when |
|---|---|
$x in (a, b, ...)
|
the value equals one of the list |
$x like 'A%'
|
the value matches the SQL pattern: % any run of characters, _ one character, \\% and \\_ themselves; case is ignored
|
$x matches '/re/'
|
the regular expression matches |
$x range (a, b)
|
a <= value <= b, the ends included |
$x between (a, b)
|
a < value < b, the ends left out |
$x contains y
|
the text holds y - or, for a list, an item of it equals y |
A not between the value and the test denies the test, as SQL writes it: $s not in (...), not like, not matches, not range. The value is everything left of the test - 5 + 1 in (6, 7) tests 6.
| HTML | RESULT |
|---|---|
|
[1] []
|
|
[1]
|
|
[1]
|
|
[1]
|
|
[]
|
like ignores case; _ stands for exactly one character:
| HTML | RESULT |
|---|---|
|
[1] [1]
|
|
[1] []
|
|
[1]
|
|
[1] []
|
A backslash in a PAD string is an escape, so the backslash a regular expression needs is written twice: '/a\\.b/'. A {n} quantifier would open a tag - repeat the class, as below, or let the page's PHP hand the pattern over.
| HTML | RESULT |
|---|---|
|
[1]
|
|
[1] []
|
|
[1] []
|
|
[1]
|
| HTML | RESULT |
|---|---|
|
[1] [1] []
|
|
[] [1]
|
|
[1]
|
|
[1]
|
contains looks into a text, or into a list for an item equal to the one asked for:
| HTML | RESULT |
|---|---|
|
[1] []
|
|
[1]
|
|
[1] []
|
Each test is a pipe function too, so it can stand after a pipe or be called, its value the first argument:
| HTML | RESULT |
|---|---|
|
[1] [1]
|
|
[1] [1]
|
|
[1] [1]
|
|
[1]
|
Together with and and or they make the conditions of the where option, which the data handling page covers with the other options:
|
PHP |
HTML fragments/expressions_29.pad |
Result |
|---|---|---|
|
1: Ada, paid 4: Alice, sent |
Functions
The built-in pipe functions - upper, left, replace, date and the rest of the {functions} list - are used two ways. After a pipe they take the value on its left: $s | left(2). Written as a call with nothing piped in they take it as their first argument: left($s, 2), as PHP's own functions do - so they nest, and stand wherever a value can. The number of parameters is checked.
| HTML | RESULT |
|---|---|
|
ab
|
|
ab
|
|
Hello WORLD
|
|
ABC
|
|
a AND b
|
|
Dutch
|
| HTML | RESULT |
|---|---|
|
bcd
|
|
ef
|
|
example
|
|
creme-brulee-co
|
|
1.5 KB
|
|
a long sentence…
|
|
1.2M
|
|
22nd
|
php:name calls a PHP function, and so does a plain name followed by ( that is no PAD function - ucfirst('word'); the setting $padPhpFunctions decides which PHP functions a template may call. A piped value goes in as the only argument - 'abc' | php:strrev - and into a call with arguments where @ stands: $n | php:number_format(@, 2).
| HTML | RESULT |
|---|---|
|
3
|
|
ababab
|
|
cba
|
|
1,234,567.89
|
|
1.234,50
|
|
007
|
|
9 3
|
|
Word Word
|
A file in the application's _functions/ directory is a pipe function as well, the piped value its $value - this manual has _functions/stars.php. function: asks for whatever function kind the name is, app: for the application's own (see extending):
|
PHP |
HTML fragments/expressions_33.pad |
Result |
|---|---|---|
|
*** well done *** *** well done *** 17 |
Pipes
A | hands the value on its left to what follows; the parts run left to right, each over what the one before answered:
| HTML | RESULT |
|---|---|
|
Ada Lovelace
|
|
EXAMPLE.COM
|
|
PAD!PAD!
|
|
14.52 EUR
|
A part after a pipe is an expression of its own, and the piped value fills the place of the operand it lacks. An operator that opens the part takes it as its left side - | + 1, | . ' EUR', | ?? 'none'. A space between such an operator and its operand is required: | + 1, never | +1.
| HTML | RESULT |
|---|---|
|
6
|
|
9
|
|
25
|
|
1
|
|
256
|
|
Hello world
|
| HTML | RESULT |
|---|---|
|
[1]
|
|
[1]
|
|
[1]
|
|
[1]
|
|
empty
|
|
2 left
|
@ stands for the piped value wherever the part needs it, as often as it needs it:
| HTML | RESULT |
|---|---|
|
16
|
|
12
|
|
"pad"
|
|
pad PAD
|
|
big
|
A part that is a printf format - it starts with % and is one conversion - formats the value as PHP's sprintf does. A % that is no format is the remainder: | % 3.
| HTML | RESULT |
|---|---|
|
3.14
|
|
05.00
|
|
00042
|
|
+42
|
|
ff FF
|
|
1.234500e+3
|
|
25.6%
|
A pipe inside round or square brackets belongs to that group: ($n | + 1) * 2 adds before it multiplies, upper($s | trim) trims the argument, and in a list or an argument list the pipe works on its own item. Outside brackets a pipe always ends the whole expression before it.
| HTML | RESULT |
|---|---|
|
12
|
|
PAD
|
|
6
|
|
6-20
|
|
xPADx
|
A field tag with a pipe, {$x | upper}, writes its answer through the sanitize chain, like every field tag. {echo} writes it raw - the right one for HTML the page made itself, the wrong one for what a visitor typed:
|
PHP |
HTML fragments/expressions_40.pad |
Result |
|---|---|---|
|
<I>PAD</I> PAD |
Dates
date(format, modifier) reads a Unix timestamp, a date in text or a date object the page's PHP made; the modifier is what PHP's strtotime reads, such as '+1 week'. Written as a call it keeps PHP's order, the format first: date('Y', $created). Dates written YYYY-MM-DD compare correctly as plain text. now is the current time, and the locale page has localDate:
| HTML | RESULT |
|---|---|
|
18 Oct 2026
|
|
Sunday
|
|
2026-10-25
|
|
14:30
|
|
2001
|
|
[1]
|
Lists meeting operators
An arithmetic operator or a comparison that meets a list works with the sum of its numbers. A list holding more than numbers, or met by the dot, is an error. A field that holds a list is handed over with the array: prefix:
| HTML | RESULT |
|---|---|
|
7
|
|
10
|
|
6
|
|
[1]
|
|
3
|
|
an empty list
|
|
PHP |
HTML fragments/expressions_43.pad |
Result |
|---|---|---|
|
Sum 30, count 4 Average 7.5 Best 10, worst 4 7, 9, 4, 10 |
Numbers at the edges
What is not a number - a NAN such as php:sqrt(-1) - is empty. % works on whole numbers, as PHP's does; php:fmod keeps the fraction. Decimals are written with PHP's precision, so 0.1 + 0.2 comes out as 0.3:
| HTML | RESULT |
|---|---|
|
[]
|
|
not a number
|
|
3 1
|
|
1 1.5
|
|
0.3
|
Values are text
An expression's answer is a value, and a value never becomes template code: a field that holds braces - from a database, a form, a file - is written as the text it is. To run it as PAD on purpose, say so where it is used, with the code pipe (or sandbox, which runs it in an isolated pass):
|
PHP fragments/expressions_45.php |
HTML fragments/expressions_45.pad |
Result |
|---|---|---|
|
|
{echo 6 * 7} {echo 6 * 7} is text 42 |
When an expression is wrong
Under the strict syntax check - $padCheckSyntax, on by default - a mistake in an expression is an error that names the fault and its place, and the page answers with the error report instead of guessing. The report shows the template's file, line and column with the lines around it (see errors in the template). What some mistakes are told:
| written | the error says |
|---|---|
{echo (1 + 2}
|
the ( opened at position 1 is never closed |
{echo 'abc}
|
the string opened with ' at position 1 is never closed |
{echo $name | uppr}
|
there is no pipe function named 'uppr' |
{echo nofunc(1)}
|
there is no function named 'nofunc' |
{if $typo eq 1}
|
there is no field named '$typo' |
{echo $items + 1}
|
the field '$items' is a list, not a value |
{if $n eq}
|
the operator 'eq' has nothing on its right |
{echo $n + * 3}
|
the operators '+' and '*' stand side by side - a value is missing between them |
{if $n === 5}
|
'===' is no operator - eq (==) compares two values |
{echo $n | +1}
|
a pipe operator needs a space before its operand - write '| + 1', not '| +1' |
{echo 5 & 3}
|
'&' is no operator or value |
{if $n not 5}
|
a 'not' after a value denies a test - write $a not in (...), not like, not matches or not range, or put it in front: not $a eq $b |
{echo 7 / 0}
|
a division by zero in / |
{echo 'a' * 2}
|
'a' is not a number for * |
{echo $n ? 'a'}
|
the ? of an inline ternary has no : - write cond ? then : else |
{echo [ 'a' => ]}
|
the key 'a' of a list has no value after its => |
{echo replace('abc')}
|
the function 'replace' wants 2 parameters after the value it works on - written as a call its first argument is that value: replace($value, ...) |
{echo php:str_repeat('x', -1)}
|
the PHP function 'str_repeat' refused its arguments: str_repeat(): Argument #2 ($times) must be greater than or equal to 0 |
{echo 'a\qb'}
|
Unsupported \ char |
The lenient walk
With $padCheckSyntax = FALSE in _config/config.php nothing is reported and the evaluator answers what it can: a text that is no number counts as 0 ('a' + 1 is 1), a missing field is empty ($nope + 1 is 1), an unknown escape loses its backslash, and an expression that cannot be worked out at all - a bracket never closed, a division by zero, a PHP function refusing its arguments - answers nothing. That suits old templates; a new one is better off with the check, which finds the typo the lenient walk hides.
See also
-
Conditions and branching -
{if},{case}and the ternary in templates - Pipes - pipes on tags, opening and closing
-
Properties -
first@itemsand the others - Prefix - the type prefixes
-
Field sigils -
{!name},{^name}and the rest -
Level and occurrence variables -
$and%