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*5
Does 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, like attacks, 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 & y
is parsed as
{  f x} & y
This 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

Thus,

  move from K|R
is parsed as
  {move from K} | R

 move from  K|R to _ 
is parsed as
{move from K} | R to _ 
which is illegal, so the expression is a syntax error.

  up R|P
is parsed as
  {up R} | P

  move to . from light a & dark .
is parsed as
  {move to . from {light a}}
 & {dark .}

This represents the set of dark squares to which a black piece on a light square moves.

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, that
f x+y
is parsed as
f {x+y}
However,
f x<y
is parsed as
{f x}<y
because 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
For example,
   sqrt 4 + 8<9 + 5
is 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 Thus,
 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 Recall that the comparison operators themselves have the numeric value of their left argument when the comparison succeeds. Thus,
 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 Q>20
then this would be parsed as
   sort (find all (Q>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 #Q>sqrt #r + #q
		  
	       (abs (#Q))>((sqrt (#r)) + (#q))
 # _ attacked by K
		  
		#( _ attackedby K)
 _ attacked by k># _ attacked by K
		   
		 (#( _ attacked by k))>(#( _ attacked by K))
	       move from R|N to r
		  
	       {move from R} | N to r
	           ERROR 
move from R to _ attacked by a
		    
		 {move from R to _ } attackedby a
square x in A
		     rank x<3
		   
		 square x in A
		     (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 {move promote A}
		    or check

What does this mean? It looks like what the user is trying to find is whether there is a 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 {{move promote A} or check}
which is to say: there is either a future promotion or a future check.

To get what the user wants, use:

  {find move promote A}
  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 a
    Q attacks x
    N attacks 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 a
   Q attacks x}
  N attacks 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 a
      {Q attacks x
       N attacks 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).

  flipcolor
     pin through R
     R attacks Q
is parsed as
  {flipcolor
    {pin through R}}
  R attacks Q

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.