jinja/grammar.ebnf
Kevin Brown 7e67623cbf Only support implicit tuples in variable expressions
They cause super inconsistent parsing behaviour if they show up
pretty much anywhere else because there is no difference between an
implicit tuple and a set of comma-separated parameters.
2020-05-16 17:54:52 -04:00

664 lines
12 KiB
EBNF

start
=
expressions
$
;
expressions
=
{expression}*
;
expression
=
| content
| raw_block_expression
| block_expression
| line_block_expression
| variable_expression
| comment_expression
| line_comment_expression
;
raw_block_expression
=
raw_block_start
raw:{ !raw_block_end CHAR }*
raw_block_end
;
raw_block_start
=
block_open "raw" {SP}* block_close
;
raw_block_end
=
block_open "endraw" {SP}* block_close
;
block_expression
=
| block_expression_pair
| block_expression_single
;
block_expression_pair
=
start:block_start contents:expressions end:block_end
;
block_expression_single
=
block:block_start
;
block_start
=
block_open !("end") name:IDENTIFIER [ "(" name_call_parameters:variable_accessor_call_parameters ")" ]
[ {SP}+ parameters:block_parameters ]
{SP}* block_close
;
block_end
=
block_open "end" name:IDENTIFIER {SP}* block_close
;
block_open
=
| ( {SP}* block_open_symbol "-" {SP}* )
| block_open_symbol {SP}*
;
block_open_symbol
=
"{%"
;
block_close
=
| ( "-" block_close_symbol {SP}* )
| block_close_symbol
;
block_close_symbol
=
"%}"
;
line_block_expression
=
| line_block_expression_pair
| line_block_expression_single
;
line_block_expression_pair
=
start:line_block_start contents:expressions end:line_block_end
;
line_block_expression_single
=
block:line_block_start
;
line_block_start
=
line_block_open !("end") name:IDENTIFIER { !"\n" SP}* parameters:[ line_block_parameters ] [ { !"\n" SP }* ":" ] line_block_close
;
line_block_end
=
line_block_open "end" name:IDENTIFIER line_block_close
;
line_block_open
=
{ !"\n" SP }* line_block_open_symbol { !"\n" SP }*
;
line_block_open_symbol
=
"#"
;
line_block_close
=
| ( {SP}* $ )
| ( { !"\n" SP }* "\n" )
;
line_block_parameters
=
@+:block_parameter { { !"\n" SP }+ @+:block_parameter }*
;
block_parameters
=
@+:block_parameter
{
block_parameter_separator
@+:block_parameter
}*
;
block_parameter_separator
=
| ( {SP}* "," {SP}* )
| ( {SP}+ )
;
block_parameter
=
| block_parameter_key_value
| block_parameter_value_only
;
block_parameter_key_value
=
key:block_parameter_key {SP}* "=" {SP}* value:variable_accessor_call_parameter_value
;
block_parameter_key
=
variable_accessor_call_parameter_key
;
block_parameter_value_only
=
| value:variable_identifier_with_alias
| value:variable_accessor_call_parameter_value
| value:conditional_expression
;
variable_expression
=
variable_open type:`variable` name:variable_expression_name variable_close
;
variable_open
=
| ( {SP}* variable_open_symbol "-" {SP}* )
| ( variable_open_symbol {SP}* )
;
variable_open_symbol
=
"{{"
;
variable_close
=
| ( {SP}* "-" variable_close_symbol {SP}* )
| ( {SP}* variable_close_symbol )
;
variable_close_symbol
=
"}}"
;
variable_expression_name
=
| TUPLE_LITERAL
| conditional_expression
;
variable_identifier
=
| variable_identifier_parentheses
| variable_identifier_raw
;
variable_identifier_parentheses
=
"(" @:conditional_expression ")"
;
variable_identifier_raw
=
[ signed:( "-" | "+" ) ]
variable:( LITERAL | IDENTIFIER )
accessors:{ variable_accessor }*
{ {SP}* filters+:variable_filter }*
;
variable_identifier_with_alias
=
variable:IDENTIFIER
{SP}* "as" {SP}*
alias:IDENTIFIER
;
variable_accessor
=
| variable_accessor_brackets
| variable_accessor_call
| variable_accessor_dot
;
variable_accessor_brackets
=
accessor_type:`brackets`
"[" parameter:variable_identifier "]"
;
variable_accessor_call
=
accessor_type:`call`
"(" parameters:[variable_accessor_call_parameters] ")"
;
variable_accessor_dot
=
accessor_type:`dot`
"." parameter:( IDENTIFIER | NUMBER_LITERAL )
;
variable_accessor_call_parameters
=
@+:variable_accessor_call_parameter
{ {SP}* "," {SP}* @+:variable_accessor_call_parameter }*
;
variable_accessor_call_parameter
=
| variable_accessor_call_parameter_key_value
| variable_accessor_call_parameter_value_only
| variable_accessor_call_parameter_vararg
| variable_accessor_call_parameter_varkwarg
;
variable_accessor_call_parameter_vararg
=
"*" dynamic_argument:variable_identifier
;
variable_accessor_call_parameter_varkwarg
=
"**" dynamic_keyword_argument:variable_identifier
;
variable_accessor_call_parameter_key_value
=
key:variable_accessor_call_parameter_key {SP}* "=" {SP}* value:variable_accessor_call_parameter_value
;
variable_accessor_call_parameter_value_only
=
value:variable_accessor_call_parameter_value
;
variable_accessor_call_parameter_key
=
IDENTIFIER
;
variable_accessor_call_parameter_value
=
conditional_expression
;
conditional_expression
=
| conditional_expression_not
| conditional_expression_if
| conditional_expression_logical
| conditional_expression_operator
| conditional_expression_test
| complex_expression
| variable_identifier
| conditional_expression_parentheses
;
complex_expression
=
| complex_expression_powers
| complex_expression_math2
| concatenate_expression
| complex_expression_math1
| complex_expression_parentheses
| variable_identifier
;
complex_expression_powers
=
left:variable_identifier {SP}* math_operator:"**" {SP}* right:variable_identifier
;
complex_expression_math2
=
left:variable_identifier
{SP}* math_operator:complex_expression_math2_operations {SP}*
right:variable_identifier
;
complex_expression_math2_operations
=
| "*"
| "/"
| "//"
| "%"
;
complex_expression_math1
=
left:variable_identifier
{SP}* math_operator:complex_expression_math1_operations {SP}*
right:complex_expression
;
complex_expression_math1_operations
=
| "+"
| "-"
;
complex_expression_parentheses
=
"(" {SP}*
complex_expression
{SP}* ")"
;
conditional_expression_parentheses
=
"(" {SP}* @:conditional_expression {SP}* ")"
;
conditional_expression_not
=
"not" {SP}+ not:conditional_expression
;
conditional_expression_if
=
true_value:variable_identifier
{SP}* "if" {SP}*
test_expression:conditional_expression
[ {SP}* "else" {SP}* false_value:conditional_expression ]
;
conditional_expression_logical
=
left:conditional_expression
{SP}* logical_operator:variable_tests_logical_operator {SP}*
right:conditional_expression
;
conditional_expression_operator
=
conditional_expression_operator_in
| (
left:complex_expression
{SP}* operator:conditional_expression_operator_operations {SP}*
right:conditional_expression
)
;
conditional_expression_operator_in
=
| (
"not"
left:variable_identifier
{SP}* operator:`"notin"` "in" {SP}*
right:variable_identifier
)
| (
left:variable_identifier
{SP}+
(
| ( "not" {SP}* "in" operator:`"notin"` )
| operator:"in"
)
{SP}+
right:variable_identifier
)
;
conditional_expression_test
=
test_variable:variable_identifier
{SP}* "is"
[ {SP}+ "not" {SP} negated:`True` ]
{SP}*
test_function:variable_identifier
[
{SP}*
!( (variable_tests_logical_operator | "is" | "else" ) {SP}* )
test_function_parameter:variable_identifier
]
;
conditional_expression_operator_operations
=
| "=="
| "!="
| ">"
| ">="
| "<"
| "<="
;
variable_tests_logical_operator
=
| "and"
| "or"
;
concatenate_expression
=
concatenate+:variable_identifier
{ {SP}* "~" {SP}* concatenate+:variable_identifier }+
;
variable_filter
=
"|" {SP}* @:filter
;
filter =
variable:IDENTIFIER
accessors:{ variable_accessor_call }*
;
comment_expression
=
comment_open comment:comment_content comment_close
;
comment_open =
comment_open_symbol
;
comment_open_symbol
=
"{#"
;
comment_close
=
comment_close_symbol
;
comment_close_symbol
=
"#}"
;
comment_content
=
{ !comment_close CHAR }*
;
line_comment_expression
=
line_comment_open comment:line_comment_content &"\n"
;
line_comment_open
=
{SP}* line_comment_open_symbol
;
line_comment_open_symbol
=
'##'
;
line_comment_content
=
{ !"\n" CHAR }*
;
content
=
!(
| line_block_open
| block_open
| variable_open
| comment_open
| line_comment_open
) CHAR ;
LITERAL
=
| NONE_LITERAL
| STRING_LITERAL
| NUMBER_LITERAL
| BOOLEAN_LITERAL
| DICTIONARY_LITERAL
| LIST_LITERAL
| EXPLICIT_TUPLE_LITERAL
;
DICTIONARY_LITERAL
=
literal_type:`dictionary`
(
| ( "{" {SP}* value+:dictionary_key_value { {SP}* "," {SP}* value+:dictionary_key_value } {SP}* "}" )
| ( "{" {SP}* "}" )
)
;
dictionary_key_value
=
key:STRING_LITERAL {SP}* ":" {SP}* value:variable_identifier
;
LIST_LITERAL
=
literal_type:`list`
(
| ( "[" {SP}* value+:variable_identifier {SP}* { "," {SP}* value+:variable_identifier }* {SP}* "]" )
| ( "[" {SP}* "]" )
)
;
TUPLE_LITERAL
=
| EXPLICIT_TUPLE_LITERAL
| IMPLICIT_TUPLE_LITERAL
| EMPTY_TUPLE_LITERAL
;
EXPLICIT_TUPLE_LITERAL
=
literal_type:`tuple`
"(" value:TUPLE_LITERAL_CONTENTS ")"
;
IMPLICIT_TUPLE_LITERAL
=
literal_type:`tuple`
value:TUPLE_LITERAL_CONTENTS
;
TUPLE_LITERAL_CONTENTS
=
| ( @+:variable_identifier {SP}* { "," {SP}* @+:variable_identifier {SP}* }+ )
| ( @+:variable_identifier {SP}* "," {SP}* )
;
EMPTY_TUPLE_LITERAL
=
literal_type:`tuple`
"(" {SP}* ")"
;
INTEGER_LITERAL
=
/[\d_]*\d+/
;
SIGNED_INTEGER_LITERAL
=
/[+-]?[\d_]*\d+/
;
NUMBER_LITERAL
=
literal_type:`number`
whole:INTEGER_LITERAL
[ "." fractional:INTEGER_LITERAL ]
[ ( "e" | "E" ) exponent:SIGNED_INTEGER_LITERAL ]
;
STRING_LITERAL
=
| STRING_LITERAL_SINGLE_QUOTE
| STRING_LITERAL_DOUBLE_QUOTE
;
STRING_LITERAL_SINGLE_QUOTE
=
literal_type:`string`
"'" value:{ !"'" /./ }* "'"
;
STRING_LITERAL_DOUBLE_QUOTE
=
literal_type:`string`
'"' value:{ !'"' /./ }* '"'
;
BOOLEAN_LITERAL
=
literal_type:`boolean`
(
| ( ("true" | "True") value:`True`)
| ( ("false" | "False") value:`False`)
)
;
NONE_LITERAL
=
literal_type:`none`
( "none" | "None" ) value:`None`
;
IDENTIFIER
=
/[a-zA-Z_][a-zA-Z0-9_]*/
;
ALPHA
=
/[a-zA-Z]/
;
DIGIT
=
/[0-9]/
;
SP
=
/\s/
;
CHAR
=
| ?'.'
| ?'\s'
;