Grammar
The query language's grammar in EBNF, read from the parser's source when the site is built.
This is the grammar datalog.Parse reads, copied from the comment on Parse in datalog/parse.go when the site is built, so it’s the parser’s own description of itself. A # outside a string starts a comment that runs to the end of the line, and whitespace is insignificant.
query = { rule end } goal [ "." ] ; (* zero or more rules, then one goal *)
end = ";" | "." ; (* a "." only before whitespace or the end, #276 *)
rule = head ":-" literals ; (* head :- body; defines a derived relation *)
head = relation "(" [ harg { "," harg } ] ")" ;
harg = term [ ":" decl ] | aggregate | expr ; (* a declared type: ?n: net, see parseArgType *)
goal = literals [ "=>" projection [ "having" havings ] [ "order" "by" orders ]
[ "limit" count ] [ "offset" count ] ]
| ( "?-" | "=>" ) literals ; (* asks literals with no projection, #276 *)
literals = literal { "," literal } ;
literal = atom [ "as" variable ] | ( "not" | "!" ) atom | comparison | bodyagg | optional ; (* "not atom" = stratified negation; "as ?r" binds the fact, #279 *)
optional = "optional" "{" literals "}" ; (* holds whether literals match; what only they bind is absent when not, #282 *)
bodyagg = variable "=" aggregate ":" "{" literals "}" ; (* per shared variable's value, #71 *)
atom = relation "(" [ term { "," term } ] ")" ;
comparison = operand op operand ;
operand = term | expr ;
expr = sum ; (* arithmetic over num.* and functions, #5 *)
sum = product { ( "+" | "-" ) product } ;
product = unary { ( "*" | "/" ) unary } ;
unary = "-" unary | "(" expr ")" | call | term ;
call = relation "(" [ expr { "," expr } ] ")" ; (* a function relation; its last argument is the result *)
op = "<" | "<=" | "=" | "!=" | ">" | ">=" ;
projection = column { "," column } ;
column = variable | aggregate | expr ; (* an expr column is labelled as written without ?s, #206 *)
aggregate = aggfunc "(" [ "distinct" ] variable ")" ; (* grouped by the variable columns *)
aggfunc = "count" | "min" | "max" | "sum" | "avg" | "list" ;
havings = having { "," having } ;
having = aggregate op term ; (* filters GROUPS, after the reduce *)
orders = order { "," order } ;
order = column [ "asc" | "desc" ] ; (* a selected column; ties keep the default order *)
count = digit { digit } ;
term = variable | string | number ;
variable = "?" ident | "_" ;
ident = ( letter | digit | "_" ) { letter | digit | "_" } ; (* ASCII *)
string = '"' { char | "\\" char } '"' ; (* kept as written: \" is a backslash and a quote *)
number = [ "+" | "-" ] digit { digit } [ "." digit { digit } ] [ ( "e" | "E" ) [ "+" | "-" ] digit { digit } ] ;
relation = ident { "." | "-" | ident } ; (* a path: net.max_voltage, component-on-net *)
Every clause at once ΒΆ
One query using each part of the grammar: a rule with a comment and a declared type, then a goal with a join, a negation and a comparison, and a projection with aggregates, having, order by, limit and offset.
# lines of code each team owns, from its packages' sizes
size(?p, ?n: number) :- loc(?p, ?n);
owner(?p, ?t), size(?p, ?n), not test(_, ?p), ?n > 100
=> ?t, count(?p), sum(?n)
having count(?p) >= 1
order by sum(?n) desc
limit 3 offset 1| ?t | count(?p) | sum(?n) |
|---|---|---|
| platform | 3 | 600 |
| data | 1 | 400 |
deps datasetThe syntax page of the guide walks through the same forms one at a time.