zig-spec icon indicating copy to clipboard operation
zig-spec copied to clipboard

Trying to validate grammar

Open markfirmware opened this issue 3 years ago • 16 comments

I cannot get validation on a 20.04 cloud host, a gitpod cloud host nor in gh actions. All of the tests have 127 vs 0

cd grammar
./validate.sh

markfirmware avatar Mar 09 '21 17:03 markfirmware

FYI https://discord.com/channels/223421264751099906/302022825752002561/818905779733266453

markfirmware avatar Mar 09 '21 17:03 markfirmware

Is the grammar in working order? Do you typically use some other than linux to run check_parser.sh?

I noticed a different version of the grammar at the web page https://github.com/ziglang/www.ziglang.org/blob/0888fffe9589b72245f62766e94581adf4784a12/content/documentation/master/index.html#L13092-L13593

markfirmware avatar Mar 10 '21 19:03 markfirmware

Did not intend to close.

markfirmware avatar Mar 10 '21 19:03 markfirmware

Here is a naive conversion (basically replace <- by ::= and / by | and cut several token declarations) of the grammar at https://github.com/ziglang/zig-spec/blob/master/grammar/grammar.y to EBNF accepted by https://www.bottlecaps.de/rr/ui to generate a railroad diagram (copy the grammar bellow to Edit Grammar tab and then click on View Diagram tab):

Root ::= skip container_doc_comment? ContainerMembers eof

//# *** Top level ***
ContainerMembers
    ::= (
         TestDecl ContainerMembers
         | TopLevelComptime ContainerMembers
         | doc_comment? KEYWORD_pub? TopLevelDecl ContainerMembers
         | ContainerField COMMA ContainerMembers
         | ContainerField
     )

TestDecl ::= doc_comment? KEYWORD_test STRINGLITERALSINGLE? Block

TopLevelComptime ::= doc_comment? KEYWORD_comptime BlockExpr

TopLevelDecl
    ::= (KEYWORD_export | KEYWORD_extern STRINGLITERALSINGLE? | (KEYWORD_inline | KEYWORD_noinline))? FnProto (SEMICOLON | Block)
     | (KEYWORD_export | KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl
     | KEYWORD_usingnamespace Expr SEMICOLON

FnProto ::= KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? CallConv? EXCLAMATIONMARK? (KEYWORD_anytype | TypeExpr)

VarDecl ::= (KEYWORD_const | KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON

ContainerField ::= doc_comment? KEYWORD_comptime? IDENTIFIER (COLON (KEYWORD_anytype | TypeExpr) ByteAlign?)? (EQUAL Expr)?

//# *** Block Level ***
Statement
    ::= KEYWORD_comptime? VarDecl
     | KEYWORD_comptime BlockExprStatement
     | KEYWORD_nosuspend BlockExprStatement
     | KEYWORD_suspend (SEMICOLON | BlockExprStatement)
     | KEYWORD_defer BlockExprStatement
     | KEYWORD_errdefer Payload? BlockExprStatement
     | IfStatement
     | LabeledStatement
     | SwitchExpr
     | AssignExpr SEMICOLON

IfStatement
    ::= IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )?
     | IfPrefix AssignExpr ( SEMICOLON | KEYWORD_else Payload? Statement )

LabeledStatement ::= BlockLabel? (Block | LoopStatement)

LoopStatement ::= KEYWORD_inline? (ForStatement | WhileStatement)

ForStatement
    ::= ForPrefix BlockExpr ( KEYWORD_else Statement )?
     | ForPrefix AssignExpr ( SEMICOLON | KEYWORD_else Statement )

WhileStatement
    ::= WhilePrefix BlockExpr ( KEYWORD_else Payload? Statement )?
     | WhilePrefix AssignExpr ( SEMICOLON | KEYWORD_else Payload? Statement )

BlockExprStatement
    ::= BlockExpr
     | AssignExpr SEMICOLON

BlockExpr ::= BlockLabel? Block

//# *** Expression Level ***
AssignExpr ::= Expr (AssignOp Expr)?

Expr ::= BoolOrExpr

BoolOrExpr ::= BoolAndExpr (KEYWORD_or BoolAndExpr)*

BoolAndExpr ::= CompareExpr (KEYWORD_and CompareExpr)*

CompareExpr ::= BitwiseExpr (CompareOp BitwiseExpr)?

BitwiseExpr ::= BitShiftExpr (BitwiseOp BitShiftExpr)*

BitShiftExpr ::= AdditionExpr (BitShiftOp AdditionExpr)*

AdditionExpr ::= MultiplyExpr (AdditionOp MultiplyExpr)*

MultiplyExpr ::= PrefixExpr (MultiplyOp PrefixExpr)*

PrefixExpr ::= PrefixOp* PrimaryExpr

PrimaryExpr
    ::= AsmExpr
     | IfExpr
     | KEYWORD_break BreakLabel? Expr?
     | KEYWORD_comptime Expr
     | KEYWORD_nosuspend Expr
     | KEYWORD_continue BreakLabel?
     | KEYWORD_resume Expr
     | KEYWORD_return Expr?
     | BlockLabel? LoopExpr
     | Block
     | CurlySuffixExpr

IfExpr ::= IfPrefix Expr (KEYWORD_else Payload? Expr)?

Block ::= LBRACE Statement* RBRACE

LoopExpr ::= KEYWORD_inline? (ForExpr | WhileExpr)

ForExpr ::= ForPrefix Expr (KEYWORD_else Expr)?

WhileExpr ::= WhilePrefix Expr (KEYWORD_else Payload? Expr)?

CurlySuffixExpr ::= TypeExpr InitList?

InitList
    ::= LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE
     | LBRACE Expr (COMMA Expr)* COMMA? RBRACE
     | LBRACE RBRACE

TypeExpr ::= PrefixTypeOp* ErrorUnionExpr

ErrorUnionExpr ::= SuffixExpr (EXCLAMATIONMARK TypeExpr)?

SuffixExpr
    ::= KEYWORD_async PrimaryTypeExpr SuffixOp* FnCallArguments
     | PrimaryTypeExpr (SuffixOp | FnCallArguments)*

PrimaryTypeExpr
    ::= BUILTINIDENTIFIER FnCallArguments
     | CHAR_LITERAL
     | ContainerDecl
     | DOT IDENTIFIER
     | DOT InitList
     | ErrorSetDecl
     | FLOAT
     | FnProto
     | GroupedExpr
     | LabeledTypeExpr
     | IDENTIFIER
     | IfTypeExpr
     | INTEGER
     | KEYWORD_comptime TypeExpr
     | KEYWORD_error DOT IDENTIFIER
     | KEYWORD_false
     | KEYWORD_null
     | KEYWORD_anyframe
     | KEYWORD_true
     | KEYWORD_undefined
     | KEYWORD_unreachable
     | STRINGLITERAL
     | SwitchExpr

ContainerDecl ::= (KEYWORD_extern | KEYWORD_packed)? ContainerDeclAuto

ErrorSetDecl ::= KEYWORD_error LBRACE IdentifierList RBRACE

GroupedExpr ::= LPAREN Expr RPAREN

IfTypeExpr ::= IfPrefix TypeExpr (KEYWORD_else Payload? TypeExpr)?

LabeledTypeExpr
    ::= BlockLabel Block
     | BlockLabel? LoopTypeExpr

LoopTypeExpr ::= KEYWORD_inline? (ForTypeExpr | WhileTypeExpr)

ForTypeExpr ::= ForPrefix TypeExpr (KEYWORD_else TypeExpr)?

WhileTypeExpr ::= WhilePrefix TypeExpr (KEYWORD_else Payload? TypeExpr)?

SwitchExpr ::= KEYWORD_switch LPAREN Expr RPAREN LBRACE SwitchProngList RBRACE

//# *** Assembly ***
AsmExpr ::= KEYWORD_asm KEYWORD_volatile? LPAREN STRINGLITERAL AsmOutput? RPAREN

AsmOutput ::= COLON AsmOutputList AsmInput?

AsmOutputItem ::= LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN (MINUSRARROW TypeExpr | IDENTIFIER) RPAREN

AsmInput ::= COLON AsmInputList AsmClobbers?

AsmInputItem ::= LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN Expr RPAREN

AsmClobbers ::= COLON StringList

//# *** Helper grammar ***
BreakLabel ::= COLON IDENTIFIER

BlockLabel ::= IDENTIFIER COLON

FieldInit ::= DOT IDENTIFIER EQUAL Expr

WhileContinueExpr ::= COLON LPAREN AssignExpr RPAREN

LinkSection ::= KEYWORD_linksection LPAREN Expr RPAREN

//# Fn specific
CallConv ::= KEYWORD_callconv LPAREN Expr RPAREN

ParamDecl ::= doc_comment? (KEYWORD_noalias | KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType

ParamType
    ::= KEYWORD_anytype
     | DOT3
     | TypeExpr

//# Control flow prefixes
IfPrefix ::= KEYWORD_if LPAREN Expr RPAREN PtrPayload?

WhilePrefix ::= KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr?

ForPrefix ::= KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload

//# Payloads
Payload ::= PIPE IDENTIFIER PIPE

PtrPayload ::= PIPE ASTERISK? IDENTIFIER PIPE

PtrIndexPayload ::= PIPE ASTERISK? IDENTIFIER (COMMA IDENTIFIER)? PIPE


//# Switch specific
SwitchProng ::= SwitchCase EQUALRARROW PtrPayload? AssignExpr

SwitchCase
    ::= SwitchItem (COMMA SwitchItem)* COMMA?
     | KEYWORD_else

SwitchItem ::= Expr (DOT3 Expr)?

//# Operators
AssignOp
    ::= ASTERISKEQUAL
     | SLASHEQUAL
     | PERCENTEQUAL
     | PLUSEQUAL
     | MINUSEQUAL
     | LARROW2EQUAL
     | RARROW2EQUAL
     | AMPERSANDEQUAL
     | CARETEQUAL
     | PIPEEQUAL
     | ASTERISKPERCENTEQUAL
     | PLUSPERCENTEQUAL
     | MINUSPERCENTEQUAL
     | EQUAL

CompareOp
    ::= EQUALEQUAL
     | EXCLAMATIONMARKEQUAL
     | LARROW
     | RARROW
     | LARROWEQUAL
     | RARROWEQUAL

BitwiseOp
    ::= AMPERSAND
     | CARET
     | PIPE
     | KEYWORD_orelse
     | KEYWORD_catch Payload?

BitShiftOp
    ::= LARROW2
     | RARROW2

AdditionOp
    ::= PLUS
     | MINUS
     | PLUS2
     | PLUSPERCENT
     | MINUSPERCENT

MultiplyOp
    ::= PIPE2
     | ASTERISK
     | SLASH
     | PERCENT
     | ASTERISK2
     | ASTERISKPERCENT

PrefixOp
    ::= EXCLAMATIONMARK
     | MINUS
     | TILDE
     | MINUSPERCENT
     | AMPERSAND
     | KEYWORD_try
     | KEYWORD_await

PrefixTypeOp
    ::= QUESTIONMARK
     | KEYWORD_anyframe MINUSRARROW
     | SliceTypeStart (ByteAlign | KEYWORD_const | KEYWORD_volatile | KEYWORD_allowzero)*
     | PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN | KEYWORD_const | KEYWORD_volatile | KEYWORD_allowzero)*
     | ArrayTypeStart

SuffixOp
    ::= LBRACKET Expr (DOT2 (Expr? (COLON Expr)?)?)? RBRACKET
     | DOT IDENTIFIER
     | DOTASTERISK
     | DOTQUESTIONMARK

FnCallArguments ::= LPAREN ExprList RPAREN

//# Ptr specific
SliceTypeStart ::= LBRACKET (COLON Expr)? RBRACKET

PtrTypeStart
    ::= ASTERISK
     | ASTERISK2
     | LBRACKET ASTERISK (LETTERC | COLON Expr)? RBRACKET

ArrayTypeStart ::= LBRACKET Expr (COLON Expr)? RBRACKET

//# ContainerDecl specific
ContainerDeclAuto ::= ContainerDeclType LBRACE container_doc_comment? ContainerMembers RBRACE

ContainerDeclType
    ::= KEYWORD_struct
     | KEYWORD_opaque
     | KEYWORD_enum (LPAREN Expr RPAREN)?
     | KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? | Expr) RPAREN)?

