Skip to main content

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​

FormMeaning
=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 rootGrammar fileWire parameter
attribute_selection_tokenV1Query.gsel, and select as an alias
filter2_tokenV1Query.gwhere — there is no filter=
sort_tokenV1Query.gsort
attribute_definition_tokenV1Query.gthe trailing path segment of a URL
filter_context_tokenV1FilterContext.gwith
paging_tokenV1Paging.gpage

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 ".
  • 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.

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.

FileBytesCovers
V1Query.g9,608Selection, attribute paths, filter expressions, sort
V1FilterContext.g1,882The with parameter
V1Paging.g1,118The 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.

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. The most consequential is the naming of filter2_token, which implies a filter= query parameter that does not exist.