/* The Odin Programming Language Expressed as in EBNF */

/*
EBNF representation of itself:

Production  = name "=" [ Expression ] "." ;
Expression  = Alternative { "|" Alternative } ;
Alternative = Term { Term } ;
Term        = name | token [ "…" token ] | Group | Option | Repetition ;
Group       = "(" Expression ")" ;
Option      = "[" Expression "]" ;
Repetition  = "{" Expression "}" ;
*/


/* Tokens for Lexical Grammar */

IDENT            = ( LETTER | "_" ) { LETTER | DIGIT | "_" } ;
LETTER           = "a".."z" | "A".."Z" | "_" | UNICODE_LETTER ;

INT_LIT          = DECIMAL_LIT | BINARY_LIT | OCTAL_LIT | DOZENAL_LIT | HEX_LIT ;
DECIMAL_LIT      = DIGIT { [ "_" ] DIGIT } ;
BINARY_LIT       = "0b" BIN_DIGIT { [ "_" ] BIN_DIGIT } ;
OCTAL_LIT        = "0o" OCT_DIGIT { [ "_" ] OCT_DIGIT } ;
DOZENAL_LIT      = "0z" DOZ_DIGIT { [ "_" ] DOZ_DIGIT } ;
HEX_LIT          = "0x" HEX_DIGIT { [ "_" ] HEX_DIGIT } ;

FLOAT_LIT        = DIGIT { DIGIT } "." DIGIT { DIGIT } [ EXPONENT ]
                 | DIGIT { DIGIT } EXPONENT
                 | "0h" HEX_DIGIT { [ "_" HEX_DIGIT ]} ;

EXPONENT         = ( "e" | "E" ) [ "+" | "-" ] DIGIT { DIGIT } ;

IMAGINARY_LIT    = ( INT_LIT | FLOAT_LIT ) ( "i" | "j" | "k" ) ;

RUNE_LIT         = "'" ( CHAR | ESCAPE_SEQ ) "'" ;

STRING_LIT       = '"' { CHAR | ESCAPE_SEQ } '"' ;
RAW_STRING_LIT   = "`" { ANY_CHAR } "`" ;

ESCAPE_SEQ       = "\" ( "a" | "b" | "e" | "f" | "n" | "r" | "t" | "v"
                       | "\\" | '"' | "'"
                       | OCT_DIGIT OCT_DIGIT OCT_DIGIT
                       | "x" HEX_DIGIT HEX_DIGIT
                       | "u" HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
                       | "U" HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
                             HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT ) ;

DIGIT            = "0".."9" ;
BIN_DIGIT        = "0" | "1" ;
OCT_DIGIT        = "0".."7" ;
DOZ_DIGIT        = "0".."9" | "a"| "b"  | "A" | "B" ;
HEX_DIGIT        = "0".."9" | "a".."f" | "A".."F" ;

COMMENT          = "//" { ANY_CHAR } NEWLINE
                 | "/*" { ANY_CHAR | COMMENT } "*/" /* nestable */
                 | "#!" { ANY_CHAR } NEWLINE ;

/* Odin uses automatic semicolon insertion similar to Go/Python:
   a newline acts as ";" after certain tokens such as IDENT,
   literals, ")", "]", "}", "^", "---", "break", "continue",
   "fallthrough", "return", "or_return", "or_break", "or_continue".

   During parsing, ";" will be ignored with matching brackets in expressions similar to Python:
   "(" ")"
   "[" "]"
   "{" "}"
*/



/* Syntax Grammar */

/* NOTE: Any directive such as "#partial" can also be written with any number of whitespace after the "#"
         This grammar is simplified to represent minimize the need for "#" "partial"
*/

/* Source File Structure */

File            = { FileTag } PackageDecl ";" { TopLevelStmt } ;
FileTag         = "#+" IDENT [ TagValue ] NEWLINE ;           /* #+build, #+private, #+feature, … */
TagValue        = { ANY_CHAR } ;
PackageDecl     = "package" IDENT ;

TopLevelStmt    = ImportDecl ";"
                | ForeignImportDecl ";"
                | ForeignBlock ";"
                | WhenStmt
                | Decl ";" ;


/* Import Declarations */

ImportDecl         = { Attribute } "import" [ IDENT ] STRING_LIT ;

ForeignImportDecl  = { Attribute } "foreign" "import" [ IDENT ] ( STRING_LIT | "{" ForeignImportPathList "}" ) ;

