
WHITESPACE = _{ " " | "\t" | "\r" | NEWLINE }
COMMENT = _{ ( "/*" ~ (!"*/" ~ ANY)* ~ "*/" ) | ("//" ~ (!(NEWLINE) ~ ANY)* ~ NEWLINE) }


////// ////// //////
/// BEGIN TEMPLATE
//////

//A template is expressed as an XML-like document with support for
//property binding, control-flow (if, for) and {}-wrapped embedded expressions

//A component definition requires at least one element in its template; a `@settings` block may also be included, and any future relevant blocks like `@defaults`
//The parser will willingly _parse_ multiple @settings/@template blocks per component definition, but the compiler won't presently support them
pax_component_definition = { SOI ~ (root_tag_pair | settings_block_declaration | handlers_block_declaration | block_level_error)+ ~ EOI | empty }
empty = {SOI}
block_level_error = { (!(root_tag_pair | settings_block_declaration | handlers_block_declaration) ~ ANY)+}
root_tag_pair = { any_tag_pair }
any_tag_pair = _{statement_control_flow | matched_tag | self_closing_tag | tag_error}
tag_error = { "<" ~ pascal_identifier ~ (!("<" ~ pascal_identifier | "@handlers" | "@settings") ~ ANY)* ~ WHITESPACE* }

//This duo describes an XML-style open-tag, like <SomeElement id="..."> 
//and matching close-tag, like </SomeElement>.  Note the use of Pest's stack feature, `PUSH`
//and `POP`, to match closing & opening tags
open_tag = {"<" ~ PUSH(pascal_identifier) ~ (attribute_key_value_pair| attribute_key_value_pair_error) * ~ ">"}
open_tag_error = {"<" ~ PUSH(pascal_identifier) ~ (attribute_key_value_pair| attribute_key_value_pair_error)*}
closing_tag = {"<" ~ "/" ~ POP ~ ">"}

//Describes a (leaf-node) self-closing element, like <SomeElement />
self_closing_tag = {"<" ~ pascal_identifier ~ (attribute_key_value_pair | attribute_key_value_pair_error)* ~ "/" ~ ">"}

attribute_key_value_pair_error = { (!(attribute_key_value_pair | "/" | ">" | "<")  ~ ANY)+}

//Describes an XML subtree surrounded by a pair of matching tags, like
//<SomeElement>...</SomeElement>
matched_tag = {(open_tag | open_tag_error) ~ inner_nodes ~ closing_tag}
inner_nodes = { node_inner_content | (any_tag_pair | inner_tag_error)* }
inner_tag_error = {(!(any_tag_pair| "}" | ("<"~"/")  ) ~ ANY)+}

//Describes an atomic symbolic identifier, like `id`, `Engine`, or `some_thing`
identifier = @{ (ASCII_ALPHA | "_") ~ (ASCII_ALPHANUMERIC | "_")* }

//Describes a symbolic identifier with an Uppercase first letter, a la PascalCase
//Does not enforce the presence of any other uppercase letters.
//Intended as convention for symbolic ids in expressions, e.g. `Engine`, specification
//of explicit types in polymorphic/enum contexts (e.g. `fill: Color {...}`), and
//for namespaced access of symbolic ids, like `Orientation::Vertical`
pascal_identifier = @{ ASCII_ALPHA_UPPER ~ (ASCII_ALPHANUMERIC | "_")*}

//Describes an attribute k/v pair like `id="some_element"`. Supports expressions.
attribute_key_value_pair = {attribute_event_binding | (identifier ~ "=" ~ any_template_value)}
attribute_event_id = {"@" ~ identifier}
attribute_event_binding = {attribute_event_id ~ "=" ~ literal_function}

//`...=5.0`, `...={...}`, `id=some_id`
any_template_value = {literal_value | literal_object | expression_wrapped | identifier}

//For example: <Text>"This is my inner content"</Text>
//Presumably this content can be bare literal values other than strings like Color::hlca(...)
//It could also be an `{...}` expression
node_inner_content = { literal_value | expression_wrapped }



//string/inner/char from https://pest.rs/book/examples/json.html
string = ${ ("\"" ~ inner ~ "\"") | ("'" ~ inner ~ "'") | ("`" ~ (inner_templated | inner) ~ "`") }
inner = @{ char* }
char = {
    !("\"" | "\\") ~ ANY
    | "\\" ~ ("\"" | "\\" | "/" | "b" | "f" | "n" | "r" | "t")
    | "\\" ~ ("u" ~ ASCII_HEX_DIGIT{4})
}

