Precedence
For the table of precedence, see here .Precedence deals with how to interpret expressions that can be interpreted in two or more ways.
For example, consider the CQL expression:
2+3*5Does this mean
(2+3)*5
or does it mean
2+ (3*5)
The answer is the second option: 2+3*5
means
2+(3*5)
because *
has higher precedence than +
. This convention for the relative precedence of addition and multiplication is conventional in mathematics and computer science; the precedence of operators in CQL generally adheres to convention.
A full table of the precedence of all the CQL filters is here. This web page is designed to explain and motivate that table.
The very highest precedence in CQL is of a single parenthesized expression or expression in braces:
(2*2) {3+4}
Parentheses can be used to surround any single filter; braces can surround multiple filters to form a compound filter{compound filter}.
If we check the table we can see that *
has higher precedence than +
(because it is in a lower cell of table). This tells us the parse of 2+3*5
Parsing standard mathematical expressions
CQL uses the customary precedence of mathematical or computer science operators when they are used in CQL. Namely, and, or, not, +, /, *, %, ~, |, ∩, in, ==, !=, <, <=, >, >= all follow customary usage.Thus,
2+3*4<4/2+1
≡
(2+(3*4))<((4/2)+1)
check or not mate and Q or stalemate
≡
check or
((not mate) and Q) or
stalemate
A | ~B&Q | q
≡
A | ((~B)&Q) | q
Parsing named prefix CQL filters
A filter whose name precedes its arguments is called a prefix filter. (A filter whose name is between its arguments, likeattacks
, is called
an infix filter).
A prefix filter treats its arguments as follows:
Both the prefix filter and its argument must be set filters
Consider a CQL filter f with an argument (or parameter) arg. Suppose- arg must be a set filter, no matter what the other parameters to f are, and
- f can be a set filter with some parameter
Then the argument is parsed at a precedence equal to the (very high) precedence of :.
f x & yis parsed as
{ f x} & yThis rule applies not just to the argument, but also to any set-value parameters of the prefix filter.
This rule affects:
-
the
from
,to
,capture
,enpassantsquare
parameters of move - all the parameters of pin
- the argument to the vector filters (up, down etc.)
- the arguments to light and dark
up ♖|♙is parsed as
{up ♖} | ♙
Numeric arguments and parameters
If the argument or parameter to the prefix filter must be a numeric filter, then that argument is parsed at a precedence level just lower than addition. This means that if f takes a numeric argument, thatf x+yis parsed as
f {x+y}However,
f x<yis parsed as
{f x}<ybecause addition has higher precedence than the relation operators.
This rule applies to parsing:
- The right side of a persistent variable assignment
- The numeric arguments to makesquare
- The numeric argument to abs
- The numeric argument to -
- The numeric right hand side of operand assignments (+=, -=, *=, /=, %=)
- The numeric argument to sqrt
- The numeric argument to position
sqrt 4 + 8<9 + 5is parsed as
{sqrt {4+8}}<{9+5}
makesquare (2 + 3 3 + 4)is parsed as
makesquare ({2+3} {3+4})
Set arguments not discussed above
Any other parameter of argument that must be a set filter is parsed at a level just lower than|
. This includes
- the set arguments to rank and file
- the set argument to #
- the set filter following the
in
parameter to square or piece - the set arguments to between
- the set argument to power
- the set argument to type or colortype
- the set argument on the right hand side of piece assignment
- the set argument to pieceid
rank x & y
≡
rank (x & y)
# Q|K
≡
#{Q|K}
power a attackedby Q
≡
power {a attacked by Q}
All other arguments
Other arguments are parsed at the lowest possible precedence level. These include- consituents of a compound filter
- any filter enclosed in parentheses (not an argument list)
- the argument to find
- body of piece and square
- any of the arguments to if
- the body filter of echo
- arguments to distance, lca, descendant, ancestor
- body of loop
- argument to transforms (like flipcolor or shift)
- arguments to user-defined function calls
- argument to sort
sort find all check>20 ≡ sort (find all check)>20 ≡ sort ((find all check)>20)This has the effect of sorting by the number of checks following or at the current position, when that number is greater than 20.
The first equivalence above only works because check
is not a numeric, set, or position filter. If, say, we were to right
sort find all ♕>20
then this would be parsed as
sort (find all (♕>20))
which sorts the number of successive positions in where there are more than 20 white queens.
Precedence examples
1+2*3<4*5+6
≡
(1+(2*3))<((4*5)+6)
abs #♕>sqrt #♜ + #♛ ≡ (abs (#♕))>((sqrt (#♜)) + (#♛))
#□←♔ ≡ #(□←♔)
□←♚>#□←♔ ≡ (#(□←♚))>(#(□←♔))
square x in A rank x<3 ≡ square x in △ (rank x<3)
Precedence pitfalls and debugging
The precedence rules have been chosen so that the vast majority of the time the user can write things without parentheses and CQL does the expected thing. If CQL does not do the expected thing, it will usually give a syntax error pointing at the line and column where it was confused.Unfortunately, there are a couple of cases where the user can write what looks like sensible code, and CQL will use the precedence rules to parse it in an unwanted manner.
Most of these have to do with situations involving filters that take a single argument that can be any filter. The argument filter can "capture" trailing filters that the user did not expect, particularly because CQL does not consider white space in its parsing.
Consider for example the following filter:
find {――=♗} or check
What does this mean? It looks like what the user is trying to find is whether
either there is a bishop promotion in the future, or the current position is check. But
actually, because the find
here has the lowest possible precedence, the or
following
the move
has higher precedence than find
. Thus, the actual meaning of this expression is:
find {{――=♗} or check}which is to say: there is either a future bishop promotion or a future check.
To get what the user wants, use:
{find ――=♗} or check
This kind of issue can happen with any of the filters that take a single argument at lowest precedence: namely find, square, piece, echo, sort, and the transform filters, like flipcolor.
The reverse of this issue happens when a filter captures less than the user expects:
square x in ▲ ♕→x ♘→x
Here, the user intended to get the set of all black pieces attacked by a queen and a knight. But the body of square takes only a single filters, so this is parsed as
{square x in ▲ ♕→x} ♘→x
Unfortunately, CQL will not report this error until it is actually run on a game, and the error will
not include line number information but will say something like "attempt to access unbound variable x".
When you see this error, check that the variable x
is actually inside the scope of the square
or piece
or echo
that
defined that variable. (Also check the spelling of the variable).
To get what the users wants, use
square x in ▲ {♕→x ♘→ x}
This kind of error can occur with any of the three filters that defines new variables: square, piece, and echo.
A similar issue often happens with the transform filters, whose parsing precedence was changed from a much older version of CQL (CQL 3).
⬓ pin through ♖ ♖→♕is parsed as
{⬓ {pin through ♖}} ♖→♕
It might occasionally be helpful to run cql with the
-parse
option, (cql -parse foo.cql
). This unsupported option is intended for CQL developers, and prints out cryptic
internal parse tree information. It is, however, possible that for very simple CQL files, the cql -parse
output would be helpful to non-CQL developers, even though it is not designed for that purpose.
Final note
Rather than constantly checking these rules, we recommend using braces or parentheses to make clear what you want if there is any question. There is never any performance penalty for surrounding an expression with braces or parentheses.