33. Pike BNF

Caveat lector: This is a simplified grammar for Pike 9.0 and is NOT the normative grammar. The compiler grammar is maintained in src/language.yacc; it takes precedence over this overview.

33.1. Notation

This chapter uses extended BNF: quoted strings are literal tokens, | separates alternatives, parentheses group expressions, and ?, * and + mean optional, zero or more, and one or more occurrences. These operators apply to the preceding item or group; quoted punctuation has its literal meaning. Whitespace between tokens is omitted.

This is a simplified description of valid syntax, not a grammar for building a parser. Name lookup, constant-expression requirements, valid assignment destinations, type constraints and context restrictions are checked separately. Preprocessor directives are handled before this grammar is applied.

33.2. Definitions

A program may be empty. Annotations, modifiers and declaration attributes are optional. Annotations precede modifiers; declaration attributes follow them. Class implicit create() arguments are also members, and a variadic (...) argument (if any) must be last and cannot have a default value.

program              ::= ( definition | ";" )*
definition           ::= import | inheritance | function | variables | constant
                       | class_definition | enum_definition | typedef
                       | generic_declaration
                       | annotation ";" | "@" "constant" ";"
                       | static_assertion ";"
                       | modifiers "{" program "}"
modifiers            ::= ( annotation ":" )* modifier*
modifier             ::= "extern" | "final" | "inline" | "local" | "optional"
                       | "private" | "protected" | "public" | "static"
                       | "variant"
                       | "__async__" | "__generator__" | "__weak__"
                       | "__unused__"
annotation           ::= "@" assignment_expression
attributes           ::= attribute*
attribute            ::= "__attribute__" "(" string ","? ")"
                       | ( "__deprecated__" | "__experimental__" | "__factory__"
                           ) ( "(" ")" )?
import               ::= "import" assignment_expression ";"
inheritance          ::= modifiers "inherit" assignment_expression
                           ( ":" ( identifier | string ) )? ";"
function             ::= modifiers attributes type "constant"? identifier
                           "(" arguments ")" ( block | ";" )
variables            ::= modifiers attributes type "constant"?
                           variable_names ";"
variable_names       ::= variable_name ( "," variable_name )*
variable_name        ::= identifier ( "=" assignment_expression )?
constant             ::= modifiers attributes "constant" cast_type?
                           constant_names ";"
constant_names       ::= constant_name ( "," constant_name )*
constant_name        ::= identifier "=" assignment_expression
class_definition     ::= modifiers attributes "class" identifier class_tail
anonymous_class      ::= "class" class_tail
class_tail           ::= generic_parameters? ( "(" create_arguments ")" )?
                           "{" program "}"
create_arguments     ::= ( create_argument ( "," create_argument )*
                           ( "," varargs_create_argument )?
                         | varargs_create_argument )? ","?
create_argument      ::= modifiers attributes type identifier
                           ( "=" assignment_expression )?
varargs_create_argument ::= modifiers attributes type "..." identifier
arguments            ::= ( argument ( "," argument )*
                           ( "," varargs_argument )?
                         | varargs_argument )? ","?
argument             ::= type identifier? ( "=" assignment_expression )?
varargs_argument     ::= type "..." identifier?
enum_definition      ::= modifiers attributes enum
enum                 ::= "enum" identifier?
                           "{" ( enum_member ( "," enum_member )* ","? )? "}"
enum_member          ::= attributes identifier ( "=" assignment_expression )?
typedef              ::= modifiers "typedef" type identifier ";"
generic_declaration  ::= "__generic__" generic_parameter
                           ( "," generic_parameter )* ";"
generic_parameters   ::= "(<" ( generic_parameter
                           ( "," generic_parameter )* ","? )? ">)"
generic_parameter    ::= type? identifier ( "=" type )?
static_assertion     ::= "_Static_assert" "(" assignment_expression ","
                           assignment_expression ")"

33.3. Statements

Conditions and loop clauses may declare local variables. The semicolon form of foreach can bind an index, a value, neither or both. The comma-form of foreach requires an assignable destination. Case labels are only valid in switch statements; break and continue targets must be valid enclosing statements.

Note that case ranges accept both .. and ...; the latter is new in Pike 9.0 and is accepted for C2Y syntax compatibility. Type ranges and slicing accept only ...

block                ::= "{" statement* "}"
statement            ::= init_expression ";" | import | block | ";"
                       | cond | while | do_while | for | foreach | switch
                       | case | "default" ":" | return | break | continue
                       | identifier ":" statement
                       | local_constant | local_function | local_class
init_expression      ::= expression | type variable_names
local_constant       ::= "constant" cast_type? constant_names ";"
local_function       ::= ( "__async__" | "__generator__" )?
                           type identifier "(" arguments ")" block