ForeignImportPathList = Expr { "," Expr } [ "," ] ;

/* Value Declarations */

Decl             = { Attribute } ValueDecl ;

ValueDecl        = IdentList ":" Type
                 | IdentList ":" [ Type ] "=" ExprList
                 | IdentList ":" [ Type ] ":" ExprList ;

IdentList        = IDENT { "," IDENT } ;
ExprList         = Expr { "," Expr } ;

/* Foreign Block Declaration */

ForeignBlock     = { Attribute } "foreign" [ IDENT ] "{" { ForeignDecl ";" } "}" ;

ForeignDecl      = { Attribute } ValueDecl ;


/* Statements */

Stmt             = EmptyStmt
                 | Decl ";"
                 | ExprStmt ";"
                 | AssignStmt ";"
                 | BlockStmt
                 | IfStmt
                 | WhenStmt
                 | ForStmt
                 | SwitchStmt
                 | TypeSwitchStmt
                 | DeferStmt
                 | ReturnStmt ";"
                 | BranchStmt ";"
                 | UsingStmt ";"
                 | Attribute Stmt
                 | Label Stmt
                 | ForeignBlock
                 | DirectiveStmt ;


EmptyStmt        = ";" ;

/* Directive Statements */

DirectiveStmt    = "#assert" "(" Expr [ "," STRING_LIT ] ")"
                 | "#panic"  "(" STRING_LIT ")"
                 | "#force_inline"    CallBody
                 | "#force_no_inline" CallBody
                 | "#must_tail"       CallBody ;

/* Block Statement */

BlockStmt        = [ Label ] "{" StmtList "}" ;
StmtList         = { Stmt } ;
Label            = IDENT ":" ;

/* Assignment Statements */

ExprStmt         = Expr ;

AssignStmt       = ExprList AssignOp ExprList ;

AssignOp         = "=" | "+=" | "-=" | "*=" | "/=" | "%=" | "%%="
                 | "|=" | "~=" | "&=" | "&~="
                 | "<<=" | ">>=" | "&&=" | "||=" ;

/* Control Flow */

IfStmt           = "if" [ SimpleStmt ";" ] Expr StmtBody
                   { "else" "if" [ SimpleStmt ";" ] Expr StmtBody }
                   [ "else" StmtBody ] ;

WhenStmt         = "when" Expr StmtBody
                   { "else" "when" Expr StmtBody }
                   [ "else" StmtBody ] ;

ForStmt          = [ Label ] [ ForDirectivePrefix ]
                   "for" [ ForHead ] StmtBody ;

ForDirectivePrefix = "#reverse"
                   | "#unroll" [ CallBody ] ;

ForHead          = RangeClause
                 | ForCondition
                 | ForCClause ;

ForCondition     = Expr ;

ForCClause       = [ SimpleStmt ] ";" [ Expr ] ";" [ SimpleStmt ] ;

RangeClause      = [ [ SimpleStmt ] ";" ] [ [ "&" ] IDENT { "," [ "&" ] IDENT } "in" ] RangeExpr ;

RangeExpr        = Expr
                 | Expr "..<" Expr
                 | Expr "..=" Expr ;

SwitchStmt       = [ Label ] [ "#partial" ] "switch" [ SimpleStmt ";" ] [ Expr ]
                   "{" { CaseClause } "}" ;

CaseClause       = "case" [ ExprList ] ":" StmtList ;

TypeSwitchStmt   = [ Label ] [ "#partial" ] "switch" IDENT "in" Expr
                   "{" { TypeCaseClause } "}" ;

TypeCaseClause   = "case" [ TypeList ] ":" StmtList ;

TypeList         = Type { "," Type } ;


/* Branch & Defer */

BranchStmt       = "break" [ IDENT ]
                 | "continue" [ IDENT ]
                 | "fallthrough" ;

DeferStmt        = "defer" ( Stmt | ( "{" StmtList "}" ) ) ;

ReturnStmt       = [ ReturnStmtDirectivePrefix ] "return" [ ExprList ] ;
ReturnStmtDirectivePrefix = "#force_inline" | "#force_no_inline" | "#must_tail" ;

/* Using Statement */

UsingStmt        = "using" ( IdentList | Expr ) ;

/* Simple Statement (used in control flow init) */

SimpleStmt       = ExprStmt | AssignStmt | ValueDecl ;

/* Body for Control Flow */

