---
id: ebnf
slug: /reference/query-grammar/ebnf
title: 'Grammar Reference'
description: 'The complete rest-1.v1 query syntax rendered as EBNF, with the raw ANTLR grammar files available for download.'
sidebar_label: 'Grammar Reference (EBNF)'
sidebar_position: 6
tags: ['reference', 'query-grammar', 'ebnf', 'antlr', 'grammar']
---

This page is the complete syntax in one place, rendered as EBNF. The prose
pages — [attribute paths](/reference/query-grammar/attribute-paths),
[filter expressions](/reference/query-grammar/filter-expressions),
[filter context](/reference/query-grammar/filter-context) and
[sort and paging](/reference/query-grammar/sort-and-paging) — explain what each
production means; this page is for checking a form quickly.

The EBNF below is a faithful transcription of the published ANTLR v3 grammar
files, reordered for reading and with the ANTLR-specific machinery (token
names, lexer actions, `EOF`) removed. Where the grammar and the server disagree, the server wins and
the divergence is called out inline.

## Notation

| Form            | Meaning                          |
| --------------- | -------------------------------- |
| `=`             | Defines a rule                   |
| `,`             | Concatenation                    |
| `\|`            | Alternatives                     |
| `[ x ]`         | Optional — zero or one           |
| `{ x }`         | Repetition — zero or more        |
| `( x )`         | Grouping                         |
| `"x"`           | A literal character or string    |
| `? … ?`         | A condition described in prose   |

## Entry Points

`V1Query.g` is unusual in having four root rules rather than one, because the
same vocabulary is reused by several query parameters.
The table maps each root to the wire parameter that actually carries it.

| Grammar root                 | Grammar file        | Wire parameter                       |
| ---------------------------- | ------------------- | ------------------------------------ |
| `attribute_selection_token`  | `V1Query.g`         | `sel`, and `select` as an alias      |
| `filter2_token`              | `V1Query.g`         | **`where`** — there is no `filter=`  |
| `sort_token`                 | `V1Query.g`         | `sort`                               |
| `attribute_definition_token` | `V1Query.g`         | the trailing path segment of a URL   |
| `filter_context_token`       | `V1FilterContext.g` | `with`                               |
| `paging_token`               | `V1Paging.g`        | `page`                               |