local_class          ::= "class" identifier class_tail
cond                 ::= "if" "(" init_expression ")" statement
                           ( "else" statement )?
while                ::= "while" "(" init_expression ")" statement
do_while             ::= "do" statement "while" "(" init_expression ")" ";"
for                  ::= "for" "(" init_expression? ";" init_expression? ";"
                           init_expression? ")" statement
foreach              ::= "foreach" "(" assignment_expression foreach_bindings
                           ")" statement
foreach_bindings     ::= "," lvalue | ";" lvalue? ";" lvalue?
switch               ::= "switch" "(" init_expression ")" statement
case                 ::= "case" init_expression ":"
                       | "case" init_expression case_range_operator
                           init_expression? ":"
                       | "case" case_range_operator init_expression ":"
case_range_operator  ::= ".." | "..."
return               ::= ( "continue" | "break" )? "return" init_expression? ";"
break                ::= "break" identifier? ";"
continue             ::= "continue" identifier? ";"
lvalue               ::= assignment_expression | type identifier ( "="
                           assignment_expression )?
                       | "[" lvalue ( "," lvalue )* "]"

33.4. Expressions and precedence

The productions below follow operator precedence from lowest to highest. Assignment, the conditional operator and exponentiation associate to the right. Other binary operators associate to the left. The left-hand side of an assignment and operands of increment and decrement must be assignable. Multi-assign, such as [a, b] = values, supports only the = operator.

expression           ::= assignment_expression ( "," assignment_expression )*
assignment_expression ::= conditional_expression
                        | logical_or_expression assignment_operator
                           assignment_expression
                        | "[" lvalue ( "," lvalue )* "]" "="
                           assignment_expression
assignment_operator  ::= "=" | "+=" | "-=" | "*=" | "/=" | "%=" | "**="
                       | "&=" | "|=" | "^="
                       | "<<=" | ">>=" | "?="
conditional_expression ::= logical_or_expression ( "?" expression ":"
                           assignment_expression )?
logical_or_expression ::= logical_and_expression
                           ( "||" logical_and_expression )*
logical_and_expression ::= bitwise_or_expression
                           ( "&&" bitwise_or_expression )*
bitwise_or_expression ::= bitwise_xor_expression ( "|" bitwise_xor_expression )*
bitwise_xor_expression ::= bitwise_and_expression
                           ( "^" bitwise_and_expression )*
bitwise_and_expression ::= equality_expression ( "&" equality_expression )*
equality_expression  ::= relational_expression
                           ( ( "==" | "!=" ) relational_expression )*
relational_expression ::= shift_expression ( ( "<" | ">" | "<="
                            | ">=" ) shift_expression )*
shift_expression     ::= additive_expression ( ( "<<" | ">>" )
                           additive_expression )*
additive_expression  ::= multiplicative_expression
                           ( ( "+" | "-" ) multiplicative_expression )*
multiplicative_expression ::= cast_expression
                           ( ( "*" | "/" | "%" ) cast_expression )*
cast_expression      ::= unary_expression
                       | "(" cast_type ")" cast_expression
                       | "[" cast_type "]" cast_expression
cast_type            ::= builtin_type ( "|" type_atom )*
unary_expression     ::= power_expression
                       | ( "!" | "~" | "+" | "-" ) cast_expression
power_expression     ::= prefix_expression ( "**" power_expression )?
prefix_expression    ::= ( "++" | "--" )? chain_expression
chain_expression     ::= postfix_expression ( safe_suffix safe_follow_suffix* )*
postfix_expression   ::= name | primary_expression generic_bindings?
                       | postfix_expression ordinary_suffix generic_bindings?
                       | postfix_expression ( "++" | "--" )
ordinary_suffix      ::= safe_follow_suffix | "." member_name | "[" "*" "]"
safe_follow_suffix   ::= "(" expression_list ")" block?
                       | "[" assignment_expression "]"
                       | "[" range_bound ".." range_bound "]"
                       | "->" arrow_member
safe_suffix          ::= "(?" expression_list ")" block?
                       | "[?" assignment_expression "]"
                       | "[?" range_bound ".." range_bound "]"
                       | "->?" arrow_member
range_bound          ::= ( "<"? init_expression )?
member_name          ::= identifier | integer_literal | string_literal
arrow_member         ::= identifier | reserved_word
primary_expression   ::= integer_literal | float_literal | string
                       | array | mapping | multiset
                       | "(" expression ")" | catch | gauge | typeof | sscanf
                       | lambda
                       | anonymous_class | enum | generic_selection
                       | static_assertion