//# Alignment
ByteAlign ::= KEYWORD_align LPAREN Expr RPAREN

//# Lists
IdentifierList ::= (doc_comment? IDENTIFIER COMMA)* (doc_comment? IDENTIFIER)?

SwitchProngList ::= (SwitchProng COMMA)* SwitchProng?

AsmOutputList ::= (AsmOutputItem COMMA)* AsmOutputItem?

AsmInputList ::= (AsmInputItem COMMA)* AsmInputItem?

StringList ::= (STRINGLITERAL COMMA)* STRINGLITERAL?

ParamDeclList ::= (ParamDecl COMMA)* ParamDecl?

ExprList ::= (Expr COMMA)* Expr?

//# *** Tokens ***

AMPERSAND            ::= '&'
AMPERSANDEQUAL       ::= '&='
ASTERISK             ::= '*'
ASTERISK2            ::= '**'
ASTERISKEQUAL        ::= '*='
ASTERISKPERCENT      ::= '*%'
ASTERISKPERCENTEQUAL ::= '*%='
CARET                ::= '^'
CARETEQUAL           ::= '^='
COLON                ::= ':'
COMMA                ::= ','
DOT                  ::= '.'
DOT2                 ::= '..'
DOT3                 ::= '...'
DOTASTERISK          ::= '.*'
DOTQUESTIONMARK      ::= '.?'
EQUAL                ::= '='
EQUALEQUAL           ::= '=='
EQUALRARROW          ::= '=>'
EXCLAMATIONMARK      ::= '!'
EXCLAMATIONMARKEQUAL ::= '!='
LARROW               ::= '<'
LARROW2              ::= '<<'
LARROW2EQUAL         ::= '<<='
LARROWEQUAL          ::= '<='
LBRACE               ::= '{'
LBRACKET             ::= '['
LPAREN               ::= '('
MINUS                ::= '-'
MINUSEQUAL           ::= '-='
MINUSPERCENT         ::= '-%'
MINUSPERCENTEQUAL    ::= '-%='
MINUSRARROW          ::= '->'
PERCENT              ::= '%'
PERCENTEQUAL         ::= '%='
PIPE                 ::= '|'
PIPE2                ::= '||'
PIPEEQUAL            ::= '|='
PLUS                 ::= '+'
PLUS2                ::= '++'
PLUSEQUAL            ::= '+='
PLUSPERCENT          ::= '+%'
PLUSPERCENTEQUAL     ::= '+%='
LETTERC              ::= 'c'
QUESTIONMARK         ::= '?'
RARROW               ::= '>'
RARROW2              ::= '>>'
RARROW2EQUAL         ::= '>>='
RARROWEQUAL          ::= '>='
RBRACE               ::= '}'
RBRACKET             ::= ']'
RPAREN               ::= ')'
SEMICOLON            ::= ';'
SLASH                ::= '/'
SLASHEQUAL           ::= '/='
TILDE                ::= '~'

