Filters
A filter is a CQL construct which can be either true or false of a given chess position.By "position" here we don't just mean the board position, but also where that position occurs in the game, including all the preceding and subsequent positions.
A filter is only ever applied to whatever the current position is. But
it can be applied to many different positions in a game as the current
position is updated by the CQL main loop. Some filters, like next
and previous
can themselves temporarily modify the current position.
For example, one filter is the check
filter. This will be true if one
of the Kings is in check, and will be false otherwise. The name of the
check filter is simply check
Many filters include other filters as constituents. For example, an
or
filter has two constituents, its first clause and its
second clause. Thus,
check or Ra3is a single filter with two constituents. The filter matches a position if and only if either one of the Kings is in check or if the White rook is on a3 (or both, of course).
Filters between braces
Braces introduce a kind of filter called a composite filter. Acomposite
filter matches a position if and only each of its
clauses match. For example,
{check Ra3}is a single composite filter that matches a position in which both one of the Kings in in check and the white Rook is on a3.
The filters in the body of the CQL form are implicitly enclosed in a
composite filter: they all must match in order for the body to match. The
square
and piece
filters also take these implicit composite filters as
arguments.
Parsing filter expressions
Sometimes it can seem difficult to parse an expression using filters. For example, what does this mean?not Ra3 or checkMaybe it means, one might think,
{not Ra3} or checkWhich would match a position where there is no white Rook on a3 or which is a check. Or maybe it means
not {Ra3 or check}which would match a position in which it is not the case that there is either a Rook on a3 or it is check, that is, this would match positions that are not checks and in which there is no white Rook on a3.
CQL always uses the second interpretation. All operators in CQL - not
,
or
, on
- have equal precedence, and CQL always tries to make arguments
as long as possible as soon as possible.
Thus, the filter not
takes a single argument, a filter. not
matches a position if and only if its argument does not match.
So, when parsing not Ra3 or check
, CQL tries to make the
argument to not
as long as possible. Since
Ra3 or checkis longer than just
Ra3CQL will interpret
not Ra3 or check
as being the filter
Ra3 or checkNote that
{Ra3 or check}
is exactly the same as Ra3
or check
. It is a composite filter with one argument, the or filter
Ra3 or check
.
reading argument lists
Sometimes reading CQL code can be confusing because an argument list might contain more than one filter. An argument list is a series of one or more filters separated by white space and enclosed in parentheses.For example, the argument list
(Ra3 check)is an argument list with two filters in it . Similarly
(Ra3 or Nb2 check or stalemate)is another argument list with two filters. The first filter is the or filter
Ra3 or Nb2which matches a position if there is a white rook on a3 or a white knight on b2. The second filter is
check or stalematewhich is true if either the position is stalemate or check
This could be written equivalently as
({Ra3 or Nb2} {check or stalemate})If it's clearer to you, always enclose filters in braces.
For example, the next
filter takes as its argument a list of
filters. These filters must match successive future positions from the
current position. Thus,
next (Ra3 or Nb2 check or stalemate)is identical to
next ({Ra3 or Nb2} {check or stalemate} )This matches the current position if the current position has either a R on a3 or an N on b2, and if the following position - the position after the next move - is either a check or a stalemate.
You might be tempted to think that
(Ra3 or Nb2 check or stalemate)Might be parsed as
(Ra3 or {Nb2 check} or stalemate)but this would be incorrect, because
Nb2 checkis not the same as
{Nb2 check}
Note that there is no filter
Nb2 checkThe rule to remember here is this: Two filters next to one other separated by white space is never a single filter
More parsing examples
Likewise,{not Ra3 check}matches positions where the R is not on a3 and the position is a check. It is equivalent to
{ {not Ra3} check}This is because there is no such single filter
Ra3 checkso that
Ra3 checkcannot be an argument to
not,
which takes only a single filter as its argument.
This rule is most commonly applied to transform filters (shift
,
flip
and so on). All of these take a single filter argument, but it is common to want to apply these to multiple filters at once. Remember to enclose their bodies in braces if you want to apply them to multiple filters at once. For instance, use
{rotate90 Ka1 kh8}would be parsed as
{{rotate90 Ka1} kh8}which would match a position with the white King on a corner and the black on h8.
By contrast,
{rotate90 {Ka1 kh8}}would match positions with the kings on diagonally opposite corners.
However, since a transform filter is itself one filter, they can be chained:
flipcolor rotate90 {Ka1 kh8}would apply
flipcolor
to the result of applying rotate90
to {K1 kh8}
(which in point of fact would not change anything semantically here).
filters that take parameters as well as arguments
Some filters take parameters as well as an argument list. These parameters might be a range or other parameters that come between the name of the filter and its arguments.