MemQL Grammar
This is the authoring grammar for .memql files, in EBNF. It is generated from the parser's own construct and clause tables, the annotation registry in component/language/annotations, and the function catalog and operator table in component/language/functions, so it cannot describe a language the cluster does not accept.
It exists to be given to a model -- as grammar-in-prompt, or as the grammar a constrained decoder is held to. A hand-maintained grammar drifts silently: it does not fail, it teaches a form the parser refuses, and the failure reads as "the model is bad at MemQL".
Two things are deliberately absent. The internal query form -- the string an SDK sends to Execute -- has its own grammar; a reader given both would have no way to tell which one their file is written in. And which annotation is legal on which construct, in which argument form, is the attribute matrix, not a syntax rule. What each name MEANS is the vocabulary.
Edition 2026, grammar version 2026.09-dsl-v1-followups-9f344ecf.
(* MemQL authoring grammar. Edition 2026, grammar version 2026.09-dsl-v1-followups-9f344ecf. GENERATED from the parser, the annotation registry and the function catalog -- do not edit. Run `make docs-grammar` to refresh it. Notation: <name> is a production, "x" a terminal, [ x ] optional, { x } zero or more, x* zero or more, x+ one or more, ( a | b ) a choice, "a".."z" a character range, and 'x' a terminal holding a double quote. A right-hand side written in English is lexical: the lexer decides it. *) (* ---- A file ---- *)<file> ::= <declaration-annotation>* <use>* <declaration>*<use> ::= "use" <dotted-path> "{" <import-name> { "," <import-name> } "}"<dotted-path> ::= <name> { "." <name> } "."<import-name> ::= <name> [ "as" <name> ]<declaration> ::= <action> | <automation> | <builtin> | <capability> | <concept> | <logic> | <mutation> | <policy> | <prompt> | <provider> | <query> | <rule> | <seed> | <shape> | <spec> | <tool> | <trait> (* ---- Declarations ---- *)<action> ::= <action-annotation>* "action" <name> "{" <action-body> "}"<action-body> ::= ( <args-block> )* <capability-call><automation> ::= <automation-annotation>* "automation" <name> "{" <automation-body> "}"<automation-body> ::= ( <args-block> | <precondition-block> )* <statement>*<builtin> ::= <builtin-annotation>* "builtin" <name> "{" <builtin-body> "}"<builtin-body> ::= <field>*<capability> ::= <capability-annotation>* "capability" <dotted-name> "{" <capability-body> "}"<capability-body> ::= ( <args-block> )*<concept> ::= <concept-annotation>* "concept" <name> "{" <concept-body> "}"<concept-body> ::= ( <concept-field> | <concept-body-annotation> )*<logic> ::= <logic-annotation>* "logic" <name> "{" <logic-body> "}"<logic-body> ::= ( <args-block> )* <statement>*<mutation> ::= <mutation-annotation>* "mutation" <concept-name> <name> "{" <mutation-body> "}"<mutation-body> ::= ( <args-block> | <insert-block> | <update-block> | <accept-block> | <stamp-block> )*<policy> ::= <policy-annotation>* "policy" <name> "{" "}"<prompt> ::= <prompt-annotation>* "prompt" <name> "{" <prompt-body> "}"<prompt-body> ::= <field>*<provider> ::= <provider-annotation>* "provider" <name> "{" <provider-body> "}"<provider-body> ::= ( <params-block> | <auth-block> )*<query> ::= <query-annotation>* "query" <concept-name> <name> "{" <query-body> "}"<query-body> ::= ( <args-block> | <filter-clause> | <refine-clause> | <shape-clause> | <sort-clause> | <paginate-clause> | <asOf-clause> | <count-clause> )*<rule> ::= <rule-annotation>* "rule" <name> "{" "}"<seed> ::= <seed-annotation>* "seed" <concept-name> <name> "{" <seed-body> "}"<seed-body> ::= <seed-entry>*<shape> ::= <shape-annotation>* "shape" [ <concept-name> ] <name> "{" <shape-body> "}"<shape-body> ::= <path>*<spec> ::= <spec-annotation>* "spec" <bound-name> <name> "=" <lambda><tool> ::= <tool-annotation>* "tool" <name> "{" <tool-body> "}"<tool-body> ::= <field>*<trait> ::= <trait-annotation>* "trait" <name> "=" <lambda> (* ---- Body clauses ---- *)<args-block> ::= "args" "{" <field>* "}"<filter-clause> ::= "filter" <lambda><refine-clause> ::= "refine" <lambda><shape-clause> ::= "shape" <name><sort-clause> ::= "sort" <string> { "," <string> }<paginate-clause> ::= "paginate" <number><asOf-clause> ::= "asOf" ( "latest" | <expression> )<count-clause> ::= "count"<insert-block> ::= "insert" "{" ( <write-entry> [ "," ] )* "}"<update-block> ::= "update" "{" ( <write-entry> [ "," ] )* "}"<accept-block> ::= "accept" "{" ( <name> [ "," ] )* "}"<stamp-block> ::= "stamp" "{" ( <map-entry> [ "," ] )* "}"<precondition-block> ::= "precondition" <name> "{" <precondition-entry>* "}"<params-block> ::= "params" "{" <param-entry>* "}"<auth-block> ::= "auth" "{" <auth-entry>* "}"<capability-call> ::= "capability" <dotted-name> "(" [ <argument> { "," <argument> } ] ")"<write-entry> ::= <name> ":" <expression> | "args" "." <name> | <accept-block> | <stamp-block><map-entry> ::= <name> ":" <expression><param-entry> ::= <name> ( <string> | <number> | "true" | "false" )<auth-entry> ::= <name> ( <string> | "env" "(" <string> ")" )<precondition-entry> ::= <precondition-key> ":" <expression><precondition-key> ::= "check" | "literal" | "description"<seed-entry> ::= <name> ":" <seed-value> | <name> "{" <seed-entry>* "}"<seed-value> ::= <string> | <number> | "true" | "false" | "[" ( <string> [ "," ] )* "]"<path> ::= <name> { "." <name> } (* ---- Statements (a logic's and an automation's body) ---- *)<statement> ::= <binding> | <field-write> | <construct-call> | <if-statement> | <for-statement> | <switch-statement> | <parallel-statement> | <publish-statement> | <return-statement><binding> ::= <name> ":=" ( <construct-call> | <expression> )<field-write> ::= "row" "." <name> "=" <expression><construct-call> ::= <construct-invocation> <trailing-clause>*<construct-invocation> ::= <construct-kind> <name> "(" [ <argument> { "," <argument> } ] ")"<construct-kind> ::= "action" | "automation" | "builtin" | "logic" | "mutation" | "query"<argument> ::= <name> ":" <expression><trailing-clause> ::= "retry" "(" <number> ")" | "wait" ( "any" | "all" ) | "on" "surface" "(" <string> ")" | "on" "error" ( "continue" | "stop" )<if-statement> ::= "if" <expression> "{" <statement>* "}" { "else" "if" <expression> "{" <statement>* "}" } [ "else" "{" <statement>* "}" ]<for-statement> ::= "for" <name> "in" <expression> [ "if" <expression> ] "{" <statement>* "}" [ <trailing-clause> ]<switch-statement> ::= "switch" <expression> "{" { "case" <literal> { "," <literal> } "{" <statement>* "}" } [ "default" "{" <statement>* "}" ] "}"<parallel-statement> ::= "parallel" "{" { "branch" <name> "{" <statement>* "}" } "}" [ "wait" "any" ] [ <trailing-clause> ]<publish-statement> ::= "publish" <string> "{" <map-entry> { "," <map-entry> } "}"<return-statement> ::= "return" [ <construct-call> | <expression> ] (* ---- Annotations ---- *)(* Which annotation is legal where is this production set; which ARGUMENT form each takes there is the attribute matrix, not a syntax rule. *)<action-annotation> ::= ( "@description" | "@disabled" ) [ <annotation-args> ]<automation-annotation> ::= ( "@actor" | "@description" | "@disabled" | "@filter" | "@loop" | "@mcp" | "@mode" | "@template" | "@trigger" ) [ <annotation-args> ]<builtin-annotation> ::= ( "@alias" | "@args" | "@description" | "@disabled" | "@executor" | "@requiresCapability" | "@sdk" ) [ <annotation-args> ]<capability-annotation> ::= ( "@description" | "@disabled" | "@sideEffect" ) [ <annotation-args> ]<concept-annotation> ::= ( "@composable" | "@description" | "@displayCard" | "@mirroredTo" | "@origin" | "@rowAuthz" | "@type" | "@version" ) [ <annotation-args> ]<logic-annotation> ::= ( "@actor" | "@description" | "@disabled" | "@eventField" | "@requiresCapability" | "@requiresRank" ) [ <annotation-args> ]<mutation-annotation> ::= ( "@actor" | "@addToSet" | "@appendFields" | "@createOnly" | "@description" | "@disabled" | "@mcp" | "@mergeFields" | "@noUnset" | "@public" | "@removeFromSet" | "@requiresCapability" | "@requiresRank" | "@scrubPii" | "@serverOnly" | "@shopperFormExtension" ) [ <annotation-args> ]<policy-annotation> ::= ( "@description" | "@fallback" | "@primary" ) [ <annotation-args> ]<prompt-annotation> ::= ( "@defaultProvider" | "@description" | "@disabled" | "@level" | "@templateFile" ) [ <annotation-args> ]<provider-annotation> ::= ( "@base" | "@default" | "@description" | "@disabled" | "@extends" | "@modality" | "@model" | "@vendor" ) [ <annotation-args> ]<query-annotation> ::= ( "@actor" | "@cache" | "@description" | "@disabled" | "@mcp" | "@public" | "@requiresCapability" | "@requiresRank" | "@serverOnly" | "@unbounded" ) [ <annotation-args> ]<rule-annotation> ::= ( "@description" | "@disabled" | "@exclude" | "@level" | "@locked" | "@onUnavailable" | "@policy" | "@precedence" | "@when" ) [ <annotation-args> ]<seed-annotation> ::= ( "@description" | "@disabled" | "@scope" | "@templateFile" | "@version" ) [ <annotation-args> ]<shape-annotation> ::= ( "@actor" | "@description" | "@row" ) [ <annotation-args> ]<spec-annotation> ::= ( "@description" | "@disabled" ) [ <annotation-args> ]<tool-annotation> ::= ( "@allowedRoles" | "@description" | "@destructive" | "@disabled" | "@executionTime" | "@handler" | "@mcp" | "@requiresAgentRole" | "@requiresConfirmation" | "@requiresRank" ) [ <annotation-args> ]<trait-annotation> ::= ( "@description" | "@disabled" ) [ <annotation-args> ]<concept-body-annotation> ::= ( "@relationship" ) [ <annotation-args> ]<declaration-annotation> ::= <action-annotation> | <automation-annotation> | <builtin-annotation> | <capability-annotation> | <concept-annotation> | <logic-annotation> | <mutation-annotation> | <policy-annotation> | <prompt-annotation> | <provider-annotation> | <query-annotation> | <rule-annotation> | <seed-annotation> | <shape-annotation> | <spec-annotation> | <tool-annotation> | <trait-annotation><concept-field-annotation> ::= ( "@description" | "@internal" | "@maxLength" | "@maximum" | "@minLength" | "@minimum" | "@open" | "@pattern" | "@pii" | "@required" | "@secret" | "@serverSet" | "@variant" ) [ <annotation-args> ]<args-field-annotation> ::= ( "@enum" | "@maxLength" | "@maximum" | "@minimum" | "@pattern" | "@required" ) [ <annotation-args> ]<tool-field-annotation> ::= ( "@autoInjected" | "@default" | "@description" | "@enum" | "@required" ) [ <annotation-args> ]<prompt-field-annotation> ::= ( "@default" | "@description" | "@enum" | "@required" ) [ <annotation-args> ]<builtin-field-annotation> ::= ( "@description" | "@enum" | "@required" ) [ <annotation-args> ]<annotation-args> ::= "(" [ <annotation-arg> { "," <annotation-arg> } ] ")"<annotation-arg> ::= <string> | <number> | "true" | "false" | <object> | <lambda> | <name> [ "=" <annotation-value> ] | "!" <string><annotation-value> ::= <string> | <number> | "true" | "false" | <name> | <lambda> (* ---- Fields ---- *)<field> ::= <doc-comment>* <name> <field-type> [ "!" ] <field-annotation>*<concept-field> ::= <doc-comment>* <name> ( "{" <concept-field>* "}" | <field-type> [ "!" ] <concept-field-annotation>* [ "{" <concept-field>* "}" ] )<field-annotation> ::= <concept-field-annotation> | <args-field-annotation> | <tool-field-annotation> | <prompt-field-annotation> | <builtin-field-annotation>(* `array` is a deprecated spelling of []T. It still derives -- the tree writes it -- and the vocabulary is where a reader is told what to write instead. *)<field-type> ::= "string" | "int" | "float" | "bool" | "datetime" | "object" | "enum" "(" <string> { "," <string> } ")" | "any" | "boolean" | "integer" | "number" | "array" | "[]" <field-type><doc-comment> ::= "///" <text> (* ---- Expressions ---- *)<expression> ::= <expr-10><lambda-params> ::= <name> | "(" <name> "," <name> ")"<primary> ::= <literal> | <reserved-root> | <construct-invocation> | <call> | <list> | <object> | "(" <expression> ")" | <name><reserved-root> ::= "actor" | "args" | "config" | "now" | "partition" | "payload" | "trace"<list> ::= "[" [ <expression> { "," <expression> } [ "," ] ] "]"<object> ::= "{" [ <map-entry> { "," <map-entry> } [ "," ] ] "}" (* ---- The precedence ladder, loosest binding first. Level numbers are the operator table's; level 1 binds tightest. ---- *)<expr-10> ::= <lambda> | <expr-9><lambda> ::= <lambda-params> "=>" <expr-9><expr-9> ::= <expr-8> [ "?" <expr-9> ":" <expr-9> ]<expr-8> ::= <expr-7> { "||" <expr-7> }<expr-7> ::= <expr-6> { "&&" <expr-6> }<expr-6> ::= <expr-5> { ( "!=" | "<" | "<=" | "==" | ">" | ">=" | "in" | "startsWith" ) <expr-5> }<expr-5> ::= <expr-4> { "??" <expr-4> }<expr-4> ::= <expr-3> { ( "+" | "-" ) <expr-3> }<expr-3> ::= <expr-2> { ( "%" | "*" | "/" ) <expr-2> }<expr-2> ::= ( "!" | "-" ) <expr-2> | <expr-1><expr-1> ::= <primary> { "." ( <name> | <method-call> ) | ".?" <name> } (* ---- Calls ---- *)(* <predicate-name> is a spec or a trait applied to its receiver, so it is any name. It therefore also derives the retired ONE-ARGUMENT calls, which have the same tokens and which the PARSER refuses by name: count(x) write x.count() exists(x) write x != nil first(x) write x.first() last(x) write x.last() len(x) write x.count() mean(x) write x.avg() not(a) write !a Every other retired call takes a different number of arguments, and none of those derives here at all. *)<call> ::= <function-name> "(" [ <expression> { "," <expression> } ] ")" | <predicate-name> "(" <expression> ")"<predicate-name> ::= <name><method-call> ::= <method-name> "(" [ <expression> { "," <expression> } ] ")"<function-name> ::= "addDuration" | "aliasOf" | "canonicalId" | "childOf" | "contains" | "createdBy" | "daysBetween" | "equals" | "error" | "hash" | "ids" | "lower" | "owns" | "parentOf" | "references" | "secret" | "shortId" | "systemSecret" | "systemVar" | "toString" | "trim" | "upper" | "var"<method-name> ::= "all" | "any" | "avg" | "count" | "distinct" | "empty" | "first" | "groupBy" | "includes" | "last" | "max" | "min" | "nodes" | "orderBy" | "orderByDesc" | "reduce" | "select" | "single" | "skip" | "sum" | "take" | "where" (* ---- Terminals ---- *)<literal> ::= <string> | <number> | "true" | "false" | "nil"<name> ::= ( "a".."z" | "A".."Z" | "_" ) { "a".."z" | "A".."Z" | "0".."9" | "_" | "-" }<dotted-name> ::= <name> { "." <name> }<concept-name> ::= <name><bound-name> ::= <name><string> ::= '"' { <character> } '"'<number> ::= [ "-" ] <digit>+ [ "." <digit>+ ]<text> ::= { <character> }<character> ::= any Unicode code point; "\\" escapes the next one<digit> ::= "0".."9"