KEYWORD_align       ::= 'align'
KEYWORD_allowzero   ::= 'allowzero'
KEYWORD_and         ::= 'and'
KEYWORD_anyframe    ::= 'anyframe'
KEYWORD_anytype     ::= 'anytype'
KEYWORD_asm         ::= 'asm'
KEYWORD_async       ::= 'async'
KEYWORD_await       ::= 'await'
KEYWORD_break       ::= 'break'
KEYWORD_callconv    ::= 'callconv'
KEYWORD_catch       ::= 'catch'
KEYWORD_comptime    ::= 'comptime'
KEYWORD_const       ::= 'const'
KEYWORD_continue    ::= 'continue'
KEYWORD_defer       ::= 'defer'
KEYWORD_else        ::= 'else'
KEYWORD_enum        ::= 'enum'
KEYWORD_errdefer    ::= 'errdefer'
KEYWORD_error       ::= 'error'
KEYWORD_export      ::= 'export'
KEYWORD_extern      ::= 'extern'
KEYWORD_false       ::= 'false'
KEYWORD_fn          ::= 'fn'
KEYWORD_for         ::= 'for'
KEYWORD_if          ::= 'if'
KEYWORD_inline      ::= 'inline'
KEYWORD_noalias     ::= 'noalias'
KEYWORD_nosuspend   ::= 'nosuspend'
KEYWORD_noinline    ::= 'noinline'
KEYWORD_null        ::= 'null'
KEYWORD_opaque      ::= 'opaque'
KEYWORD_or          ::= 'or'
KEYWORD_orelse      ::= 'orelse'
KEYWORD_packed      ::= 'packed'
KEYWORD_pub         ::= 'pub'
KEYWORD_resume      ::= 'resume'
KEYWORD_return      ::= 'return'
KEYWORD_linksection ::= 'linksection'
KEYWORD_struct      ::= 'struct'
KEYWORD_suspend     ::= 'suspend'
KEYWORD_switch      ::= 'switch'
KEYWORD_test        ::= 'test'
KEYWORD_threadlocal ::= 'threadlocal'
KEYWORD_true        ::= 'true'
KEYWORD_try         ::= 'try'
KEYWORD_undefined   ::= 'undefined'
KEYWORD_union       ::= 'union'
KEYWORD_unreachable ::= 'unreachable'
KEYWORD_usingnamespace ::= 'usingnamespace'
KEYWORD_var         ::= 'var'
KEYWORD_volatile    ::= 'volatile'
KEYWORD_while       ::= 'while'

mingodad avatar May 10 '21 18:05 mingodad

Effectively the grammar on ziglang.org documentation do not match the one here, here is the EBNF for the 0.7.1 and the dev versions to be viewd at https://www.bottlecaps.de/rr/ui:

Zig-0.7.1.ebnf:

Root ::= skip ContainerMembers eof

//# *** Top level ***
ContainerMembers
    ::= TestDecl ContainerMembers
     | TopLevelComptime ContainerMembers
     | KEYWORD_pub? TopLevelDecl ContainerMembers
     | ContainerField COMMA ContainerMembers
     | ContainerField


TestDecl ::= KEYWORD_test STRINGLITERALSINGLE Block

TopLevelComptime ::= KEYWORD_comptime BlockExpr

TopLevelDecl
    ::= (KEYWORD_export | KEYWORD_extern STRINGLITERALSINGLE? | KEYWORD_inline)? FnProto (SEMICOLON | Block)
     | (KEYWORD_export | KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl
     | KEYWORD_usingnamespace Expr SEMICOLON

FnProto ::= KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? EXCLAMATIONMARK? (KEYWORD_anytype | TypeExpr)

VarDecl ::= (KEYWORD_const | KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON

ContainerField ::= KEYWORD_comptime? IDENTIFIER (COLON TypeExpr)? (EQUAL Expr)?

//# *** Block Level ***
Statement
    ::= KEYWORD_comptime? VarDecl
     | KEYWORD_comptime BlockExprStatement
     | KEYWORD_nosuspend BlockExprStatement
     | KEYWORD_suspend (SEMICOLON | BlockExprStatement)
     | KEYWORD_defer BlockExprStatement
     | KEYWORD_errdefer BlockExprStatement
     | IfStatement
     | LabeledStatement
     | SwitchExpr
     | AssignExpr SEMICOLON

IfStatement
    ::= IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )?
     | IfPrefix AssignExpr ( SEMICOLON | KEYWORD_else Payload? Statement )

LabeledStatement ::= BlockLabel? (Block | LoopStatement)

LoopStatement ::= KEYWORD_inline? (ForStatement | WhileStatement)

ForStatement
    ::= ForPrefix BlockExpr ( KEYWORD_else Statement )?
     | ForPrefix AssignExpr ( SEMICOLON | KEYWORD_else Statement )

WhileStatement
    ::= WhilePrefix BlockExpr ( KEYWORD_else Payload? Statement )?
     | WhilePrefix AssignExpr ( SEMICOLON | KEYWORD_else Payload? Statement )

BlockExprStatement
    ::= BlockExpr
     | AssignExpr SEMICOLON

BlockExpr ::= BlockLabel? Block

//# *** Expression Level ***
AssignExpr ::= Expr (AssignOp Expr)?

Expr ::= KEYWORD_try* BoolOrExpr

BoolOrExpr ::= BoolAndExpr (KEYWORD_or BoolAndExpr)*

BoolAndExpr ::= CompareExpr (KEYWORD_and CompareExpr)*

CompareExpr ::= BitwiseExpr (CompareOp BitwiseExpr)?

BitwiseExpr ::= BitShiftExpr (BitwiseOp BitShiftExpr)*

BitShiftExpr ::= AdditionExpr (BitShiftOp AdditionExpr)*

AdditionExpr ::= MultiplyExpr (AdditionOp MultiplyExpr)*

MultiplyExpr ::= PrefixExpr (MultiplyOp PrefixExpr)*

PrefixExpr ::= PrefixOp* PrimaryExpr

PrimaryExpr
    ::= AsmExpr
     | IfExpr
     | KEYWORD_break BreakLabel? Expr?
     | KEYWORD_comptime Expr
     | KEYWORD_nosuspend Expr
     | KEYWORD_continue BreakLabel?
     | KEYWORD_resume Expr
     | KEYWORD_return Expr?
     | BlockLabel? LoopExpr
     | Block
     | CurlySuffixExpr

IfExpr ::= IfPrefix Expr (KEYWORD_else Payload? Expr)?

Block ::= LBRACE Statement* RBRACE

LoopExpr ::= KEYWORD_inline? (ForExpr | WhileExpr)

ForExpr ::= ForPrefix Expr (KEYWORD_else Expr)?

WhileExpr ::= WhilePrefix Expr (KEYWORD_else Payload? Expr)?

CurlySuffixExpr ::= TypeExpr InitList?

InitList
    ::= LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE
     | LBRACE Expr (COMMA Expr)* COMMA? RBRACE
     | LBRACE RBRACE

TypeExpr ::= PrefixTypeOp* ErrorUnionExpr

ErrorUnionExpr ::= SuffixExpr (EXCLAMATIONMARK TypeExpr)?

SuffixExpr
    ::= KEYWORD_async PrimaryTypeExpr SuffixOp* FnCallArguments
     | PrimaryTypeExpr (SuffixOp | FnCallArguments)*

PrimaryTypeExpr
    ::= BUILTINIDENTIFIER FnCallArguments
     | CHAR_LITERAL
     | ContainerDecl
     | DOT IDENTIFIER
     | DOT InitList
     | ErrorSetDecl
     | FLOAT
     | FnProto
     | GroupedExpr
     | LabeledTypeExpr
     | IDENTIFIER
     | IfTypeExpr
     | INTEGER
     | KEYWORD_comptime TypeExpr
     | KEYWORD_error DOT IDENTIFIER
     | KEYWORD_false
     | KEYWORD_null
     | KEYWORD_anyframe
     | KEYWORD_true
     | KEYWORD_undefined
     | KEYWORD_unreachable
     | STRINGLITERAL
     | SwitchExpr

ContainerDecl ::= (KEYWORD_extern | KEYWORD_packed)? ContainerDeclAuto

ErrorSetDecl ::= KEYWORD_error LBRACE IdentifierList RBRACE

GroupedExpr ::= LPAREN Expr RPAREN

IfTypeExpr ::= IfPrefix TypeExpr (KEYWORD_else Payload? TypeExpr)?

LabeledTypeExpr
    ::= BlockLabel Block
     | BlockLabel? LoopTypeExpr

LoopTypeExpr ::= KEYWORD_inline? (ForTypeExpr | WhileTypeExpr)

ForTypeExpr ::= ForPrefix TypeExpr (KEYWORD_else TypeExpr)?

WhileTypeExpr ::= WhilePrefix TypeExpr (KEYWORD_else Payload? TypeExpr)?

SwitchExpr ::= KEYWORD_switch LPAREN Expr RPAREN LBRACE SwitchProngList RBRACE

//# *** Assembly ***
AsmExpr ::= KEYWORD_asm KEYWORD_volatile? LPAREN STRINGLITERAL AsmOutput? RPAREN

AsmOutput ::= COLON AsmOutputList AsmInput?

AsmOutputItem ::= LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN (MINUSRARROW TypeExpr | IDENTIFIER) RPAREN

AsmInput ::= COLON AsmInputList AsmClobbers?

AsmInputItem ::= LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN Expr RPAREN

AsmClobbers ::= COLON StringList

//# *** Helper grammar ***
BreakLabel ::= COLON IDENTIFIER

BlockLabel ::= IDENTIFIER COLON

FieldInit ::= DOT IDENTIFIER EQUAL Expr

WhileContinueExpr ::= COLON LPAREN AssignExpr RPAREN

LinkSection ::= KEYWORD_linksection LPAREN Expr RPAREN

ParamDecl ::= (KEYWORD_noalias | KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType

ParamType
    ::= KEYWORD_anytype
     | DOT3
     | TypeExpr

//# Control flow prefixes
IfPrefix ::= KEYWORD_if LPAREN Expr RPAREN PtrPayload?

WhilePrefix ::= KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr?

ForPrefix ::= KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload

//# Payloads
Payload ::= PIPE IDENTIFIER PIPE

PtrPayload ::= PIPE ASTERISK? IDENTIFIER PIPE

PtrIndexPayload ::= PIPE ASTERISK? IDENTIFIER (COMMA IDENTIFIER)? PIPE


//# Switch specific
SwitchProng ::= SwitchCase EQUALRARROW PtrPayload? AssignExpr

SwitchCase
    ::= SwitchItem (COMMA SwitchItem)* COMMA?
     | KEYWORD_else

SwitchItem ::= Expr (DOT3 Expr)?

//# Operators
AssignOp
    ::= ASTERISKEQUAL
     | SLASHEQUAL
     | PERCENTEQUAL
     | PLUSEQUAL
     | MINUSEQUAL
     | LARROW2EQUAL
     | RARROW2EQUAL
     | AMPERSANDEQUAL
     | CARETEQUAL
     | PIPEEQUAL
     | ASTERISKPERCENTEQUAL
     | PLUSPERCENTEQUAL
     | MINUSPERCENTEQUAL
     | EQUAL

CompareOp
    ::= EQUALEQUAL
     | EXCLAMATIONMARKEQUAL
     | LARROW
     | RARROW
     | LARROWEQUAL
     | RARROWEQUAL

BitwiseOp
    ::= AMPERSAND
     | CARET
     | PIPE
     | KEYWORD_orelse
     | KEYWORD_catch Payload?

BitShiftOp
    ::= LARROW2
     | RARROW2

AdditionOp
    ::= PLUS
     | MINUS
     | PLUS2
     | PLUSPERCENT
     | MINUSPERCENT

MultiplyOp
    ::= PIPE2
     | ASTERISK
     | SLASH
     | PERCENT
     | ASTERISK2
     | ASTERISKPERCENT

PrefixOp
    ::= EXCLAMATIONMARK
     | MINUS
     | TILDE
     | MINUSPERCENT
     | AMPERSAND
     | KEYWORD_try
     | KEYWORD_await

PrefixTypeOp
    ::= QUESTIONMARK
     | KEYWORD_anyframe MINUSRARROW
     | ArrayTypeStart (ByteAlign | KEYWORD_const | KEYWORD_volatile | KEYWORD_allowzero)*
     | PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN | KEYWORD_const | KEYWORD_volatile | KEYWORD_allowzero)*

SuffixOp
    ::= LBRACKET Expr (DOT2 Expr?)? RBRACKET
     | DOT IDENTIFIER
     | DOTASTERISK
     | DOTQUESTIONMARK

FnCallArguments ::= LPAREN ExprList RPAREN

//# Ptr specific
ArrayTypeStart ::= LBRACKET Expr? RBRACKET

PtrTypeStart
    ::= ASTERISK
     | ASTERISK2
     | PTRUNKNOWN
     | PTRC

//# ContainerDecl specific
ContainerDeclAuto ::= ContainerDeclType LBRACE ContainerMembers RBRACE

ContainerDeclType
    ::= (KEYWORD_struct | KEYWORD_enum | KEYWORD_opaque) (LPAREN Expr RPAREN)?
     | KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? | Expr) RPAREN)?