StmtBody         = "{" StmtList "}"
                 | "do" Stmt ; /* must be on the same line as the initial token of the statement */

/* Expressions */

/* Binary Expressions
   Operator Precedence (low -> high)
*/

Expr             = OrElseExpr ;

OrElseExpr       = TernaryExpr { "or_else" TernaryExpr } ;

TernaryExpr      = RangeExprLvl
                 | RangeExprLvl "?"    RangeExprLvl ":"    TernaryExpr
                 | RangeExprLvl "if"   RangeExprLvl "else" TernaryExpr
                 | RangeExprLvl "when" RangeExprLvl "else" TernaryExpr ;

RangeExprLvl     =  LogicalOrExpr [ "..<" | "..=" ] LogicalOrExpr ;

LogicalOrExpr    = LogicalAndExpr { "||" LogicalAndExpr } ;
LogicalAndExpr   = ComparisonExpr { "&&" ComparisonExpr } ;

ComparisonExpr   = AddExpr { CompareOp AddExpr } ;
CompareOp        = "==" | "!=" | "<" | "<=" | ">" | ">=" ;

AddExpr          = MulExpr { AddOp MulExpr } ;
AddOp            = "+" | "-" | "|" | "~" | "in" | "not_in" ;

MulExpr          = UnaryExpr { MulOp UnaryExpr } ;
MulOp            = "*" | "/" | "%" | "%%" | "&" | "&~" | "<<" | ">>" ;

/* Unary Expression */

UnaryExpr        = PostfixExpr
                 | UnaryOp UnaryExpr
                 | "cast" "(" Type ")" UnaryExpr
                 | "transmute" "(" Type ")" UnaryExpr
                 | "auto_cast" UnaryExpr
                 | "." IDENT                            /* implicit selector */;

UnaryOp          = "+" | "-" | "~" | "&" | "!" ;


/* Postfix / Operand Expressions */

PostfixExpr      = Operand { PostfixOp } ;

PostfixOp        = CallBody                                     /* call                    */
                 | "->" IDENT CallBody                          /* selector call           */
                 | "." IDENT                                    /* field selector          */
                 | "." "?"                                      /* inferred type assertion */
                 | "." "(" Type ")"                             /* type assertion          */
                 | "[" Expr "]"                                 /* index                   */
                 | "[" [ Expr ] ":" [ Expr ] "]"                /* slice                   */
                 | "[" Expr "," Expr "]"                        /* matrix index            */
                 | "^"                                          /* pointer deref           */
                 | PostfixOrOp ;

CallBody         = "(" [ ArgList ] ")" ;


PostfixOrOp      = "or_return"
                 | "or_break"    [ IDENT ]
                 | "or_continue" [ IDENT ] ;

ArgList          = Arg { "," Arg } [ "," ] ;
Arg              = [ IDENT "=" ] Expr
                 | ".." Expr ;                                  /* spread */


Operand          = IDENT
                 | "---"                                        /* unintialized expression */
                 | "context"
                 | "typeid"
                 | Literal
                 | "(" Expr ")"
                 | ProcLiteral
                 | ProcGroup
                 | [ "#partial" ] CompoundLiteral
                 | DirectiveExpr
                 | Type ;

DirectiveExpr    = "#" IDENT ; /* Used as the catch all */


/* Literals */

Literal          = INT_LIT
                 | FLOAT_LIT
                 | IMAGINARY_LIT
                 | RUNE_LIT
                 | STRING_LIT
                 | RAW_STRING_LIT ;



CompoundLiteral  = [ Type ] "{" [ ElementList ] "}" ;

ElementList      = Element { "," Element } [ "," ] ;

Element          = Expr [ "=" Expr ]
                 | ( Expr "..<" Expr | Expr "..=" Expr ) "=" Expr ;

/* Types */

Type             = TypeName
                 | TypeLit
                 | "(" Type ")"
                 | "distinct" Type
                 | "typeid"
                 | "#type" Type ;

TypeName         = IDENT
                 | QualifiedIdent
                 | PolyType ;

QualifiedIdent   = IDENT "." IDENT ;

PolyType         = "$" IDENT [ "/" Type ] ;

TypeLit          = PointerType
                 | MultiPointerType
                 | ArrayType
                 | SliceType
                 | DynArrayType
                 | FixedCapDynArrayType
                 | MapType
                 | StructType
                 | UnionType
                 | EnumType
                 | BitSetType
                 | BitFieldType
                 | ProcType
                 | MatrixType
                 | SimdType
                 | SOAType ;