The parameters `find`, `findin`, `asof`, `deleted` and `needTotal` have no
grammar; they are plain scalar values. They are documented in the
[parameter map](/reference/query-grammar#parameter-map).

## Selection, Sort and Attribute Definition

```text
attribute_selection_token   = [ attribute_name , { "," , attribute_name } ] ;

sort_token                  = [ sort_term , { "," , sort_term } ] ;

sort_term                   = [ "+" ] , attribute_name
                            | "-" , attribute_name ;

attribute_definition_token  = asset_type , "." , attribute_name ;
```

## Attribute Paths

```text
attribute_name       = attribute_name_part
                     , { "." , attribute_name_part }
                     , [ ".@" , aggregation_name ] ;

attribute_name_part  = NAME
                     , [ now_only ]
                     , [ downcast ]
                     , [ attribute_filter ] ;

now_only             = "#" ;                                    (* send as %23 *)

downcast             = ":" , asset_type ;

attribute_filter     = "[" , filter_expression , "]" ;

asset_type           = NAME ;

aggregation_name     = NAME ;                                   (* see note *)
```

`aggregation_name` is a bare `NAME` in the grammar, but the server accepts only
`Sum`, `Count`, `DistinctCount`, `Any`, `MinDate`, `MaxDate`, `And`, `Or` and
`MaxState` — the grammar's own documentation comment lists these too, except
`Any`, which it omits. See
[aggregations](/reference/query-grammar/attribute-paths#aggregations).

## Filter Expressions

```text
filter_expression  = filter_term
                   , { ( and_operator | or_operator ) , filter_term } ;

filter_term        = grouped_filter_term | simple_filter_term ;

grouped_filter_term = "(" , filter_expression , ")" ;

simple_filter_term = attribute_name
                     , [ binary_operator , ( filter_value_list | variable ) ]
                   | unary_operator , attribute_name ;

and_operator       = "&" | ";" ;

or_operator        = "|" ;

binary_operator    = "=" | "!=" | "<" | "<=" | ">" | ">=" | "~=" ;

unary_operator     = "+" | "-" ;

filter_value_list  = filter_value , { "," , filter_value } ;

filter_value       = single_quoted_string | double_quoted_string ;

variable           = context_asset | variable_name ;
```

`filter_expression` is a flat left-associated repetition: **AND and OR have the
same precedence.** Parenthesise when mixing them.

## Filter Context

```text
filter_context_token  = [ filter_context_term , { "|" , filter_context_term } ] ;

filter_context_term   = variable_name , "=" , filter_context_values ;

filter_context_values = context_value , { "," , context_value } ;

context_value         = ? any run of characters other than "," and "|" ? ;
```

`context_value` is deliberately permissive because the real implementation is a
token splitter, not a parser — `V1FilterContext.g` says so in its own preamble
and models it with a lexer mode flag. Values here are **not** quoted, unlike
values in a filter expression. See
[filter context](/reference/query-grammar/filter-context).

## Paging

```text
paging_token  = page_size , [ "," , page_start ] ;
page_size     = INT ;
page_start    = INT ;
```

Size first. See
[sort and paging](/reference/query-grammar/sort-and-paging#paging).

## Lexical Rules

```text
single_quoted_string  = "'" , { ? any character except "'" ? } , "'" ;
double_quoted_string  = '"' , { ? any character except '"' ? } , '"' ;

context_asset         = "$" ;
variable_name         = "$" , NAME ;

NAME                  = name_char , { name_char } ;
name_char             = alpha | digit | "_" ;
alpha                 = "A".."Z" | "a".."z" ;
digit                 = "0".."9" ;

INT                   = digit , { digit } ;
```

Three things follow from these lexical rules that are easy to miss:

- **Quoted strings have no escape sequence.** A value cannot contain its own
  delimiter, and no value can contain both `'` and `"`.
- **`NAME` excludes `-`, spaces and every other punctuation character.** Asset
  type names and attribute names are alphanumerics and underscores only.
- **There is no whitespace rule.** The grammars define no skipped-whitespace
  token, so do not put spaces around operators in a query token.

## Structure at a Glance

The reuse of `attribute_name` is what makes the language compact — and what
makes it recursive, since a filter attached to a path part contains a filter
expression, which contains attribute paths of its own.

```text
attribute_selection_token ─┐
sort_token ────────────────┼──▶ attribute_name ──▶ attribute_name_part ──┬──▶ now_only
attribute_definition_token ┘                                             ├──▶ downcast
                                                                         └──▶ attribute_filter
                                                                                    │
filter2_token ──▶ filter_expression ◀───────────────────────────────────────────────┘
                        │
                        ├──▶ grouped_filter_term ──▶ filter_expression   (recursion)
                        └──▶ simple_filter_term ──┬──▶ attribute_name    (recursion)
                                                  ├──▶ filter_value_list
                                                  └──▶ variable
```

## The Raw Grammar Files

The three ANTLR v3 files are published verbatim, including their modified-BSD
license headers. They are a machine-readable contract: fetch them if you are
building a parser, a linter or a query builder.

| File                                                          | Bytes | Covers                                               |
| ------------------------------------------------------------- | ----- | ---------------------------------------------------- |
| [`V1Query.g`](pathname:///grammars/V1Query.g)                 | 9,608 | Selection, attribute paths, filter expressions, sort |
| [`V1FilterContext.g`](pathname:///grammars/V1FilterContext.g) | 1,882 | The `with` parameter                                 |
| [`V1Paging.g`](pathname:///grammars/V1Paging.g)               | 1,118 | The `page` parameter                                 |

They are published unmodified; the modified-BSD license header at the top of
each file is intact. A [README](pathname:///grammars/README.md) sits alongside them
recording their provenance and caveats.

:::note Read the grammars as a syntax map, not as an oracle
The files were last revised in 2012 and the upstream project states that they
describe the intended syntax rather than the shipped implementation. Eight known
divergences are catalogued in
[grammar versus implementation](/reference/query-grammar#grammar-versus-implementation).
The most consequential is the naming of `filter2_token`, which implies a
`filter=` query parameter that does not exist.
:::

## Related Topics

- [Query grammar overview](/reference/query-grammar)
- [Attribute paths](/reference/query-grammar/attribute-paths)
- [Filter expressions](/reference/query-grammar/filter-expressions)
- [Filter context](/reference/query-grammar/filter-context)
- [Sort and paging](/reference/query-grammar/sort-and-paging)