expression_list      ::= ( splice_expression ( "," splice_expression )* ","? )?
splice_expression    ::= "@"? assignment_expression
array                ::= "({" expression_list "})"
multiset             ::= "(<" expression_list ">)"
mapping              ::= "([" ( association ( "," association )* ","? )? "])"
association          ::= assignment_expression ":" assignment_expression
catch                ::= "catch" ( "(" init_expression ")" | block )
gauge                ::= "gauge" ( "(" init_expression ")" | block )
typeof               ::= "typeof" "(" assignment_expression ")"
sscanf               ::= "sscanf" "(" assignment_expression ","
                           assignment_expression ( "," lvalue )* ")"
lambda               ::= ( "__async__" | "__generator__" )?
                           "lambda" "(" arguments ")" block
generic_selection    ::= "_Generic" "(" assignment_expression ","
                           generic_association ( "," generic_association )* ")"
generic_association  ::= ( type | "default" ) ":" assignment_expression

33.5. Names and types

A leading dot looks up a module relative to the source file. Scope prefixes select inherited, enclosing, predefined or versioned names. Generic bindings use (<...>). Not every syntactically valid name is meaningful in every scope.

Identifier types include class names and typedefs. Explicit casts must start with a built-in type; a soft cast uses square brackets. Type ranges may be unions of individual integers and open or closed ranges. A bit or bits suffix, as in int(8bit), denotes an unsigned bit width. Array and string types can constrain length as well as element or character type.

name                 ::= name_start ( "." member_name )* generic_bindings?
name_start           ::= identifier | "." ( identifier | string_literal )
                       | "global" "." identifier
                       | "predef" "::" identifier | version "::" identifier
                       | "continue" "::" "this_function" | "::" identifier
                       | scope_prefix identifier
scope_prefix         ::= ( identifier | string | "local" | "global" ) "::"
                           ( ( identifier | "local" ) "::" )*
version              ::= digit+ "." digit+
generic_bindings     ::= "(<" ( type ( "," type )* ","? )? ">)"
type                 ::= type_atom ( "|" type_atom )*
type_atom            ::= builtin_type | name | typeof
builtin_type         ::= "mixed" | "void" | "auto" | "__unknown__" | "float"
                       | int_type | string_type | object_type | program_type
                       | mapping_type | array_type | multiset_type
                       | function_type
                       | "__attribute__" "(" string "," type ")"
                       | ( "__deprecated__" | "__experimental__" | "__factory__"
                           ) "(" type ")"
int_type             ::= "int" ( "(" integer_ranges ")" )?
integer_ranges       ::= integer_range ( "|" integer_range )*
integer_range        ::= signed_integer | signed_integer? ".." signed_integer?
                       | bit_width
signed_integer       ::= "-"? integer_literal
bit_width            ::= integer_literal ( "bit" | "bits" )
string_type          ::= "string" ( "(" ( integer_ranges | integer_ranges ":"
                           integer_ranges? | ":" integer_ranges ) ")" )?
object_type          ::= "object" ( "(" ( type | string ) ")" )?
program_type         ::= "program" ( "(" ( type | string ) ")" )?
array_type           ::= "array" ( "(" ( type | integer_ranges ":" type?
                           | ":" type ) ")" )?
mapping_type         ::= "mapping" ( "(" type ":" type ")" )?
multiset_type        ::= "multiset" ( "(" type ")" )?
function_type        ::= "function" ( "(" function_arguments ":" type ")" )?
function_arguments   ::= ( type ( "," type )* ( "..." | "," )? )?

33.6. Lexical elements

integer_literal, float_literal and string_literal stand for tokens recognized by the lexer. Integers include zero, decimal, hexadecimal, binary and octal literals, and single-quoted character constants. A leading sign is a unary operator, not part of a numeric token. Floating-point literals include exponent notation. Strings support escapes and adjacent string literals concatenate; __func__ also contributes a string segment.

word denotes an ordinary identifier accepted by the lexer, excluding reserved keywords. This includes names containing non-ASCII characters, subject to the lexer's character rules. Operator identifiers and getter/setter names are also identifier tokens. reserved_word stands for a keyword or reserved identifier accepted after an arrow. The abbreviated productions below show their spelling; no whitespace is allowed inside an identifier, a version prefix or a bit-width token. See the Lexical Grammar and Preprocessor chapters for literal and preprocessing details. The current lexical implementation is in src/lexer.h and src/cpp.cmod.

string               ::= ( string_literal | "__func__" )+
identifier           ::= word | operator_identifier | accessor_identifier
operator_identifier  ::= ( "`" | "``" | "```" ) operator_name
operator_name        ::= "+" | "+=" | "-" | "*" | "**" | "/" | "%" | "&"
                       | "|" | "^" | "~"
                       | "<" | "<<" | "<=" | ">" | ">>"
                       | ">=" | "==" | "!=" | "!" | "="
                       | "()" | "->" | "->=" | "[]" | "[]=" | "[..]"
accessor_identifier  ::= "`" "->"? word "="?
digit                ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8"
                       | "9"