// JavaScript PEG/Pest grammar
WHITESPACE = _{ " " | NEWLINE }
program = { SOI ~ (statement ~ ";"?)* ~ EOI } // FIXME: Everything regarding semicolons is buggy
statement = _{ if_statement | declaration | return_statement | while_statement | expression }
return_statement = { "return" ~ expression }
declaration = { ("const" | "let" | "var")? ~ value ~ assignment_operator ~ expression }
if_statement = {
"if" ~ "(" ~ expression ~ ")" ~ (("{" ~ statement* ~ "}") | statement)
~ (("else" ~ (("{" ~ statement* ~ "}") | statement))
| ("else" ~ "if" ~ if_statement))?
}
while_statement = { "while" ~ "(" ~ expression ~ ")" ~ (("{" ~ statement* ~ "}") | statement) }
expression = _{ ((value ~ operator ~ value) | value) ~ ";"? } // FIXME: semicolon
arguments = { expression ~ ("," ~ expression)* }
// identifiers
identifier = ${ (ASCII_ALPHA | "_" | "$") ~ (ASCII_ALPHANUMERIC | "_" | "$")* }
// ignores keywords
keyword = _{ "return" | "const" | "let" | "var" | "function" }
// punctuators
operator = { binary_operator | assignment_operator }
binary_operator = _{ bitwise_operator | "+" | "-" | "*" | "/" | "%" | "**" | "==" | "!=" | "===" | "!==" | "<" | "<=" | ">" | ">=" | "&&" | "||" }
bitwise_operator = _{ "&" | "|" | "^" | "<<" | ">>" | ">>>" }
assignment_operator = _{ "=" | "+=" | "-=" | "*=" | "/=" | "%=" | "<<=" | ">>=" | ">>>=" | "&=" | "^=" | "|=" | "**=" }
// literals
literal = { number | string | boolean | nullish }
value = _{ literal | object | function | arrow_function | regex | keyword | identifier | array }
boolean = { "true" | "false" }
nullish = { null | undefined }
null = { "null" }
undefined = { "undefined" }
string = { "\"" ~ inner ~ "\"" | "\'" ~ inner ~ "\'" }
inner = ${ char* }
char = { !("\"" | "\\") ~ ANY }
number = @{
"-"?
~ ("0" | ASCII_NONZERO_DIGIT ~ ASCII_DIGIT*)
~ ("." ~ ASCII_DIGIT+)?
~ (^"e" ~ ("+" | "-")? ~ ASCII_DIGIT+)?
}
array = { "[" ~ (value ~ ",")*? ~ value? ~ "]" }
object = { "("? ~ "{" ~ pair* ~ "}" ~ ")"? }
pair = { pair_key ~ ":" ~ value ~ ","? }
pair_key = _{ string | number | identifier }
regex = { "/" ~ inner ~ "/" }
function = { "function" ~ identifier? ~ "(" ~ arguments? ~ ")" ~ ("{" ~ function_body? ~ "}" | function_body?) }
function_body = { statement* }
arrow_function = { "(" ~ arguments? ~ ")" ~ "=>" ~ ("{" ~ function_body? ~ "}" | arrow_function_body?) }
arrow_function_body = { implicit_return | statement* }
implicit_return = { expression }