couchbase-lite-core
couchbase-lite-core copied to clipboard
Grammar railroad diagram
Using a modified peg/leg from here https://github.com/mingodad/peg to convert the LiteCore/Query/N1QL_Parser/n1ql.leg in an EBNF understood by https://www.bottlecaps.de/rr/ui to generate a nice railroad diagram (https://en.wikipedia.org/wiki/Syntax_diagram) to help show/understand the syntax.
Command to generate leg -e syntax.leg then some minor manual fixes are still needed.
Copy and paste the EBNF shown bellow at https://www.bottlecaps.de/rr/ui on the tab Edit Grammar then click on the tab View Diagram.
//To be viewd at https://www.bottlecaps.de/rr/ui
n1ql ::=
( _ selectStatement _ _NOT_ . )
| ( selectResults _NOT_ . )
_ ::=
[ \t\r\n]*
selectStatement ::=
SELECT _ ( ( DISTINCT ) | ALL )? selectResults _ ( from _ )? ( WHERE expression )? ( groupBy _ ( having )? )? ( orderBy _ )? ( ( LIMIT expression ( OFFSET expression )? ) | ( OFFSET expression ( LIMIT expression )? ) )? ( _ ';' )?
selectResults ::=
selectResult ( _ ',' _ selectResult )*
SELECT ::=
"SELECT" WB
DISTINCT ::=
"DISTINCT" WB
ALL ::=
"ALL" WB
from ::=
FROM dataSource ( _ join )*
WHERE ::=
"WHERE" WB
expression ::=
expr8
groupBy ::=
GROUP BY expression ( _ ',' _ expression )*
having ::=
HAVING expression
orderBy ::=
ORDER BY ordering ( _ ',' _ ordering )*
LIMIT ::=
"LIMIT" WB
OFFSET ::=
"OFFSET" WB
selectResult ::=
expression ( _ AS? columnAlias )?
AS ::=
"AS" WB
columnAlias ::=
IDENTIFIER
FROM ::=
"FROM" WB
dataSource ::=
collectionName ( AS? collectionAlias )?
join ::=
joinOperator _ dataSource _ ( ON expression )?
collectionName ::=
IDENTIFIER ( '.' IDENTIFIER )?
collectionAlias ::=
IDENTIFIER
IDENTIFIER ::=
( [a-zA-Z_] [a-zA-Z0-9_$]* _ )
| ( "`" ( [^`] | "``" )* "`" _ )
joinOperator ::=
( ( LEFT OUTER? ) | INNER | CROSS )? JOIN
ON ::=
"ON" WB
LEFT ::=
"LEFT" WB
OUTER ::=
"OUTER" WB
INNER ::=
"INNER" WB
CROSS ::=
"CROSS" WB
JOIN ::=
"JOIN" WB
GROUP ::=
"GROUP" WB
BY ::=
"BY" WB
HAVING ::=
"HAVING" WB
ORDER ::=
"ORDER" WB
ordering ::=
expression ( _ order )?
order ::=
( ASC | DESC )
ASC ::=
"ASC" WB
DESC ::=
"DESC" WB
indexName ::=
IDENTIFIER
expr8 ::=
expr7 ( _ OP_PREC_8 _ expr7 )*
caseExpression ::=
CASE ( _NOT_ WHEN expression )? ( WHEN expression THEN expression )+ ( ELSE expression )? END
CASE ::=
"CASE" WB
WHEN ::=
"WHEN" WB
THEN ::=
"THEN" WB
ELSE ::=
"ELSE" WB
END ::=
"END" WB
anyEveryExpression ::=
anyEvery _ variableName _ IN _ expression _ SATISFIES _ expression END
anyEvery ::=
( anyOrSome AND EVERY )
| ( anyOrSome )
| ( EVERY )
variableName ::=
IDENTIFIER
IN ::=
"IN" WB
SATISFIES ::=
"SATISFIES" WB
anyOrSome ::=
ANY
| SOME
AND ::=
"AND" WB
EVERY ::=
"EVERY" WB
ANY ::=
"ANY" WB
SOME ::=
"SOME" WB
expr7 ::=
expr6 ( _ OP_PREC_7 _ expr6 )*
OP_PREC_8 ::=
OR
expr6 ::=
( expr5 NOT NULL )
| ( expr5 IS ( ( NULL ) | ( MISSING ) | ( VALUED ) ) )
| ( expr5 IS NOT ( ( NULL ) | ( MISSING ) | ( VALUED ) ) )
| inExpression
| likeExpression
| betweenExpression
| ( expr5 ( _ OP_PREC_6 _ expr5 )* )
OP_PREC_7 ::=
AND
expr5 ::=
expr4 ( _ OP_PREC_5 _ expr4 )*
NOT ::=
"NOT" WB
NULL ::=
"NULL" WB
IS ::=
"IS" WB
MISSING ::=
"MISSING" WB
VALUED ::=
"VALUED" WB
inExpression ::=
expr5 IN_OR_NOT ( ( selectExpr ) | ( parenExprs ) )
likeExpression ::=
expr5 _ NOT? LIKE expr5
betweenExpression ::=
expr5 NOT? BETWEEN expr5 AND expr5
OP_PREC_6 ::=
( ( '==' | '=' ) )
| ( ( '<>' | '!=' ) )
| ( IS NOT )
| ( IS )
expr4 ::=
expr3 ( _ OP_PREC_4 _ expr3 )*
OP_PREC_5 ::=
( '<=' | '<' | '>=' | '>' )
expr3 ::=
expr2 ( _ OP_PREC_3 _ expr2 )*
OP_PREC_4 ::=
( '<<' | '>>' | '&' | '|' )
expr2 ::=
expr1 ( _ OP_PREC_2 _ expr1 )*
OP_PREC_3 ::=
[-+]
expr1 ::=
expr0 ( _ OP_PREC_1 _ expr0 )*
OP_PREC_2 ::=
[*/%]
expr0 ::=
( baseExpr '.' propertyPath )
| ( baseExpr _ collateSuffix )
| ( baseExpr )
OP_PREC_1 ::=
'||'
LIKE ::=
"LIKE" WB
BETWEEN ::=
"BETWEEN" WB
OR ::=
"OR" WB
IN_OR_NOT ::=
( NOT IN )
| ( IN )
selectExpr ::=
'(' selectStatement ')'
parenExprs ::=
_ '(' _ ( expression ( ',' _ expression )* )? ')'
baseExpr ::=
_baseExpr _
propertyPath ::=
propertyName ( ( '.' _ propertyName ) | ( '[' _ INT_LITERAL _ ']' _ ) )*
collateSuffix ::=
COLLATE ( ( collation _ _NOT_ collation ) | ( '(' _ ( collation _ )+ ')' _ ) )
COLLATE ::=
"COLLATE" WB
collation ::=
( "NO"? ( "UNICODE" | "CASE" | "DIAC" ) WB )
WB ::=
_NOT_ [a-zA-Z0-9_] _
_baseExpr ::=
literal
| arrayLiteral
| dictLiteral
| ( OP_PREFIX _ baseExpr )
| ( EXISTS selectExpr )
| caseExpression
| anyEveryExpression
| ( '$' IDENTIFIER )
| function
| property
| ( '(' _ expression _ ')' )
literal ::=
FLOAT_LITERAL
| INT_LITERAL
| BOOLEAN_LITERAL
| STRING_LITERAL
| ( NULL )
| ( MISSING )
arrayLiteral ::=
'[' _ ( expression ( _ ',' _ expression )* )? ']'
dictLiteral ::=
'{' _ ( STRING_LITERAL ':' _ expression ( _ ',' _ STRING_LITERAL ':' _ expression )* )? '}'
OP_PREFIX ::=
( '-' | '+' | NOT )
EXISTS ::=
"EXISTS" WB
function ::=
( "meta" _ '(' _ ( collectionName _ )? ')' _ )
| ( "match" _ '(' _ indexName _ ',' _ expression _ ')' _ )
| ( "rank" _ '(' _ indexName _ ')' _ )
| ( functionName parenExprs )
property ::=
( '*' )
| ( collectionAlias '.' _ '*' )
| ( propertyPath )
propertyName ::=
IDENTIFIER
INT_LITERAL ::=
'-'? DIGIT+ WB
functionName ::=
IDENTIFIER
FALSE ::=
"FALSE" WB
TRUE ::=
"TRUE" WB
STRING_LITERAL ::=
( "'" ( [^'] | "''" )* "'" _ )
| ( '"' ( [^"] | '""' )* '"' _ )
FLOAT_LITERAL ::=
'-'? ( ( '.' DIGIT+ ) | ( ( DIGIT+ ( '.' DIGIT* ) ( [Ee] [-+]? DIGIT+ )? ) | ( DIGIT+ ( [Ee] [-+]? DIGIT+ ) ) ) ) WB
BOOLEAN_LITERAL ::=
( TRUE )
| ( FALSE )
DIGIT ::=
[0-9]
//Added tokens for railroad generation
_NOT_ ::= '!'
_AND_ ::= '&'
Very nice, I will show this to our docs team.
Awesome!