/
germanubis
/
tera
Обзор
Документация
Войти
/
germanubis
/
tera
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/parser/tera.pest
320 строк
10 KB
Patrick Ackland
Fix for square bracket identifiers breaking tests (#839)
27 май 2024, 22:06
Не верифицирован
27 май 2024, 22:06
7b3942b
Код
Авторство
О чём код?
// More about pest syntax https://pest.rs/book/grammars/syntax.html // Built-in rules (WHITESPACE, ANY, SOI, DOI and others) https://pest.rs/book/grammars/built-ins.html // ----------------------------------------------- WHITESPACE = _{ " " | "\t" | "\r" | "\n" } /// LITERALS int = @{ "-" ? ~ ("0" | '1'..'9' ~ '0'..'9' * ) } float = @{ "-" ? ~ ( "0" ~ "." ~ '0'..'9' + | '1'..'9' ~ '0'..'9' * ~ "." ~ '0'..'9' + ) } // matches anything between 2 double quotes double_quoted_string = @{ "\"" ~ (!("\"") ~ ANY)* ~ "\""} // matches anything between 2 single quotes single_quoted_string = @{ "\'" ~ (!("\'") ~ ANY)* ~ "\'"} // matches anything between 2 backquotes\backticks backquoted_quoted_string = @{ "`" ~ (!("`") ~ ANY)* ~ "`"} string = @{ double_quoted_string | single_quoted_string | backquoted_quoted_string } boolean = { "true" | "false" | "True" | "False" } // ----------------------------------------------- /// OPERATORS op_or = @{ "or" ~ WHITESPACE } op_and = @{ "and" ~ WHITESPACE } op_not = @{ "not" ~ WHITESPACE } op_lte = { "<=" } op_gte = { ">=" } op_lt = { "<" } op_gt = { ">" } op_eq = { "==" } op_ineq = { "!=" } op_plus = { "+" } op_minus = { "-" } op_times = { "*" } op_slash = { "/" } op_modulo = { "%" } // ------------------------------------------------- /// Idents all_chars = _{'a'..'z' | 'A'..'Z' | "_" | '0'..'9'} // Used everywhere where an ident is used, except when accessing // data from the context. // Eg block name, argument name, macro name etc ident = @{ ('a'..'z' | 'A'..'Z' | "_") ~ all_chars* } // The context_ident used to get data from the context. // Same as ident but allows `.` in it dotted_ident = @{ ('a'..'z' | 'A'..'Z' | "_") ~ all_chars* ~ ("." ~ all_chars+)* } square_brackets = @{ "[" ~ (int | string | dotted_square_bracket_ident) ~ "]" } dotted_square_bracket_ident = @{ dotted_ident ~ ( ("." ~ all_chars+) | square_brackets )* } string_concat = { (fn_call | float | int | string | dotted_square_bracket_ident) ~ ("~" ~ (fn_call | float | int | string | dotted_square_bracket_ident))+ } // ---------------------------------------------------- /// EXPRESSIONS /// We'll use precedence climbing on those in the parser phase // boolean first so they are not caught as identifiers basic_val = _{ boolean | test_not | test | macro_call | fn_call | dotted_square_bracket_ident | float | int } basic_op = _{ op_plus | op_minus | op_times | op_slash | op_modulo } basic_expr = { ("(" ~ basic_expr ~ ")" | basic_val) ~ (basic_op ~ ("(" ~ basic_expr ~ ")" | basic_val))* } basic_expr_filter = !{ basic_expr ~ filter* } string_expr_filter = !{ (string_concat | string) ~ filter* } comparison_val = { basic_expr_filter ~ (basic_op ~ basic_expr_filter)* } comparison_op = _{ op_lte | op_gte | op_gt | op_lt | op_eq | op_ineq } comparison_expr = { (string_expr_filter | comparison_val) ~ (comparison_op ~ (string_expr_filter | comparison_val))? } // The `in` operator in_cond_container = {string_expr_filter | array_filter | dotted_square_bracket_ident} in_cond = !{ (string_expr_filter | basic_expr_filter) ~ op_not? ~ "in" ~ in_cond_container } logic_val = !{ op_not? ~ (in_cond | comparison_expr) | "(" ~ logic_expr ~ ")" } logic_expr = !{ logic_val ~ ((op_or | op_and) ~ logic_val)* } array = !{ "[" ~ (logic_val ~ ",")* ~ logic_val? ~ "]"} array_filter = !{ array ~ filter* } string_array = !{ "[" ~ (string ~ ",")* ~ string? ~ "]"} // ---------------------------------------------------- /// FUNCTIONS & FILTERS // A keyword argument: something=10, something="a value", something=1+10 etc kwarg = { ident ~ "=" ~ (logic_expr | array_filter) } kwargs = _{ kwarg ~ ("," ~ kwarg )* ~ ","? } fn_call = !{ ident ~ "(" ~ kwargs? ~ ")" } filter = { "|" ~ (fn_call | ident) } // ------------------------------------------------------ /// MACROS // A macro argument can have default value, only a literal though macro_def_arg = ${ (ident ~ "=" ~ (boolean | string | float | int)) | ident } macro_def_args = _{ macro_def_arg ~ ("," ~ macro_def_arg)* } macro_fn = _{ ident ~ "(" ~ macro_def_args? ~ ")" } macro_fn_wrapper = !{ macro_fn } macro_call = { ident ~ "::" ~ ident ~ "(" ~ kwargs? ~ ")" } // ------------------------------------------------------- /// TESTS // It's a bit weird that tests are the only thing in Tera not using kwargs // but at the same time it's one arg most of the time so... test_arg = { logic_expr | array_filter } test_args = _{ test_arg ~ ("," ~ test_arg)* } test_call = !{ ident ~ ("(" ~ test_args ~ ")")? } test_not = { dotted_square_bracket_ident ~ "is" ~ "not" ~ test_call } test = { dotted_square_bracket_ident ~ "is" ~ test_call } // ------------------------------------------------------- /// TERA // All the blocks that Tera recognises variable_start = { "{{-" | "{{" } variable_end = { "-}}" | "}}" } // whitespace control tag_start = { "{%-" | "{%" } tag_end = { "-%}" | "%}" } comment_start = { "{#-" | "{#" } comment_end = { "-#}" | "#}" } block_start = _{ variable_start | tag_start | comment_start } comment_text = ${ (!(comment_end) ~ ANY)+ } // Tag marks ignore_missing = { "ignore" ~ WHITESPACE* ~ "missing" } // Actual tags include_tag = ${ tag_start ~ WHITESPACE* ~ "include" ~ WHITESPACE+ ~ (string | string_array) ~ WHITESPACE* ~ ignore_missing? ~ WHITESPACE* ~ tag_end } comment_tag = ${ comment_start ~ comment_text ~ comment_end } block_tag = ${ tag_start ~ WHITESPACE* ~ "block" ~ WHITESPACE+ ~ ident ~ WHITESPACE* ~ tag_end } macro_tag = ${ tag_start ~ WHITESPACE* ~ "macro" ~ WHITESPACE+ ~ macro_fn_wrapper ~ WHITESPACE* ~ tag_end } if_tag = ${ tag_start ~ WHITESPACE* ~ "if" ~ WHITESPACE+ ~ logic_expr ~ WHITESPACE* ~ tag_end } elif_tag = ${ tag_start ~ WHITESPACE* ~ "elif" ~ WHITESPACE+ ~ logic_expr ~ WHITESPACE* ~ tag_end } else_tag = !{ tag_start ~ "else" ~ tag_end } for_tag = ${ tag_start ~ WHITESPACE* ~ "for"~ WHITESPACE+ ~ ident ~ ("," ~ WHITESPACE* ~ ident)? ~ WHITESPACE+ ~ "in" ~ WHITESPACE+ ~ (basic_expr_filter | array_filter) ~ WHITESPACE* ~ tag_end } filter_tag = ${ tag_start ~ WHITESPACE* ~ "filter" ~ WHITESPACE+ ~ (fn_call | ident) ~ WHITESPACE* ~ tag_end } set_tag = ${ tag_start ~ WHITESPACE* ~ "set" ~ WHITESPACE+ ~ ident ~ WHITESPACE* ~ "=" ~ WHITESPACE* ~ (logic_expr | array_filter) ~ WHITESPACE* ~ tag_end } set_global_tag = ${ tag_start ~ WHITESPACE* ~ "set_global" ~ WHITESPACE+ ~ ident ~ WHITESPACE* ~ "=" ~ WHITESPACE* ~ (logic_expr | array_filter) ~ WHITESPACE* ~ tag_end } endblock_tag = !{ tag_start ~ "endblock" ~ ident? ~ tag_end } endmacro_tag = !{ tag_start ~ "endmacro" ~ ident? ~ tag_end } endif_tag = !{ tag_start ~ "endif" ~ tag_end } endfor_tag = !{ tag_start ~ "endfor" ~ tag_end } endfilter_tag = !{ tag_start ~ "endfilter" ~ tag_end } break_tag = !{ tag_start ~ "break" ~ tag_end } continue_tag = !{ tag_start ~ "continue" ~ tag_end } variable_tag = !{ variable_start ~ (logic_expr | array_filter) ~ variable_end } super_tag = !{ variable_start ~ "super()" ~ variable_end } text = ${ (!(block_start) ~ ANY)+ } raw_tag = !{ tag_start ~ "raw" ~ tag_end } endraw_tag = !{ tag_start ~ "endraw" ~ tag_end } raw_text = ${ (!endraw_tag ~ ANY)* } raw = ${ raw_tag ~ raw_text ~ endraw_tag } filter_section = ${ filter_tag ~ filter_section_content* ~ endfilter_tag } forloop = ${ for_tag ~ for_content* ~ (else_tag ~ for_content*)* ~ endfor_tag } macro_if = ${ if_tag ~ macro_content* ~ (elif_tag ~ macro_content*)* ~ (else_tag ~ macro_content*)? ~ endif_tag } block_if = ${ if_tag ~ block_content* ~ (elif_tag ~ block_content*)* ~ (else_tag ~ block_content*)? ~ endif_tag } for_if = ${ if_tag ~ for_content* ~ (elif_tag ~ for_content*)* ~ (else_tag ~ for_content*)? ~ endif_tag } filter_section_if = ${ if_tag ~ filter_section_content* ~ (elif_tag ~ filter_section_content*)* ~ (else_tag ~ filter_section_content*)? ~ endif_tag } content_if = ${ if_tag ~ content* ~ (elif_tag ~ content*)* ~ (else_tag ~ content*)? ~ endif_tag } block = ${ block_tag ~ block_content* ~ endblock_tag } macro_definition = ${ macro_tag ~ macro_content* ~ endmacro_tag } filter_section_content = @{ include_tag | variable_tag | comment_tag | set_tag | set_global_tag | block | forloop | filter_section_if | raw | filter_section | text } // smaller sets of allowed content in macros macro_content = @{ include_tag | variable_tag | comment_tag | set_tag | set_global_tag | macro_if | forloop | filter_section | raw | text } // smaller set of allowed content in block block_content = @{ include_tag | super_tag | variable_tag | comment_tag | set_tag | set_global_tag | block | block_if | forloop | filter_section | raw | text } // set of allowed content inside for loops for_content = @{ include_tag | variable_tag | comment_tag | set_tag | set_global_tag | for_if | forloop | break_tag | continue_tag | filter_section | raw | text } content = @{ include_tag | variable_tag | comment_tag | set_tag | set_global_tag | block | content_if | forloop | filter_section | raw | text } extends_tag = ${ WHITESPACE* ~ tag_start ~ WHITESPACE* ~ "extends" ~ WHITESPACE+ ~ string ~ WHITESPACE* ~ tag_end ~ WHITESPACE* } import_macro_tag = ${ WHITESPACE* ~ tag_start ~ WHITESPACE* ~ "import" ~ WHITESPACE+ ~ string ~ WHITESPACE+ ~ "as" ~ WHITESPACE+ ~ ident ~ WHITESPACE* ~ tag_end ~ WHITESPACE* } top_imports = _{ (extends_tag ~ import_macro_tag*) | (import_macro_tag+ ~ extends_tag?) } // top level rule template = ${ SOI ~ comment_tag* ~ top_imports? ~ (content | macro_definition)* // macro_definition must be top-level ~ EOI }