inner_templated = { (char* ~ ("{" ~ inner_templated_dynamic ~ "}"))+ }
inner_templated_dynamic = {char*}


////// ////// //////
/// BEGIN SETTINGS
//////

settings_block_declaration = {"@" ~ "settings" ~ "{" ~ (selector_block | selector_block_error)* ~ "}"}
selector_block_error = {(!(WHITESPACE | "}") ~ ANY)+ ~ "}"}
selector_block = {selector ~ literal_object}
literal_object = { pascal_identifier? ~ "{" ~ (settings_key_value_pair)* ~ "}" }
//Describes a CSS-style selector, used for joining settings to elements
//Note: only basic `id` and `class` syntax supported for now; could be extended
//Example: `#some-element`
selector = {("." | "#") ~ identifier}

//Describes a key-value pair in a settings block, which supports a number of formats,
//included recursive nesting via `property_block`
settings_key_value_pair = { settings_key ~ settings_value ~ ","? }
settings_key = { identifier ~ (":" | "=") } //Offer some grace here, since our borrowing of HTML/CSS semantics means we inherit the mismatch between xml-like `=` and json-like `:`.  Let's allow both and let linters deal with cleaning up mismatches.
settings_value = { literal_value | literal_object | expression_wrapped }
////// ////// //////
/// BEGIN HANDLERS
//////

handlers_block_declaration = {"@" ~ "handlers" ~ "{" ~ (handlers_key_value_pair | handler_key_value_pair_error)* ~ "}"}
handler_key_value_pair_error = {(!(handlers_key_value_pair | "}") ~ ANY)+}
//Describes a key-value pair in a event handlers block;
//keys are event names and values are functions to call
handlers_key_value_pair = { handlers_key ~ handlers_value ~ ","? }
handlers_key = { identifier ~ ":" }
handlers_value = { literal_function | function_list }
literal_function = { "self."? ~ identifier ~ ","? }
function_list = {"[" ~ literal_function* ~ "]"}

literal_value = { literal_number_with_unit | literal_number | literal_tuple | literal_enum_value | literal_boolean | string }
literal_number_with_unit = { literal_number ~ literal_number_unit }
literal_number = {"-"? ~ (literal_number_float | literal_number_integer)}
literal_number_integer = {(!(".") ~ ASCII_DIGIT)+ }
literal_number_float = {ASCII_DIGIT* ~ "." ~ ASCII_DIGIT+}
literal_number_unit = {("%" | "px" | "deg" | "rad")}
literal_tuple = {("(") ~ literal_value ~ ("," ~ literal_value)* ~ (")")}
literal_boolean = {("true" | "false")}


//Enums like Orientation::Vertical or Color::Rgba(100%, 100%, 0, 0)
//Note that this is parsed separately from expression enums, `xo_enum*`
literal_enum_value = {pascal_identifier ~ ("::" ~ pascal_identifier)+ ~ ("("~literal_enum_args_list~")")?}
literal_enum_args_list = {literal_value ~ ("," ~ literal_value)*}

literal_tuple_access = {identifier ~ "." ~ literal_number_integer}


////// ////// //////
/// BEGIN EXPRESSIONS
/// This sub-grammar describes PAXEL, the Pax Expression Language
//////

// Expression body may be a binary operation like `x + 5` or `num_clicks % 2 == 0`
// or a literal returned value like `Color { ... }` or `5`
//
// If we wish to include postfix operators, or e.g. refactor `px` and `%` to be treated as postfix operators,
// the following is the order of xo that the Pratt parser expects
// expr_with_postfix  =   { xo_prefix* ~ xo_primary ~ xo_postfix* ~ (xo_infix ~ xo_prefix* ~ xo_primary ~ xo_postfix* )* }
expression_body =   { xo_prefix* ~ xo_primary ~ (xo_infix ~ xo_prefix* ~ xo_primary )* }

expression_wrapped = _{
    "{" ~ (expression_body| expression_body_error) ~ "}"
}

expression_body_error = { ( !(expression_body | "}") ~ ANY) + }

expression_grouped = { "(" ~ expression_body ~ ")" ~ literal_number_unit? }

