Grammar Reference
This page is the complete syntax in one place, rendered as EBNF. The prose pages — attribute paths, filter expressions, filter context and 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.
Selection, Sort and Attribute Definition
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
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.
Filter Expressions
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
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.
Paging
paging_token = page_size , [ "," , page_start ] ;
page_size = INT ;
page_start = INT ;
Size first. See sort and paging.
Lexical Rules
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". NAMEexcludes-, 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.
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 | 9,608 | Selection, attribute paths, filter expressions, sort |
V1FilterContext.g | 1,882 | The with parameter |
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 sits alongside them recording their provenance and caveats.
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.
The most consequential is the naming of filter2_token, which implies a
filter= query parameter that does not exist.