/** * ZoKrates Grammar * Author: Jacob Eberhardt, Thibaut Schaeffer */ file = { SOI ~ NEWLINE* ~ import_directive* ~ NEWLINE* ~ ty_struct_definition* ~ NEWLINE* ~ function_definition* ~ EOI } import_directive = { main_import_directive | from_import_directive } from_import_directive = { "from" ~ "\"" ~ import_source ~ "\"" ~ "import" ~ identifier ~ ("as" ~ identifier)? ~ NEWLINE*} main_import_directive = {"import" ~ "\"" ~ import_source ~ "\"" ~ ("as" ~ identifier)? ~ NEWLINE+} import_source = @{(!"\"" ~ ANY)*} function_definition = {"def" ~ identifier ~ "(" ~ parameter_list ~ ")" ~ "->" ~ "(" ~ type_list ~ ")" ~ ":" ~ NEWLINE* ~ statement* } parameter_list = _{(parameter ~ ("," ~ parameter)*)?} parameter = {vis? ~ ty ~ identifier} // basic types ty_field = {"field"} ty_bool = {"bool"} ty_basic = { ty_field | ty_bool } ty_basic_or_struct = { ty_basic | ty_struct } ty_array = { ty_basic_or_struct ~ ("[" ~ expression ~ "]")+ } ty = { ty_array | ty_basic | ty_struct } type_list = _{(ty ~ ("," ~ ty)*)?} // structs ty_struct = { identifier } // type definitions ty_struct_definition = { "struct" ~ identifier ~ "{" ~ NEWLINE* ~ struct_field_list ~ NEWLINE* ~ "}" ~ NEWLINE* } struct_field_list = _{(struct_field ~ (NEWLINE+ ~ struct_field)*)? } struct_field = { ty ~ identifier } vis_private = {"private"} vis_public = {"public"} vis = { vis_private | vis_public } // Statements statement = { (return_statement // does not require subsequent newline | (iteration_statement | definition_statement | expression_statement ) ~ NEWLINE ) ~ NEWLINE* } iteration_statement = { "for" ~ ty ~ identifier ~ "in" ~ expression ~ ".." ~ expression ~ "do" ~ NEWLINE* ~ statement* ~ "endfor"} return_statement = { "return" ~ expression_list} definition_statement = { optionally_typed_assignee_list ~ "=" ~ expression } // declare and assign, so only identifiers are allowed, unlike `assignment_statement` expression_statement = {expression} optionally_typed_assignee_list = _{ optionally_typed_assignee ~ ("," ~ optionally_typed_assignee)* } optionally_typed_assignee = { (assignee) | (ty ~ assignee) } // we don't use { ty? ~ identifier } as with a single token, it gets parsed as `ty` but we want `identifier` // Expressions expression_list = _{(expression ~ ("," ~ expression)*)?} expression = { term ~ (op_binary ~ term)* } term = { ("(" ~ expression ~ ")") | inline_struct_expression | conditional_expression | postfix_expression | primary_expression | inline_array_expression | array_initializer_expression | unary_expression } spread = { "..." ~ expression } range = { from_expression? ~ ".." ~ to_expression? } from_expression = { expression } to_expression = { expression } conditional_expression = { "if" ~ expression ~ "then" ~ expression ~ "else" ~ expression ~ "fi"} postfix_expression = { identifier ~ access+ } // we force there to be at least one access, otherwise this matches single identifiers. Not sure that's what we want. access = { array_access | call_access | member_access } array_access = { "[" ~ range_or_expression ~ "]" } call_access = { "(" ~ expression_list ~ ")" } member_access = { "." ~ identifier } primary_expression = { identifier | constant } inline_struct_expression = { identifier ~ "{" ~ NEWLINE* ~ inline_struct_member_list ~ NEWLINE* ~ "}" } inline_struct_member_list = _{(inline_struct_member ~ ("," ~ NEWLINE* ~ inline_struct_member)*)? ~ ","? } inline_struct_member = { identifier ~ ":" ~ expression } inline_array_expression = { "[" ~ NEWLINE* ~ inline_array_inner ~ NEWLINE* ~ "]" } inline_array_inner = _{(spread_or_expression ~ ("," ~ NEWLINE* ~ spread_or_expression)*)?} spread_or_expression = { spread | expression } range_or_expression = { range | expression } array_initializer_expression = { "[" ~ expression ~ ";" ~ constant ~ "]" } unary_expression = { op_unary ~ term } // End Expressions assignee = { identifier ~ assignee_access* } assignee_access = { array_access | member_access } identifier = @{ ((!keyword ~ ASCII_ALPHA) | (keyword ~ (ASCII_ALPHANUMERIC | "_"))) ~ (ASCII_ALPHANUMERIC | "_")* } constant = { decimal_number | boolean_literal } decimal_number = @{ "0" | ASCII_NONZERO_DIGIT ~ ASCII_DIGIT* } boolean_literal = { "true" | "false" } op_inclusive_or = {"||"} op_exclusive_or = {"^"} op_and = {"&&"} op_equal = {"=="} op_not_equal = {"!="} op_lt = {"<"} op_lte = {"<="} op_gt = {">"} op_gte = {">="} op_add = {"+"} op_sub = {"-"} op_mul = {"*"} op_div = {"/"} op_pow = {"**"} op_not = {"!"} op_binary = _ { op_pow | op_inclusive_or | op_exclusive_or | op_and | op_equal | op_not_equal | op_lte | op_lt | op_gte | op_gt | op_add | op_sub | op_mul | op_div } op_unary = { op_not } WHITESPACE = _{ " " | "\t" | "\\" ~ NEWLINE} COMMENT = _{ ("/*" ~ (!"*/" ~ ANY)* ~ "*/") | ("//" ~ (!NEWLINE ~ ANY)*) } keyword = @{"as"|"bool"|"byte"|"def"|"do"|"else"|"endfor"|"export"|"false"|"field"|"for"|"if"|"then"|"fi"|"import"|"from"| "in"|"private"|"public"|"return"|"struct"|"true"|"uint" }