From d8bbdf5ae651a465393ec5a593a857d3a72f4179 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 17 May 2026 12:54:22 +0100 Subject: [PATCH] Add EBNF for Odin --- odin.ebnf | 466 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 466 insertions(+) create mode 100644 odin.ebnf diff --git a/odin.ebnf b/odin.ebnf new file mode 100644 index 000000000..0b0932b86 --- /dev/null +++ b/odin.ebnf @@ -0,0 +1,466 @@ +/* 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" +*/ + + +File = { FileTag } PackageDecl ";" { TopLevelStmt } ; +FileTag = "#+" IDENT [ TagValue ] NEWLINE ; /* #+build, #+private, #+feature, … */ +TagValue = { ANY_CHAR } ; +PackageDecl = "package" IDENT ; + +TopLevelStmt = ImportDecl ";" + | ForeignImportDecl ";" + | ForeignBlock ";" + | WhenStmt + | Decl ";" ; + + +ImportDecl = { Attribute } "import" [ IDENT ] STRING_LIT ; + +ForeignImportDecl = { Attribute } "foreign" "import" [ IDENT ] ( STRING_LIT | "{" ForeignImportPathList "}" ) ; + +ForeignImportPathList = Expr { "," Expr } [ "," ] ; + + +Decl = { Attribute } ValueDecl ; + +ValueDecl = IdentList ":" Type + | IdentList ":" [ Type ] "=" ExprList + | IdentList ":" [ Type ] ":" ExprList ; + +IdentList = IDENT { "," IDENT } ; +ExprList = Expr { "," Expr } ; + +Stmt = EmptyStmt + | Decl ";" + | ExprStmt ";" + | AssignStmt ";" + | BlockStmt + | IfStmt + | WhenStmt + | ForStmt + | SwitchStmt + | TypeSwitchStmt + | DeferStmt + | ReturnStmt ";" + | BranchStmt ";" + | UsingStmt ";" + | Attribute Stmt + | Label Stmt + | ForeignBlock + | DirectiveStmt ; + + +EmptyStmt = ";" ; + + +BlockStmt = [ Label ] "{" StmtList "}" ; +StmtList = { Stmt } ; +Label = IDENT ":" ; + + + +ExprStmt = Expr ; + +AssignStmt = ExprList AssignOp ExprList ; + +AssignOp = "=" | "+=" | "-=" | "*=" | "/=" | "%=" | "%%=" + | "|=" | "~=" | "&=" | "&~=" + | "<<=" | ">>=" | "&&=" | "||=" ; + + + +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 } ; + + + +BranchStmt = "break" [ IDENT ] + | "continue" [ IDENT ] + | "fallthrough" ; + +DeferStmt = "defer" ( Stmt | ( "{" StmtList "}" ) ) ; + +ReturnStmt = [ ReturnStmtDirectivePrefix ] "return" [ ExprList ] ; +ReturnStmtDirectivePrefix = "#force_inline" | "#force_no_inline" | "#must_tail" ; + + +UsingStmt = "using" ( IdentList | Expr ) ; + + +SimpleStmt = ExprStmt | AssignStmt | ValueDecl ; + + +StmtBody = "{" StmtList "}" + | "do" Stmt ; /* must be on the same line as the initial token of the statement */ + + +/* Binary Expressions */ + +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 = "+" | "-" | "~" | "&" | "!" ; + + + +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 */ + + +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 ; + +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 ] ; + +ForeignBlock = { Attribute } "foreign" [ IDENT ] "{" { ForeignDecl ";" } "}" ; + +ForeignDecl = { Attribute } ValueDecl ; + + +DirectiveStmt = "#assert" "(" Expr [ "," STRING_LIT ] ")" + | "#panic" "(" STRING_LIT ")" + | "#force_inline" CallBody + | "#force_no_inline" CallBody + | "#must_tail" CallBody ; \ No newline at end of file