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.
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.
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 ")"
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 )* "]"
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
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 )* ( "..." | "," )? )?
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"