/*
Some examples of valid expressions:

[Object construction]
Color {h: 360, s: 1, l: 1, a: 1}

[Object construction with implicit type (type enforced by downstream compiler)
{h: 360, s: 1, l: 1, a: 1}

[Boolean statements]
num_clicks % 2 == 0

[Complex statements including ternaries, grouping, logical operators, and object construction]
(num_clicks % 2 == 0 && is_selected) ?
    {r: 255 * color_intensity, g: 0, b: 0, a: 1} :
    {r: 0, g: 255 * color_intensity, b: 0, a: 1}

[String literals + operations]
"Is " + (is_selected ? "" : "not ") + "selected."
*/

//`xo` is short for both "expression operator" and "expression operand", collectively all symbols
//that can be expressed inside expressions

xo_primary = _{ expression_grouped | xo_function_call | xo_object | xo_range | xo_tuple | xo_list | xo_literal  | xo_symbol }

xo_prefix = _{xo_neg | xo_bool_not}
    xo_neg = {"-"}
    xo_bool_not = {"!"}

xo_infix = _{
    xo_add |
    xo_bool_and |
    xo_bool_or |
    xo_div |
    xo_exp |
    xo_mod |
    xo_mul |
    xo_rel_eq |
    xo_rel_gt |
    xo_rel_gte |
    xo_rel_lt |
    xo_rel_lte |
    xo_rel_neq |
    xo_sub |
    xo_tern_then |
    xo_tern_else
}
    xo_add = {"+"}
    xo_bool_and = {"&&"}
    xo_bool_or = {"||"}
    xo_div = {"/"}
    xo_exp = {"^"}
    xo_mod = {"%%"}
    xo_mul = {"*"}
    xo_rel_eq = {"=="}
    xo_rel_gt = {">"}
    xo_rel_gte = {">="}
    xo_rel_lt = {"<"}
    xo_rel_lte = {"<="}
    xo_rel_neq = {"!="}
    xo_sub = {"-"}
    xo_tern_then = {"?"}
    xo_tern_else = {":"}

xo_range = { (xo_literal | xo_symbol) ~ (xo_range_exclusive) ~ (xo_literal | xo_symbol)}
    xo_range_exclusive = @{".."}
//     xo_range_inclusive = @{"..="}

xo_literal = {literal_enum_value | literal_tuple_access | literal_number_with_unit | literal_number  | string | literal_tuple }

//objects may recurse into arbitrary expressions for any value -- consider the `key_2` in:
// `some_prop={ TypedReturn {key_0: 0, key_1: "one", key_2: 1.0 + 1.0} }`
xo_object = { identifier? ~ "{" ~ xo_object_settings_key_value_pair* ~ "}" }
xo_object_settings_key_value_pair = { settings_key ~ expression_body ~ ","? }

xo_symbol = { "$"? ~ identifier ~ (("." ~ identifier) | ("[" ~ expression_body ~ "]") )* }
xo_tuple = { "(" ~ expression_body ~ ("," ~ expression_body)* ~ ")"}
xo_list = { "[" ~ (expression_body ~ ("," ~ expression_body)*)? ~ "]" }

xo_function_call = {identifier ~ (("::") ~ identifier)* ~ ("("~xo_function_args_list~")")}
xo_function_args_list = {(expression_body ~ ("," ~ expression_body)*)?}

////// ////// //////
/// BEGIN CONTROL FLOW
//////

//Control flow statements are NOT embeddable all places that expressions are.  That is, control-flow statements
//can only sit alongside elements in a template and cannot be bound to properties.  As a result,
//and to foster clarity of nomenclature, we call these `statements` rather than `expressions`.
//These statements work as syntactic sugar for built-in primitives: Conditional, Repeat, and Slot.
statement_control_flow = {(statement_if | statement_for | statement_slot)}

statement_if = {"if" ~ expression_body ~ "{" ~ inner_nodes ~ "}"} //FUTURE: support else, else if
statement_for = {"for" ~ statement_for_predicate_declaration ~ "in" ~ statement_for_source ~ "{" ~ inner_nodes ~ "}"}
statement_slot = {"slot" ~ expression_body}

//Examples:
//for i | for (elem, i)
statement_for_predicate_declaration = {
    identifier |
    ("(" ~ identifier ~ ","~ identifier ~")")
}

//Examples:
// in some_symbol
// in self.some_symbol
// in this.Pascal_snake-kebab
// in 0..5
// in this.some_symbol..25
// in 25..some_symbol
statement_for_source = { xo_range | xo_symbol }