CQL file: structure and operation

There are three parts to a CQL file:
  1. The word cql
  2. The CQL header: a sequence of cql parameters enclosed in parentheses
  3. The CQL body: a sequence of filters.

For example, here is a CQL file to find games where black is stalemated and is pinned at least twice:

  cql (input hhdbvi.pgn)
  btm
  stalemate
  1< pin through a

In the above example, the CQL header consists of one cql parameter:

  input hhdbvi.pgn

The parameter, input hhdbvi.pgn specifies that the games are to be read from the file hhdbvi.pgn.

The CQL body in the above example is a sequence of three filters:

  1. A btm filter, which matches a position with black to move.
  2. A stalemate filter, which matches a position that is stalemate
  3. A < filter
    	1<pin through a
The < filter has two arguments itself, each of which are also filters:
1
and
         pin through a

The first of these, 1 is just the number 1. The second argument to the < filter, pin through a, represents the set of squares on which there is a black pinned piece. When a number is compared to a set using <, the set is replaced by its cardinality. Thus, the < filter in the CQL file will match a position whenever there are multiple pinned black pieces in the position.

This CQL file will output every game from hhdbvi.pgn in which there is a black stalemate with at least two pinned black pieces.

Operation of a CQL file

Here are the steps CQL takes when it is invoked on a .cql file:
  1. CQL reads the CQL header and any command line arguments to determine where it should read the PGN data from (the input file).
  2. For each game in the input file, CQL tries to match that game against its CQL body. It does this by visiting each position in the game being examined. Each time CQL visits a position, it does this:
    1. set the current position to the position being examined
    2. Evaluate each filter in the CQL body on the current position.
      • If all the filters are true, the position matches
      • Otherwise, the position fails to match.
  3. After all the positions in the game being examined were visited, CQL determines if the game matches. Normally, the game matches if at least one of its positions matched the CQL body. (This can be changed by the matchcount parameter).
  4. If the game matches, it is output to the output file.

The input file

The input file should be a standard PGN file. CQL works best with PGN files which are in so-called export format, which roughtly means they should not be hand-generated but should adhere to a strict format, such as should be produced by the output of a chess program or web site. The PGN file should not contain any illegal moves, although null moves are allowed. (CQL sometimes halts with cryptic error messages if the PGN file is ill-formed.)

Over the decades, a number of commonly used programs have introduced non-compliant behavior into the PGN files they generate. Some PGN files have nested comments. CQL will try to eliminate the internal braces in nested comments. Others have Unicode non-ASCII characters, which CQL typically ignores.

If the filename contains spaces, you should enclose the filename in quotes:

  cql (input "filename with spaces.pgn")

The output file

The output file where the matched games are output to is by default the name of the CQL file, without any .cql file extension, prepended to -out.pgn. For example, if the CQL file is foo.cql then the output file will be foo-out.pgn. You can specify the output explicitly, either on the command line with -output or in the CQL header

If the filename contains spaces, you should enclose the filename in quotes.

Which positions are visited

By default, CQL only visits the mainline positions in each CQL file. This can be changed by specifying the -variations flag on the command line or including the variations parameter in the CQL header:

 cql(input hhdbvi.pgn variations)
  ...

CQL visits the positions in the order they occur in the PGN file.

Summary

Everything in CQL depends on this order of operations. In brief,
  1. CQL reads parameters from the CQL header, and parses the CQL body into filters
  2. For each game, CQL determines whether any position from a game matches all the filters in its body
  3. Every time a game matches the CQL body, the game output to a pgn file