PointerType         = "^" Type ;
MultiPointerType    = "[^]" Type ;



ArrayType           = "[" Expr "]" Type                          /* fixed-length or enumerated array */
                    | "[" "?" "]"  Type                          /* infer-length array       */
                    | "[" EnumType "]" Type ;                    /* enumerated array         */

SliceType           = "[" "]" Type ;

DynArrayType        = "[" "dynamic" "]" Type ;

FixedCapDynArrayType = "[" "dynamic" ";" Expr "]" Type ;        /* [dynamic; N]T            */

MapType             = "map" "[" Type "]" Type ;


StructType       = "struct"
                   [ "(" PolyParamList ")" ]
                   [ StructDirectives ]
                   [ WhereClause ]
                   "{" [ FieldList ] "}" ;

StructDirectives = { "#packed"
                   | "#raw_union"
                   | "#align" "(" Expr ")"
                   | "#min_field_align" "(" Expr ")"
                   | "#max_field_align" "(" Expr ")"
                   | "#simple"
                   | "#all_or_none" } ;

FieldList        = Field { "," Field } [ "," ] ;

Field            = FieldNameList ":" Type [ FieldTag ] ;

FieldNameList    = FieldName { "," FieldName } ;
FieldName        = [ "using" | "#subtype" ] IDENT ;

FieldTag         = STRING_LIT | RAW_STRING_LIT ;

UnionType        = "union"
                   [ "(" PolyParamList ")" ]
                   [ UnionDirectives ]
                   [ WhereClause ]
                   "{" [ TypeList ] "}" ;

UnionDirectives  = { "#no_nil"
                   | "#shared_nil"
                   | "#align" "(" Expr ")" } ;


EnumType         = "enum" [ Type ] "{" [ EnumFieldList ] "}" ;

EnumFieldList    = EnumField { "," EnumField } [ "," ] ;
EnumField        = IDENT [ "=" Expr ] ;


BitSetType       = "bit_set" "[" ( Type | RangeExpr ) [ ";" Type ] "]" ;

BitFieldType     = "bit_field" Type "{" [ BitFieldList ] "}" ;

BitFieldList     = BitFieldField { "," BitFieldField } [ "," ] ;
BitFieldField    = IDENT ":" Type "|" Expr ;


ProcType         = "proc" [ CallingConvention ] "(" [ ProcParamList ] ")"
                   [ "->" ProcResults ] ;

ProcLiteral      = ProcType [ WhereClause ] ( ProcBody | "---" ) ; /* --- = no body / foreign  */
ProcGroup        = "proc" "{" ProcGroupList "}" ;                  /* procedure group          */


ProcBody         = "{" { Stmt } "}"
ProcGroupList    = Expr { "," Expr } [ "," ] ;

CallingConvention = STRING_LIT ;                                /* "odin", "c", "std", "contextless", "fast", "none", etc. */

ProcParamList    = ProcParam { "," ProcParam } [ "," ] ;

ProcParam        = [ ParamPrefix ] PolyIdentList ":" [ ParamModifier ] ProcParamType [ "=" Expr ] ;

ProcParamType    = Type
                 | "typeid" "/" ProcParamType ;

PolyIdent        = [ "$" ] IDENT ;
PolyIdentList    = PolyIdent { "," PolyIdent } ;

ParamPrefix      = "using"
                 | "#no_alias"
                 | "#any_int"
                 | "#by_ptr"
                 | "#c_vararg"
                 | "#no_broadcast" ;

ParamModifier    = ".." ;                                       /* variadic */

ProcResults      = Type
                 | "(" [ FieldList ] ")" ;

MatrixType       = [ "#row_major" | "#column_major" ] "matrix" "[" Expr "," Expr "]" Type ;


SimdType         = "#simd" "[" Expr "]" Type ;


SOAType          = "#soa" ( ArrayType | SliceType | DynArrayType | FixedCapDynArrayType ) ;


PolyParamList    = PolyParam { "," PolyParam } ;
PolyParam        = PolyIdent ":" Type ;

WhereClause      = "where" ConstraintExpr { "," ConstraintExpr } ;
ConstraintExpr   = Expr ;


Attribute        = "@" "(" AttrElemList ")"
                 | "@" IDENT ;                                  /* shorthand: @private */

AttrElemList     = AttrElem { "," AttrElem } [ "," ] ;
AttrElem         = IDENT [ "=" Expr ] ;