//# Alignment
ByteAlign ::= KEYWORD_align LPAREN Expr RPAREN

//# Lists
IdentifierList ::= (IDENTIFIER COMMA)* IDENTIFIER?

SwitchProngList ::= (SwitchProng COMMA)* SwitchProng?

AsmOutputList ::= (AsmOutputItem COMMA)* AsmOutputItem?

AsmInputList ::= (AsmInputItem COMMA)* AsmInputItem?

StringList ::= (STRINGLITERAL COMMA)* STRINGLITERAL?

ParamDeclList ::= (ParamDecl COMMA)* ParamDecl?

ExprList ::= (Expr COMMA)* Expr?

//# *** Tokens ***
eof ::= "!."
hex ::= [0-9a-fA-F]
char_escape
    ::= "\\x" hex hex
     | "\\u{" hex+ "}"
     | "\\" [nr\\t'"]
char_char
    ::= char_escape
     | [^\\'\n]
string_char
    ::= char_escape
     | [^\\"\n]

line_comment ::= '//'[^\n]*
line_string ::= ("\\\\" [^\n]* [ \n]*)+
skip ::= ([ \n] | line_comment)*

CHAR_LITERAL ::= "'" char_char "'" skip
FLOAT
    ::= "0x" hex+   "." hex+   ([pP] [-+]? hex+)?   skip
     |      [0-9]+ "." [0-9]+ ([eE] [-+]? [0-9]+)? skip
     | "0x" hex+   "."? [pP] [-+]? hex+   skip
     |      [0-9]+ "."? [eE] [-+]? [0-9]+ skip
INTEGER
    ::= "0b" [01]+  skip
     | "0o" [0-7]+ skip
     | "0x" hex+   skip
     |      [0-9]+ skip
STRINGLITERALSINGLE ::= '"' string_char* '"' skip
STRINGLITERAL
    ::= STRINGLITERALSINGLE
     | line_string                 skip
IDENTIFIER
    ::= '!' keyword [A-Za-z_] [A-Za-z0-9_]* skip
     | '@"' string_char* '"'                            skip
BUILTINIDENTIFIER ::= "@"[A-Za-z_][A-Za-z0-9_]* skip


AMPERSAND            ::= '&'     // ![=]      skip
AMPERSANDEQUAL       ::= '&='    //           skip
ASTERISK             ::= '*'     // ![*%=]    skip
ASTERISK2            ::= '**'    //           skip
ASTERISKEQUAL        ::= '*='    //           skip
ASTERISKPERCENT      ::= '*%'    // ![=]      skip
ASTERISKPERCENTEQUAL ::= '*%='   //           skip
CARET                ::= '^'     // ![=]      skip
CARETEQUAL           ::= '^='    //           skip
COLON                ::= ':'     //           skip
COMMA                ::= ','     //           skip
DOT                  ::= '.'     // ![*.?]    skip
DOT2                 ::= '..'    // ![.]      skip
DOT3                 ::= '...'   //           skip
DOTASTERISK          ::= '.*'    //           skip
DOTQUESTIONMARK      ::= '.?'    //           skip
EQUAL                ::= '='     // ![>=]     skip
EQUALEQUAL           ::= '=='    //           skip
EQUALRARROW          ::= '=>'    //           skip
EXCLAMATIONMARK      ::= '!'     // ![=]      skip
EXCLAMATIONMARKEQUAL ::= '!='    //           skip
LARROW               ::= '<'     // ![<=]     skip
LARROW2              ::= '<<'    // ![=]      skip
LARROW2EQUAL         ::= '<<='   //           skip
LARROWEQUAL          ::= '<='    //           skip
LBRACE               ::= '{'     //           skip
LBRACKET             ::= '['     // ![*]      skip
LPAREN               ::= '('     //           skip
MINUS                ::= '-'     // ![%=>]    skip
MINUSEQUAL           ::= '-='    //           skip
MINUSPERCENT         ::= '-%'    // ![=]      skip
MINUSPERCENTEQUAL    ::= '-%='   //           skip
MINUSRARROW          ::= '->'    //           skip
PERCENT              ::= '%'     // ![=]      skip
PERCENTEQUAL         ::= '%='    //           skip
PIPE                 ::= '|'     // ![|=]     skip
PIPE2                ::= '||'    //           skip
PIPEEQUAL            ::= '|='    //           skip
PLUS                 ::= '+'     // ![%+=]    skip
PLUS2                ::= '++'    //           skip
PLUSEQUAL            ::= '+='    //           skip
PLUSPERCENT          ::= '+%'    // ![=]      skip
PLUSPERCENTEQUAL     ::= '+%='   //           skip
PTRC                 ::= '[*c]'  //           skip
PTRUNKNOWN           ::= '[*]'   //           skip
QUESTIONMARK         ::= '?'     //           skip
RARROW               ::= '>'     // ![>=]     skip
RARROW2              ::= '>>'    // ![=]      skip
RARROW2EQUAL         ::= '>>='   //           skip
RARROWEQUAL          ::= '>='    //           skip
RBRACE               ::= '}'     //           skip
RBRACKET             ::= ']'     //           skip
RPAREN               ::= ')'     //           skip
SEMICOLON            ::= ';'     //           skip
SLASH                ::= '/'     // ![=]      skip
SLASHEQUAL           ::= '/='    //           skip
TILDE                ::= '~'     //           skip

end_of_word ::= '!'[a-zA-Z0-9_] skip
KEYWORD_align       ::= 'align'       //end_of_word
KEYWORD_allowzero   ::= 'allowzero'   //end_of_word
KEYWORD_and         ::= 'and'         //end_of_word
KEYWORD_anyframe    ::= 'anyframe'    //end_of_word
KEYWORD_anytype     ::= 'anytype'     //end_of_word
KEYWORD_asm         ::= 'asm'         //end_of_word
KEYWORD_async       ::= 'async'       //end_of_word
KEYWORD_await       ::= 'await'       //end_of_word
KEYWORD_break       ::= 'break'       //end_of_word
KEYWORD_catch       ::= 'catch'       //end_of_word
KEYWORD_comptime    ::= 'comptime'    //end_of_word
KEYWORD_const       ::= 'const'       //end_of_word
KEYWORD_continue    ::= 'continue'    //end_of_word
KEYWORD_defer       ::= 'defer'       //end_of_word
KEYWORD_else        ::= 'else'        //end_of_word
KEYWORD_enum        ::= 'enum'        //end_of_word
KEYWORD_errdefer    ::= 'errdefer'    //end_of_word
KEYWORD_error       ::= 'error'       //end_of_word
KEYWORD_export      ::= 'export'      //end_of_word
KEYWORD_extern      ::= 'extern'      //end_of_word
KEYWORD_false       ::= 'false'       //end_of_word
KEYWORD_fn          ::= 'fn'          //end_of_word
KEYWORD_for         ::= 'for'         //end_of_word
KEYWORD_if          ::= 'if'          //end_of_word
KEYWORD_inline      ::= 'inline'      //end_of_word
KEYWORD_noalias     ::= 'noalias'     //end_of_word
KEYWORD_nosuspend   ::= 'nosuspend'   //end_of_word
KEYWORD_null        ::= 'null'        //end_of_word
KEYWORD_opaque      ::= 'opaque'      //end_of_word
KEYWORD_or          ::= 'or'          //end_of_word
KEYWORD_orelse      ::= 'orelse'      //end_of_word
KEYWORD_packed      ::= 'packed'      //end_of_word
KEYWORD_pub         ::= 'pub'         //end_of_word
KEYWORD_resume      ::= 'resume'      //end_of_word
KEYWORD_return      ::= 'return'      //end_of_word
KEYWORD_linksection ::= 'linksection' //end_of_word
KEYWORD_struct      ::= 'struct'      //end_of_word
KEYWORD_suspend     ::= 'suspend'     //end_of_word
KEYWORD_switch      ::= 'switch'      //end_of_word
KEYWORD_test        ::= 'test'        //end_of_word
KEYWORD_threadlocal ::= 'threadlocal' //end_of_word
KEYWORD_true        ::= 'true'        //end_of_word
KEYWORD_try         ::= 'try'         //end_of_word
KEYWORD_undefined   ::= 'undefined'   //end_of_word
KEYWORD_union       ::= 'union'       //end_of_word
KEYWORD_unreachable ::= 'unreachable' //end_of_word
KEYWORD_usingnamespace ::= 'usingnamespace' //end_of_word
KEYWORD_var         ::= 'var'         //end_of_word
KEYWORD_volatile    ::= 'volatile'    //end_of_word
KEYWORD_while       ::= 'while'       //end_of_word

keyword ::= KEYWORD_align | KEYWORD_and | KEYWORD_anyframe | KEYWORD_anytype
         | KEYWORD_allowzero | KEYWORD_asm | KEYWORD_async | KEYWORD_await | KEYWORD_break
         | KEYWORD_catch | KEYWORD_comptime | KEYWORD_const | KEYWORD_continue
         | KEYWORD_defer | KEYWORD_else | KEYWORD_enum | KEYWORD_errdefer
         | KEYWORD_error | KEYWORD_export | KEYWORD_extern | KEYWORD_false
         | KEYWORD_fn | KEYWORD_for | KEYWORD_if | KEYWORD_inline
         | KEYWORD_noalias | KEYWORD_null | KEYWORD_opaque | KEYWORD_or
         | KEYWORD_orelse | KEYWORD_packed | KEYWORD_pub
         | KEYWORD_resume | KEYWORD_return | KEYWORD_linksection
         | KEYWORD_struct | KEYWORD_suspend
         | KEYWORD_switch | KEYWORD_test | KEYWORD_threadlocal | KEYWORD_true | KEYWORD_try
         | KEYWORD_undefined | KEYWORD_union | KEYWORD_unreachable
         | KEYWORD_usingnamespace | KEYWORD_var | KEYWORD_volatile | KEYWORD_while

Zig-dev.ebnf:

Root ::= skip ContainerMembers eof

//# *** Top level ***
ContainerMembers
    ::= TestDecl ContainerMembers
     | TopLevelComptime ContainerMembers
     | KEYWORD_pub? TopLevelDecl ContainerMembers
     | ContainerField COMMA ContainerMembers
     | ContainerField


TestDecl ::= KEYWORD_test STRINGLITERALSINGLE Block

TopLevelComptime ::= KEYWORD_comptime BlockExpr

TopLevelDecl
    ::= (KEYWORD_export | KEYWORD_extern STRINGLITERALSINGLE? | (KEYWORD_inline | KEYWORD_noinline))? FnProto (SEMICOLON | Block)
     | (KEYWORD_export | KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl
     | KEYWORD_usingnamespace Expr SEMICOLON

FnProto ::= KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? CallConv? EXCLAMATIONMARK? (KEYWORD_anytype | TypeExpr)

VarDecl ::= (KEYWORD_const | KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON

ContainerField ::= KEYWORD_comptime? IDENTIFIER (COLON TypeExpr ByteAlign?)? (EQUAL Expr)?

//# *** Block Level ***
Statement
    ::= KEYWORD_comptime? VarDecl
     | KEYWORD_comptime BlockExprStatement
     | KEYWORD_nosuspend BlockExprStatement
     | KEYWORD_suspend (SEMICOLON | BlockExprStatement)
     | KEYWORD_defer BlockExprStatement
     | KEYWORD_errdefer BlockExprStatement
     | IfStatement
     | LabeledStatement
     | SwitchExpr
     | AssignExpr SEMICOLON

IfStatement
    ::= IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )?
     | IfPrefix AssignExpr ( SEMICOLON | KEYWORD_else Payload? Statement )

LabeledStatement ::= BlockLabel? (Block | LoopStatement)

LoopStatement ::= KEYWORD_inline? (ForStatement | WhileStatement)

ForStatement
    ::= ForPrefix BlockExpr ( KEYWORD_else Statement )?
     | ForPrefix AssignExpr ( SEMICOLON | KEYWORD_else Statement )

WhileStatement
    ::= WhilePrefix BlockExpr ( KEYWORD_else Payload? Statement )?
     | WhilePrefix AssignExpr ( SEMICOLON | KEYWORD_else Payload? Statement )

BlockExprStatement
    ::= BlockExpr
     | AssignExpr SEMICOLON

BlockExpr ::= BlockLabel? Block

//# *** Expression Level ***
AssignExpr ::= Expr (AssignOp Expr)?

Expr ::= BoolOrExpr

BoolOrExpr ::= BoolAndExpr (KEYWORD_or BoolAndExpr)*

BoolAndExpr ::= CompareExpr (KEYWORD_and CompareExpr)*

CompareExpr ::= BitwiseExpr (CompareOp BitwiseExpr)?

BitwiseExpr ::= BitShiftExpr (BitwiseOp BitShiftExpr)*

BitShiftExpr ::= AdditionExpr (BitShiftOp AdditionExpr)*

AdditionExpr ::= MultiplyExpr (AdditionOp MultiplyExpr)*

MultiplyExpr ::= PrefixExpr (MultiplyOp PrefixExpr)*

PrefixExpr ::= PrefixOp* PrimaryExpr

PrimaryExpr
    ::= AsmExpr
     | IfExpr
     | KEYWORD_break BreakLabel? Expr?
     | KEYWORD_comptime Expr
     | KEYWORD_nosuspend Expr
     | KEYWORD_continue BreakLabel?
     | KEYWORD_resume Expr
     | KEYWORD_return Expr?
     | BlockLabel? LoopExpr
     | Block
     | CurlySuffixExpr

IfExpr ::= IfPrefix Expr (KEYWORD_else Payload? Expr)?

Block ::= LBRACE Statement* RBRACE

LoopExpr ::= KEYWORD_inline? (ForExpr | WhileExpr)

ForExpr ::= ForPrefix Expr (KEYWORD_else Expr)?

WhileExpr ::= WhilePrefix Expr (KEYWORD_else Payload? Expr)?

CurlySuffixExpr ::= TypeExpr InitList?

InitList
    ::= LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE
     | LBRACE Expr (COMMA Expr)* COMMA? RBRACE
     | LBRACE RBRACE

TypeExpr ::= PrefixTypeOp* ErrorUnionExpr

ErrorUnionExpr ::= SuffixExpr (EXCLAMATIONMARK TypeExpr)?

SuffixExpr
    ::= KEYWORD_async PrimaryTypeExpr SuffixOp* FnCallArguments
     | PrimaryTypeExpr (SuffixOp | FnCallArguments)*

PrimaryTypeExpr
    ::= BUILTINIDENTIFIER FnCallArguments
     | CHAR_LITERAL
     | ContainerDecl
     | DOT IDENTIFIER
     | DOT InitList
     | ErrorSetDecl
     | FLOAT
     | FnProto
     | GroupedExpr
     | LabeledTypeExpr
     | IDENTIFIER
     | IfTypeExpr
     | INTEGER
     | KEYWORD_comptime TypeExpr
     | KEYWORD_error DOT IDENTIFIER
     | KEYWORD_false
     | KEYWORD_null
     | KEYWORD_anyframe
     | KEYWORD_true
     | KEYWORD_undefined
     | KEYWORD_unreachable
     | STRINGLITERAL
     | SwitchExpr

ContainerDecl ::= (KEYWORD_extern | KEYWORD_packed)? ContainerDeclAuto

ErrorSetDecl ::= KEYWORD_error LBRACE IdentifierList RBRACE

GroupedExpr ::= LPAREN Expr RPAREN

IfTypeExpr ::= IfPrefix TypeExpr (KEYWORD_else Payload? TypeExpr)?

LabeledTypeExpr
    ::= BlockLabel Block
     | BlockLabel? LoopTypeExpr

LoopTypeExpr ::= KEYWORD_inline? (ForTypeExpr | WhileTypeExpr)

ForTypeExpr ::= ForPrefix TypeExpr (KEYWORD_else TypeExpr)?

WhileTypeExpr ::= WhilePrefix TypeExpr (KEYWORD_else Payload? TypeExpr)?

SwitchExpr ::= KEYWORD_switch LPAREN Expr RPAREN LBRACE SwitchProngList RBRACE

//# *** Assembly ***
AsmExpr ::= KEYWORD_asm KEYWORD_volatile? LPAREN STRINGLITERAL AsmOutput? RPAREN

AsmOutput ::= COLON AsmOutputList AsmInput?

AsmOutputItem ::= LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN (MINUSRARROW TypeExpr | IDENTIFIER) RPAREN

AsmInput ::= COLON AsmInputList AsmClobbers?

AsmInputItem ::= LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN Expr RPAREN

AsmClobbers ::= COLON StringList

//# *** Helper grammar ***
BreakLabel ::= COLON IDENTIFIER

BlockLabel ::= IDENTIFIER COLON

FieldInit ::= DOT IDENTIFIER EQUAL Expr

WhileContinueExpr ::= COLON LPAREN AssignExpr RPAREN

LinkSection ::= KEYWORD_linksection LPAREN Expr RPAREN

CallConv ::= KEYWORD_callconv LPAREN Expr RPAREN

ParamDecl ::= (KEYWORD_noalias | KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType

ParamType
    ::= KEYWORD_anytype
     | DOT3
     | TypeExpr

//# Control flow prefixes
IfPrefix ::= KEYWORD_if LPAREN Expr RPAREN PtrPayload?

WhilePrefix ::= KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr?

ForPrefix ::= KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload

//# Payloads
Payload ::= PIPE IDENTIFIER PIPE

PtrPayload ::= PIPE ASTERISK? IDENTIFIER PIPE

PtrIndexPayload ::= PIPE ASTERISK? IDENTIFIER (COMMA IDENTIFIER)? PIPE


//# Switch specific
SwitchProng ::= SwitchCase EQUALRARROW PtrPayload? AssignExpr

SwitchCase
    ::= SwitchItem (COMMA SwitchItem)* COMMA?
     | KEYWORD_else

SwitchItem ::= Expr (DOT3 Expr)?

//# Operators
AssignOp
    ::= ASTERISKEQUAL
     | SLASHEQUAL
     | PERCENTEQUAL
     | PLUSEQUAL
     | MINUSEQUAL
     | LARROW2EQUAL
     | RARROW2EQUAL
     | AMPERSANDEQUAL
     | CARETEQUAL
     | PIPEEQUAL
     | ASTERISKPERCENTEQUAL
     | PLUSPERCENTEQUAL
     | MINUSPERCENTEQUAL
     | EQUAL

CompareOp
    ::= EQUALEQUAL
     | EXCLAMATIONMARKEQUAL
     | LARROW
     | RARROW
     | LARROWEQUAL
     | RARROWEQUAL

BitwiseOp
    ::= AMPERSAND
     | CARET
     | PIPE
     | KEYWORD_orelse
     | KEYWORD_catch Payload?

BitShiftOp
    ::= LARROW2
     | RARROW2

AdditionOp
    ::= PLUS
     | MINUS
     | PLUS2
     | PLUSPERCENT
     | MINUSPERCENT

MultiplyOp
    ::= PIPE2
     | ASTERISK
     | SLASH
     | PERCENT
     | ASTERISK2
     | ASTERISKPERCENT

PrefixOp
    ::= EXCLAMATIONMARK
     | MINUS
     | TILDE
     | MINUSPERCENT
     | AMPERSAND
     | KEYWORD_try
     | KEYWORD_await

PrefixTypeOp
    ::= QUESTIONMARK
     | KEYWORD_anyframe MINUSRARROW
     | ArrayTypeStart (ByteAlign | KEYWORD_const | KEYWORD_volatile | KEYWORD_allowzero)*
     | PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN | KEYWORD_const | KEYWORD_volatile | KEYWORD_allowzero)*

SuffixOp
    ::= LBRACKET Expr (DOT2 Expr?)? RBRACKET
     | DOT IDENTIFIER
     | DOTASTERISK
     | DOTQUESTIONMARK

FnCallArguments ::= LPAREN ExprList RPAREN

//# Ptr specific
ArrayTypeStart ::= LBRACKET Expr? (COLON Expr)? RBRACKET

PtrTypeStart
    ::= ASTERISK
     | ASTERISK2
     | LBRACKET ASTERISK (LETTERC | COLON Expr)? RBRACKET

//# ContainerDecl specific
ContainerDeclAuto ::= ContainerDeclType LBRACE ContainerMembers RBRACE

ContainerDeclType
    ::= KEYWORD_struct
     | KEYWORD_opaque
     | KEYWORD_enum (LPAREN Expr RPAREN)?
     | KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? | Expr) RPAREN)?

//# Alignment
ByteAlign ::= KEYWORD_align LPAREN Expr RPAREN

//# Lists
IdentifierList ::= (IDENTIFIER COMMA)* IDENTIFIER?

SwitchProngList ::= (SwitchProng COMMA)* SwitchProng?

AsmOutputList ::= (AsmOutputItem COMMA)* AsmOutputItem?

AsmInputList ::= (AsmInputItem COMMA)* AsmInputItem?

StringList ::= (STRINGLITERAL COMMA)* STRINGLITERAL?

ParamDeclList ::= (ParamDecl COMMA)* ParamDecl?

ExprList ::= (Expr COMMA)* Expr?

//# *** Tokens ***
eof ::= '!.'
eol ::= ('\r'? '\n') | eof
hex ::= [0-9a-fA-F]
hex_ ::= ('_'/hex)
dec ::= [0-9]
dec_ ::= ('_'/dec)

dec_int ::= dec (dec_* dec)?
hex_int ::= hex (hex_* dec)?

char_escape
    ::= '\\x' hex hex
     | '\\u{' hex+ '}'
     | '\\' [nr\\t'"]
char_char
    ::= char_escape
     | [^\\'\r\n]
string_char
    ::= char_escape
     | [^\\"\r\n]

line_comment ::= '//'[^\r\n]* eol
line_string ::= ('\\\\' [^\r\n]* eol skip)+
skip ::= ([ \t] | eol | line_comment)*

CHAR_LITERAL ::= "'" char_char "'" skip
FLOAT
    ::= '0x' hex_* hex '.' hex_int ([pP] [-+]? hex_int)? skip
     |      dec_int   '.' dec_int ([eE] [-+]? dec_int)? skip
     | '0x' hex_* hex '.'? [pP] [-+]? hex_int skip
     |      dec_int   '.'? [eE] [-+]? dec_int skip
INTEGER
    ::= '0b' [_01]*  [01]  skip
     | '0o' [_0-7]* [0-7] skip
     | '0x' hex_* hex skip
     |      dec_int   skip
STRINGLITERALSINGLE ::= '"' string_char* '"' skip
STRINGLITERAL
    ::= STRINGLITERALSINGLE
     | line_string skip
IDENTIFIER
    ::= '!'keyword [A-Za-z_] [A-Za-z0-9_]* skip
     | '@"' string_char* '"' skip
BUILTINIDENTIFIER ::= '@'[A-Za-z_][A-Za-z0-9_]* skip


AMPERSAND            ::= '&'      //![=]      skip
AMPERSANDEQUAL       ::= '&='     //          skip
ASTERISK             ::= '*'      //![*%=]    skip
ASTERISK2            ::= '**'     //          skip
ASTERISKEQUAL        ::= '*='     //          skip
ASTERISKPERCENT      ::= '*%'     //![=]      skip
ASTERISKPERCENTEQUAL ::= '*%='    //          skip
CARET                ::= '^'      //![=]      skip
CARETEQUAL           ::= '^='     //          skip
COLON                ::= ':'      //          skip
COMMA                ::= ','      //          skip
DOT                  ::= '.'      //![*.?]    skip
DOT2                 ::= '..'     //![.]      skip
DOT3                 ::= '...'    //          skip
DOTASTERISK          ::= '.*'     //          skip
DOTQUESTIONMARK      ::= '.?'     //          skip
EQUAL                ::= '='      //![>=]     skip
EQUALEQUAL           ::= '=='     //          skip
EQUALRARROW          ::= '=>'     //          skip
EXCLAMATIONMARK      ::= '!'      //![=]      skip
EXCLAMATIONMARKEQUAL ::= '!='     //          skip
LARROW               ::= '<'      //![<=]     skip
LARROW2              ::= '<<'     //![=]      skip
LARROW2EQUAL         ::= '<<='    //          skip
LARROWEQUAL          ::= '<='     //          skip
LBRACE               ::= '{'      //          skip
LBRACKET             ::= '['      //          skip
LPAREN               ::= '('      //          skip
MINUS                ::= '-'      //![%=>]    skip
MINUSEQUAL           ::= '-='     //          skip
MINUSPERCENT         ::= '-%'     //![=]      skip
MINUSPERCENTEQUAL    ::= '-%='    //          skip
MINUSRARROW          ::= '->'     //          skip
PERCENT              ::= '%'      //![=]      skip
PERCENTEQUAL         ::= '%='     //          skip
PIPE                 ::= '|'      //![|=]     skip
PIPE2                ::= '||'     //          skip
PIPEEQUAL            ::= '|='     //          skip
PLUS                 ::= '+'      //![%+=]    skip
PLUS2                ::= '++'     //          skip
PLUSEQUAL            ::= '+='     //          skip
PLUSPERCENT          ::= '+%'     //![=]      skip
PLUSPERCENTEQUAL     ::= '+%='    //          skip
LETTERC              ::= 'c'      //          skip
QUESTIONMARK         ::= '?'      //          skip
RARROW               ::= '>'      //![>=]     skip
RARROW2              ::= '>>'     //![=]      skip
RARROW2EQUAL         ::= '>>='    //          skip
RARROWEQUAL          ::= '>='     //          skip
RBRACE               ::= '}'      //          skip
RBRACKET             ::= ']'      //          skip
RPAREN               ::= ')'      //          skip
SEMICOLON            ::= ';'      //          skip
SLASH                ::= '/'      //![=]      skip
SLASHEQUAL           ::= '/='     //          skip
TILDE                ::= '~'      //          skip

end_of_word ::= '!'[a-zA-Z0-9_] skip
KEYWORD_align       ::= 'align'       //end_of_word
KEYWORD_allowzero   ::= 'allowzero'   //end_of_word
KEYWORD_and         ::= 'and'         //end_of_word
KEYWORD_anyframe    ::= 'anyframe'    //end_of_word
KEYWORD_anytype     ::= 'anytype'     //end_of_word
KEYWORD_asm         ::= 'asm'         //end_of_word
KEYWORD_async       ::= 'async'       //end_of_word
KEYWORD_await       ::= 'await'       //end_of_word
KEYWORD_break       ::= 'break'       //end_of_word
KEYWORD_callconv    ::= 'callconv'    //end_of_word
KEYWORD_catch       ::= 'catch'       //end_of_word
KEYWORD_comptime    ::= 'comptime'    //end_of_word
KEYWORD_const       ::= 'const'       //end_of_word
KEYWORD_continue    ::= 'continue'    //end_of_word
KEYWORD_defer       ::= 'defer'       //end_of_word
KEYWORD_else        ::= 'else'        //end_of_word
KEYWORD_enum        ::= 'enum'        //end_of_word
KEYWORD_errdefer    ::= 'errdefer'    //end_of_word
KEYWORD_error       ::= 'error'       //end_of_word
KEYWORD_export      ::= 'export'      //end_of_word
KEYWORD_extern      ::= 'extern'      //end_of_word
KEYWORD_false       ::= 'false'       //end_of_word
KEYWORD_fn          ::= 'fn'          //end_of_word
KEYWORD_for         ::= 'for'         //end_of_word
KEYWORD_if          ::= 'if'          //end_of_word
KEYWORD_inline      ::= 'inline'      //end_of_word
KEYWORD_noalias     ::= 'noalias'     //end_of_word
KEYWORD_nosuspend   ::= 'nosuspend'   //end_of_word
KEYWORD_noinline    ::= 'noinline'    //end_of_word
KEYWORD_null        ::= 'null'        //end_of_word
KEYWORD_opaque      ::= 'opaque'      //end_of_word
KEYWORD_or          ::= 'or'          //end_of_word
KEYWORD_orelse      ::= 'orelse'      //end_of_word
KEYWORD_packed      ::= 'packed'      //end_of_word
KEYWORD_pub         ::= 'pub'         //end_of_word
KEYWORD_resume      ::= 'resume'      //end_of_word
KEYWORD_return      ::= 'return'      //end_of_word
KEYWORD_linksection ::= 'linksection' //end_of_word
KEYWORD_struct      ::= 'struct'      //end_of_word
KEYWORD_suspend     ::= 'suspend'     //end_of_word
KEYWORD_switch      ::= 'switch'      //end_of_word
KEYWORD_test        ::= 'test'        //end_of_word
KEYWORD_threadlocal ::= 'threadlocal' //end_of_word
KEYWORD_true        ::= 'true'        //end_of_word
KEYWORD_try         ::= 'try'         //end_of_word
KEYWORD_undefined   ::= 'undefined'   //end_of_word
KEYWORD_union       ::= 'union'       //end_of_word
KEYWORD_unreachable ::= 'unreachable' //end_of_word
KEYWORD_usingnamespace ::= 'usingnamespace' //end_of_word
KEYWORD_var         ::= 'var'         //end_of_word
KEYWORD_volatile    ::= 'volatile'    //end_of_word
KEYWORD_while       ::= 'while'       //end_of_word

keyword ::= KEYWORD_align | KEYWORD_allowzero | KEYWORD_and | KEYWORD_anyframe
         | KEYWORD_anytype | KEYWORD_asm | KEYWORD_async | KEYWORD_await
         | KEYWORD_break | KEYWORD_callconv | KEYWORD_catch | KEYWORD_comptime
         | KEYWORD_const | KEYWORD_continue | KEYWORD_defer | KEYWORD_else
         | KEYWORD_enum | KEYWORD_errdefer | KEYWORD_error | KEYWORD_export
         | KEYWORD_extern | KEYWORD_false | KEYWORD_fn | KEYWORD_for | KEYWORD_if
         | KEYWORD_inline | KEYWORD_noalias | KEYWORD_nosuspend | KEYWORD_noinline
         | KEYWORD_null | KEYWORD_opaque | KEYWORD_or | KEYWORD_orelse | KEYWORD_packed
         | KEYWORD_pub | KEYWORD_resume | KEYWORD_return | KEYWORD_linksection
         | KEYWORD_struct | KEYWORD_suspend | KEYWORD_switch
         | KEYWORD_test | KEYWORD_threadlocal | KEYWORD_true | KEYWORD_try
         | KEYWORD_undefined | KEYWORD_union | KEYWORD_unreachable
         | KEYWORD_usingnamespace | KEYWORD_var | KEYWORD_volatile | KEYWORD_while

mingodad avatar May 11 '21 08:05 mingodad

Looking through the code in src/stage1/parser.cpp the statement in grammar.y should be:

Statement
    <- KEYWORD_comptime? VarDecl
     / KEYWORD_comptime BlockExprStatement
     / KEYWORD_nosuspend BlockExprStatement
     / KEYWORD_suspend (SEMICOLON / BlockExprStatement)
     / KEYWORD_defer Payload? BlockExprStatement //////!!!! missing Payload here
     / KEYWORD_errdefer Payload? BlockExprStatement
     / IfStatement
     / LabeledStatement
     / SwitchExpr
     / AssignExpr SEMICOLON

mingodad avatar May 11 '21 12:05 mingodad

@mingodad you'd be better off looking at the self hosted parser in lib/std/zig/parse.zig. The grammar in this repo is correct and defer does not allow a Payload.

ifreund avatar May 11 '21 12:05 ifreund

Thanks for pointing out ! This mean that the src/stage1/parser.cpp is out of sync ?

mingodad avatar May 11 '21 12:05 mingodad

This mean that the src/stage1/parser.cpp is out of sync ?

No, on looking at it myself the stage1 parser adhears to the grammar in this case: https://github.com/ziglang/zig/blob/ac546f56eb2b9fd50a69f1d0bda3d0d070b76f52/src/stage1/parser.cpp#L965-L966

ifreund avatar May 11 '21 12:05 ifreund

Thank you for pointing out ! Looking at it again as you pointed out I can see that I failed to understand the code. Thank you again !

mingodad avatar May 11 '21 12:05 mingodad

Thank you for pointing out ! Looking at it again as you pointed out I can see that I failed to understand the code. Thank you again !

No problem, as usual the self hosted parser is much more readable :)

ifreund avatar May 11 '21 12:05 ifreund

I'm trying to write a zig parser using CocoR https://ssw.jku.at/Research/Projects/Coco/ and in doing so I'm getting lots of warnings/errors due to grammar ambiguity and recursion, for example comptime in PimaryExpr is recursive and zig actually accepts things like this (also with other qualifiers/attributes):

const std = @import("std");

fn fibonacci(index: u32) u32 {
    if (index < 2) return index;
    return fibonacci(index - 1) + fibonacci(index - 2);
}

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();
    try stdout.print("Hello, {}!\n", .{"world"});
    try stdout.print("fibonacci(7)= {}\n", .{fibonacci(7)});
    var x7 = comptime comptime comptime fibonacci(7); ////!!! we can have an infinite/long sequence of comptime
    try stdout.print("fibonacci(7)= {}\n", .{x7});
}

mingodad avatar May 11 '21 15:05 mingodad

I'm trying to write a zig parser using CocoR https://ssw.jku.at/Research/Projects/Coco/ and in doing so I'm getting lots of warnings/errors due to grammar ambiguity and recursion, for example comptime in PimaryExpr is recursive and zig actually accepts things like this (also with other qualifiers/attributes):

Parsing Expression Grammars such as the grammar in this repo are by definition unambiguous. I don't see recursion as an issue in general either, although that specific case of repeated comptime keywords will be addressed by https://github.com/ziglang/zig/issues/8364.

ifreund avatar May 11 '21 15:05 ifreund

One recursion/duplicate/ambiguity for comptime starts in Statement:

Statement
    <- KEYWORD_comptime? VarDecl
     / KEYWORD_comptime BlockExprStatement ///!!! BlockExprStatement can start with comptime
...
BlockExprStatement
    <- BlockExpr
     / AssignExpr SEMICOLON ///!!! from here
...
AssignExpr <- Expr (AssignOp Expr)? ///!!! going down here Expr can start recursively with comptime
...
PrimaryExpr
    <- AsmExpr
...
     / KEYWORD_comptime Expr ///!!! here the recursion starts
...

mingodad avatar May 11 '21 15:05 mingodad

It also happen with nosuspend:

const std = @import("std");

fn fibonacci(index: u32) u32 {
    if (index < 2) return index;
    return fibonacci(index - 1) + fibonacci(index - 2);
}

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();
    try stdout.print("Hello, {}!\n", .{"world"});
    try stdout.print("fibonacci(7)= {}\n", .{fibonacci(7)});
    var x7 = comptime nosuspend comptime nosuspend comptime comptime nosuspend nosuspend fibonacci(7);
    try stdout.print("fibonacci(7)= {}\n", .{x7});
}

Output:

zig run hello-ct.zig 
Hello, world!
fibonacci(7)= 13
fibonacci(7)= 13

mingodad avatar May 11 '21 17:05 mingodad

After talking with my pillow I'm convinced that one good way to make this grammar less trick/ambiguous/recursive would be to create a subgroup of Expr let's call it AssignableExpr with all valid Expr elements that are valid on the left hand side of an assignment.

//# *** Expression Level ***
AssignExpr =
	AssignableExpr [AssignOp Expr]
	.

AssignableExpr =
	/*Expr*/
	IDENTIFIER
        ...
	.

Also remove all recursive elements from PrimaryExpr and PrimaryTypeExpr like comptime that as I could see so far is used/valid in four cases: TopLevelComptime, VarDecl, prefixing function calls and prefixing parameter declarations.

mingodad avatar May 12 '21 12:05 mingodad