Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Grammar

A reference grammar for .prim files. EBNF-flavored notation; the canonical implementation lives in src/parser/grammar.rs.

File          ::= ( NamespaceLine NEWLINE )?
                  ( UseStatement NEWLINE )*
                  ( Item NEWLINE? )*

Item          ::= Decl
                | LineComment
                | DocComment
                | FileDocComment
                | BlankLine

NamespaceLine ::= "namespace" Path

Path          ::= Ident ( "::" Ident )*

UseStatement  ::= "use" Path                              // single
                | "use" Path "::" "{" UseList "}"          // brace

UseList       ::= Ident ( "," Ident )* ","?

Decl          ::= ( DocBlock )? ( Attribute NEWLINE )*
                  ( ConstDecl | EnumDecl | TypeAliasDecl )

ConstDecl     ::= TypeExpr Ident "=" Value
                  // Ident is SCREAMING_SNAKE_CASE

EnumDecl      ::= "enum" Ident ( ":" TypeExpr )? "{"
                  ( EnumVariant ( "," EnumVariant )* ","? )?
                  "}"

EnumVariant   ::= ( DocBlock )? Ident ( "=" Value )?

TypeAliasDecl ::= "type" Ident "=" TypeExpr

TypeExpr      ::= Path                                     // bare or qualified
                | TypeExpr "[]"                            // array sugar
                | TypeExpr "?"                             // optional sugar
                | "array"    "<" TypeExpr ">"
                | "array"    "<" TypeExpr "," IntLit ">"   // fixed-size
                | "optional" "<" TypeExpr ">"
                | "map"      "<" TypeExpr "," TypeExpr ">"
                | "tuple"    "<" TypeExpr ( "," TypeExpr )* ","? ">"

Value         ::= IntLit | FloatLit | StrLit | BoolLit
                | "none"
                | Path                                     // enum variant
                | "[" ValueList? "]"                       // array / tuple
                | "{" MapEntries? "}"                      // map
                | "-" ( IntLit | FloatLit )                // negation

ValueList     ::= Value ( "," Value )* ","?

MapEntries    ::= MapEntry ( "," MapEntry )* ","?
MapEntry      ::= MapKey ":" Value
MapKey        ::= StrLit | Ident | IntLit

Attribute     ::= "@" Ident ( "(" AttrArgs? ")" )?
AttrArgs      ::= AttrArg ( "," AttrArg )* ","?
AttrArg       ::= Ident | StrLit | IntLit | BoolLit

DocBlock      ::= ( "///" RestOfLine NEWLINE )+
LineComment   ::= "//" RestOfLine
DocComment    ::= "///" RestOfLine
FileDocComment::= "//!" RestOfLine

Ident         ::= [A-Za-z_][A-Za-z0-9_]*

IntLit        ::= ( "0x" HexDigit+ | "0b" BinDigit+ | "0o" OctDigit+
                  | DecDigit+ ) UnitSuffix?
FloatLit      ::= DecDigit+ "." DecDigit+ ( [eE] [+-]? DecDigit+ )? UnitSuffix?

StrLit        ::= '"' StringChar* '"'
                | "r" "#"* '"' RawStringChar* '"' "#"*

BoolLit       ::= "true" | "false"

UnitSuffix    ::= [A-Za-z]+    // e.g. ms, s, h, KiB, MiB, GiB

Rules of thumb

  • Newlines terminate top-level items, with one exception: inside <>, [], {}, (), newlines are insignificant. This is what lets long type expressions and multi-line value literals work.
  • One declaration per line outside collection delimiters.
  • No semicolons. Newlines do the same job and there’s no expression context where ASI-style ambiguity could arise.
  • Trailing commas are accepted in every comma-separated list. On value-side […] and {…} literals, a trailing comma signals “keep me multi-line” to the formatter — see Values.

Reserved tokens

Keywords (lex-time): namespace, enum, type, use, as, true, false, none.

as is reserved for the future use a::b::C as D form (RFC 0003).

Lexical structure

Whitespace is space, tab, carriage return, and newline. Newlines are significant tokens (the lexer emits Newline and, for blank-line runs, BlankLine). Inside delimiters the parser consumes them as trivia.

Comments are lexed as // ..., /// ..., or //! ... to end of line. Block comments (/* */) are explicitly rejected with a diagnostic.

Underscores are accepted in numeric literals as digit separators (1_000_000, 0xFF_FF). They have no semantic value.

Differences from RFC text

This grammar is the implementation reference. Where it disagrees with RFC 0002 or RFC 0003, the implementation wins; the RFCs are decision records, not specs.