(* Blaise Language Grammar — EBNF
 *
 * This grammar reflects the language as implemented through Phase 3 of the
 * Blaise compiler.  It is derived directly from uParser.pas and serves as
 * the authoritative reference for the parser, language tooling, and future
 * extensions.
 *
 * Notation
 * --------
 *   { x }      zero or more repetitions of x
 *   [ x ]      optional x (zero or one)
 *   ( x | y )  alternation
 *   "..."       terminal — literal token text (case-insensitive keywords)
 *   UPPER       terminal — token class defined in the Tokens section
 *
 * The Tokens section defines the terminal vocabulary produced by the lexer.
 * The Grammar section defines the syntactic structure.
 *)


(* ═══════════════════════════════════════════════════════════════════════════
 * TOKENS
 * ═══════════════════════════════════════════════════════════════════════════ *)

(* Literals *)
(*
 * Integer literals support four bases.  Underscore separators may appear
 * between digits (never at the start or end of the digit run, and never
 * immediately after the base prefix).
 *
 *   dec_digit   ::= "0" .. "9"
 *   hex_digit   ::= "0" .. "9" | "A" .. "F" | "a" .. "f"
 *   oct_digit   ::= "0" .. "7"
 *   bin_digit   ::= "0" | "1"
 *
 *   digit_seq(d) ::= d { [ "_" ] d }
 *      (underscore may appear between two digits of kind d, but not
 *       before the first digit or after the last)
 *
 * INT_LIT is one of:
 *   decimal  :  digit_seq(dec_digit)
 *   hex      :  "$"  digit_seq(hex_digit)
 *   octal    :  "&"  digit_seq(oct_digit)
 *   binary   :  "%"  digit_seq(bin_digit)
 *
 * FLOAT_LIT is a decimal-only form (no base prefix):
 *   digit_seq(dec_digit) "." digit_seq(dec_digit)
 *       [ ( "e" | "E" ) [ "+" | "-" ] digit_seq(dec_digit) ]
 *
 * Underscores are also permitted between digits in the fractional and
 * exponent parts of FLOAT_LIT, subject to the same adjacency rules.
 * Underscores may not appear adjacent to the decimal point or the
 * "e"/"E" exponent marker.
 *)
INT_LIT
    = dec_digit { [ "_" ] dec_digit }
    | "$" hex_digit { [ "_" ] hex_digit }
    | "&" oct_digit { [ "_" ] oct_digit }
    | "%" bin_digit { [ "_" ] bin_digit }
    ;

FLOAT_LIT
    = dec_digit { [ "_" ] dec_digit }
      "."
      dec_digit { [ "_" ] dec_digit }
      [ ( "e" | "E" ) [ "+" | "-" ] dec_digit { [ "_" ] dec_digit } ]
    ;

STRING_LIT    = "'" { char | "''" } "'" ;
TEXT_BLOCK_LIT = "'''" NEWLINE { any_char | NEWLINE } "'''" ;
(* TEXT_BLOCK_LIT: opening ''' must be followed immediately by a newline
   (LF, CR, or CRLF); any other character after ''' falls back to the
   classic string parse.  Closing ''' must not be followed by another '.
   The column position of the closing ''' sets the indentation baseline —
   all content lines are de-dented by that many columns.  Single quotes
   inside the block need no escaping. *)

(* Identifiers (case-insensitive) *)
IDENT    = letter { letter | digit | "_" } ;

(* Keywords — all case-insensitive *)
PROGRAM        = "program"        ;
UNIT           = "unit"           ;
INTERFACE      = "interface"      ;
IMPLEMENTATION = "implementation" ;
USES           = "uses"           ;
TYPE           = "type"           ;
RECORD         = "record"         ;
PACKED         = "packed"         ;
CLASS          = "class"          ;
VAR            = "var"            ;
THREADVAR      = "threadvar"      ;
OUT            = "out"            ;
BEGIN          = "begin"          ;
END            = "end"            ;
IF             = "if"             ;
THEN           = "then"           ;
ELSE           = "else"           ;
WHILE          = "while"          ;
DO             = "do"             ;
FOR            = "for"            ;
TO             = "to"             ;
DOWNTO         = "downto"         ;
TRY            = "try"            ;
EXCEPT         = "except"         ;
FINALLY        = "finally"        ;
RAISE          = "raise"          ;
EXIT           = "exit"           ;
NIL            = "nil"            ;
IS             = "is"             ;
AS             = "as"             ;
DIV            = "div"            ;
VIRTUAL        = "virtual"        ;
OVERRIDE       = "override"       ;
PROCEDURE      = "procedure"      ;
FUNCTION       = "function"       ;
CONSTRUCTOR    = "constructor"    ;
DESTRUCTOR     = "destructor"     ;

(* Context-sensitive soft keywords — IDENT that is treated as a keyword in a
 * specific position but may be used as an identifier elsewhere *)
PROPERTY       = "property" ;   (* only in class bodies *)
READ           = "read"     ;   (* only in property declarations *)
WRITE          = "write"    ;   (* only in property declarations *)

(* Operators and punctuation *)
ASSIGN        = ":="  ;
EQUALS        = "="   ;
NOT_EQUALS    = "<>"  ;
LESS          = "<"   ;
GREATER       = ">"   ;
LESS_EQ       = "<="  ;
GREATER_EQ    = ">="  ;
COLON         = ":"   ;
PLUS          = "+"   ;
MINUS         = "-"   ;
STAR          = "*"   ;
SLASH         = "/"   ;
LPAREN        = "("   ;
RPAREN        = ")"   ;
COMMA         = ","   ;
SEMICOLON     = ";"   ;
DOT           = "."   ;
CARET         = "^"   ;   (* pointer dereference / pointer type prefix *)

(* Ignored by the lexer *)
WHITESPACE    = { " " | "\t" | "\r" | "\n" } ;
LINE_COMMENT  = "//" { char } newline ;
BLOCK_COMMENT = "{" { char } "}" | "(*" { char } "*)" ;
DIRECTIVE     = "{$" { char } "}" ;   (* compiler directive *)

(* Conditional-compilation directives, handled in the lexer.  Symbols are
 * case-insensitive.  Predefined: BLAISE (always), and the target's CPU/OS
 * symbols (CPUX86_64, CPUAMD64, LINUX, UNIX on Linux/x86-64).  The -d/--define
 * command-line flag and {$DEFINE} add symbols; {$UNDEF} removes them.
 *
 *   {$DEFINE sym}            define a symbol
 *   {$UNDEF sym}             undefine a symbol
 *   {$IFDEF sym} ... {$ENDIF}
 *   {$IFDEF sym} ... {$ELSE} ... {$ENDIF}
 *   {$IFNDEF sym} ... [ {$ELSE} ... ] {$ENDIF}
 *
 * IFDEF keeps its body when the symbol is defined (IFNDEF when not); the
 * optional ELSE branch is taken otherwise.  IFDEF/IFNDEF blocks nest.  There
 * is deliberately no expression form ({$IF ...}) yet — only symbol presence.
 * All other directives (e.g. {$H+}, {$mode ...}) are accepted and ignored. *)


(* ═══════════════════════════════════════════════════════════════════════════
 * COMPILATION UNITS
 * ═══════════════════════════════════════════════════════════════════════════ *)

(* A source file is either a program or a unit. *)

Program
    = PROGRAM IDENT SEMICOLON
      [ UsesClause ]
      Block
      DOT
    ;

Unit
    = UNIT UnitName SEMICOLON
      InterfaceSection
      ImplementationSection
      DOT
    ;

InterfaceSection
    = INTERFACE
      [ UsesClause ]
      { ConstSection | ProcSignature }
    ;

ImplementationSection
    = IMPLEMENTATION
      [ UsesClause ]
      { ConstSection | StandaloneDecl }
    ;

UsesClause
    = USES UnitName { COMMA UnitName } SEMICOLON
    ;

UnitName
    = IDENT { DOT IDENT }
    ;

(* Dotted unit names map to path-separated files: Generics.Collections →
 * Generics/Collections.pas.  The dot is part of the name, not a field
 * access operator; it is valid in the Unit declaration header and in
 * any UnitName position inside a UsesClause. *)


(* ═══════════════════════════════════════════════════════════════════════════
 * BLOCK
 * ═══════════════════════════════════════════════════════════════════════════ *)

Block
    = [ TypeSection ]
      { ConstSection | VarSection | ThreadVarSection | StandaloneDecl }
      BEGIN StmtList END
    ;

(* VarSection and StandaloneDecl may be interleaved with each other. *)


(* ═══════════════════════════════════════════════════════════════════════════
 * TYPE SECTION
 * ═══════════════════════════════════════════════════════════════════════════ *)

TypeSection
    = TYPE TypeDecl { TypeDecl }
    ;

TypeDecl
    = IDENT EQUALS TypeDef SEMICOLON
    | IDENT LESS TypeParamList GREATER EQUALS TypeDef SEMICOLON
    ;

(* The second form declares a generic type template.  TypeDef must be either
 * RecordDef (→ TGenericRecordDef), ClassDef (→ TGenericTypeDef), or
 * InterfaceDef (→ TGenericInterfaceDef).  The parser rejects other TypeDef
 * kinds at that position. *)

TypeDef
    = RecordDef
    | ClassDef
    | GenericRecordDef
    | GenericClassDef
    | InterfaceDef
    | GenericInterfaceDef
    | EnumDef
    | SetType
    | ProceduralType
    | IntSubrange    (* named integer subrange, e.g. TByte = 0..255 *)
    | TypeName       (* covers ArrayType, pointer alias ^T, simple alias, metaclass *)
    ;

IntSubrange
    = [ MINUS ] INT_LIT DOTDOT [ MINUS ] INT_LIT
    ;

(* A named integer subrange is an alias to the narrowest STANDARD integer type
 * that holds both bounds (0..255 -> Byte, -10..10 -> ShortInt, etc.).  Blaise
 * does not range-check, so the subrange carries no run-time bound enforcement;
 * the base-type choice exists only to keep record/array element layout correct.
 * Only integer-literal bounds are accepted as a named type; identifier- or
 * enum-bounded subranges (TLow..THigh, red..blue) are not a named-type form. *)

RecordDef
    = [ PACKED ] RECORD { VisibilitySection | FieldDecl | MethodDecl | ConstSection } END
    ;

(* Records may declare methods alongside fields.  Method bodies are written
 * out-of-line as TRecordName.MethodName, identical to classes.  An ordinary
 * record method receives the receiver by pointer (`Self` is a `var` parameter
 * under the hood); the language gives normal value semantics — assignments
 * copy the record.  A method declared STATIC (see VisibilitySection /
 * MethodDecl) receives no `Self` and is the record's factory / namespaced
 * function form, called as TRecordName.Method(...).  Records may also carry
 * STATIC VAR / STATIC CONST members.
 * No constructors, destructors, or virtual methods are allowed in records.
 *
 * `packed` is permitted only in front of `record`.  It disables natural
 * alignment padding between fields and disables tail padding on the record
 * as a whole, so the record's size equals the cumulative byte size of its
 * fields.  ARC-managed field types (`string`, class, interface, dynamic
 * array) keep their natural 8-byte alignment even inside a packed record
 * because the runtime helpers (`_StringRelease`, `_ClassRelease`, etc.)
 * perform 64-bit loads through the field pointer.  `packed class` and
 * `packed array` are rejected as parse errors. *)

ClassDef
    = CLASS [ LPAREN GenericName { COMMA GenericName } RPAREN ]
             { VisibilitySection | ConstSection | FieldList | MethodDeclList }
             END
    ;

(* A class body is a sequence of members in any order, optionally grouped under
 * visibility / storage sections.  A VisibilitySection keyword sets the current
 * visibility (and, with STATIC, the current member-association) for every
 * member that follows until the next such keyword. *)

VisibilitySection
    = [ STRICT ] ( PRIVATE | PROTECTED ) [ STATIC ]
    | ( PUBLIC | PUBLISHED ) [ STATIC ]
    | STATIC                                   (* STATIC alone keeps current visibility *)
    ;

(* STATIC as a section qualifier (e.g. `private static`, or a bare `static`
 * continuing the current visibility) makes every field, const, method, and
 * property declared until the next section keyword a type-associated (class-
 * level) member: no implicit Self, a single shared storage slot for vars/
 * consts.  STATIC may equally be written as a per-member prefix on a single
 * method or property (see MethodDecl / PropertyDecl).  `private static var`
 * and `private static const` are the section forms for static fields and
 * static constants.  There is no `class var` / `class function` /
 * `class property` — the word `class` is never a member qualifier in Blaise.
 *
 * Visibility keywords (PRIVATE/PROTECTED/PUBLIC/PUBLISHED), STRICT and STATIC
 * are contextual: outside a class/record body they remain usable as
 * identifiers, except those already reserved elsewhere.  STRICT is only a
 * keyword immediately before PRIVATE or PROTECTED at the start of a member
 * section; `strict public`, `strict published` and a bare `strict` are
 * errors.  Visibility is ENFORCED: PRIVATE is visible only within the
 * declaring unit; PROTECTED additionally within descendant types; STRICT
 * PRIVATE only within the declaring type's own methods; STRICT PROTECTED
 * within the declaring type and its descendants; PUBLIC/PUBLISHED everywhere.
 * Visibility composes orthogonally with STATIC. *)

GenericName
    = IDENT [ LESS TypeArgList GREATER ]   (* plain name or generic specialisation *)
    ;

(* The parser stores the first name in the paren list as ParentName.  During
 * semantic analysis, if ParentName contains '<' it is resolved as a generic
 * interface and silently moved to the ImplementsNames list.  Subsequent names
 * are always treated as implements entries.
 * Example: class(TObject, IFoo, IBar<Integer>) *)

GenericRecordDef
    = RECORD                               (* generic template, e.g. record with <T> *)
      { FieldDecl | MethodDecl }
      END
    ;

(* GenericRecordDef is syntactically identical to RecordDef; the distinction
 * is that it appears after "IDENT < TypeParamList > =" in a TypeDecl.  The
 * parser creates a TGenericRecordDef AST node wrapping the TRecordTypeDef
 * body.  Type parameters are substituted at instantiation time (e.g.
 * TMyVal<Integer>).  Records are value types — no inheritance, no vtable,
 * no typeinfo — so monomorphized instances produce only field layout and
 * method bodies, not class metadata. *)

GenericClassDef
    = CLASS LESS TypeParamList GREATER     (* generic template, e.g. class<T> *)
      [ LPAREN IDENT RPAREN ]
      { ConstSection | FieldList }
      MethodDeclList
      END
    ;

(* Interface members are method signatures and properties.  Property
 * accessors must name methods of the interface (or an inherited one) —
 * interfaces have no fields, so field-backed accessors cannot exist.
 * I.Prop reads lower to the getter dispatch; I.Prop := V lowers to the
 * setter dispatch through the itab. *)
InterfaceDef
    = INTERFACE
      [ LPAREN IDENT RPAREN ]             (* optional parent interface *)
      { MethodSignature | PropertyDecl }
      END
    ;

GenericInterfaceDef
    = INTERFACE
      [ LPAREN IDENT RPAREN ]             (* optional parent interface *)
      { MethodSignature | PropertyDecl }
      END
    ;

(* GenericInterfaceDef is syntactically identical to InterfaceDef; the distinction
 * is that it appears after "IDENT < TypeParamList > =" in a TypeDecl.  The parser
 * creates a TGenericInterfaceDef AST node wrapping the TInterfaceTypeDef body.
 * Type parameters are substituted at instantiation time (e.g. IFoo<Integer>). *)

FieldList
    = { FieldDecl }
    ;

FieldDecl
    = IdentList COLON TypeName SEMICOLON
    ;

MethodDeclList
    = { MethodDecl | PropertyDecl }
    ;

MethodDecl
    = [ STATIC ]                          (* per-member static prefix (no implicit Self) *)
      ( PROCEDURE | FUNCTION | CONSTRUCTOR | DESTRUCTOR ) IDENT
      [ LESS TypeParamList GREATER ]      (* optional generic type params *)
      [ LPAREN ParamList RPAREN ]
      [ COLON TypeName ]                  (* return type for functions *)
      SEMICOLON
      { MethodDirective SEMICOLON }
      [ ( Block | AsmBlock ) SEMICOLON ]  (* body absent for forward, external, or abstract decls *)
    ;

(* A STATIC method (whether via the per-member prefix here or an enclosing
 * STATIC VisibilitySection) takes no implicit Self.  CONSTRUCTOR / DESTRUCTOR
 * may NOT be declared STATIC: Blaise has no type-initialiser construct (no
 * `class constructor`); eager one-time setup belongs in the unit
 * initialization / finalization sections.  See language-rationale.adoc. *)

MethodDirective
    = VIRTUAL
    | OVERRIDE
    | ExternalDirective
    | "overload"                          (* required on every overload of a same-named proc/func/method *)
    | "nostackframe"                      (* asm body owns the frame; no compiler prologue/epilogue *)
    | IDENT                               (* inline, stdcall, cdecl, abstract, reintroduce, assembler, … *)
    ;

(* Inline-assembler body.  The lexer captures everything between ASM and the
 * matching END as one opaque token (verbatim GNU/AT&T assembly text); the front
 * end never tokenises the contents.  Used for the runtime's startup/setjmp/
 * atomic routines.  Requires the native backend.  See
 * docs/inline-asm-design.adoc. *)
AsmBlock
    = ASM (* verbatim assembly text *) END
    ;

(* Calling-convention directives (cdecl, stdcall, register, pascal, safecall)
 * are parsed as the IDENT alternative and recorded on the declaration
 * (TMethodDecl.CallingConv, carried into TRoutineSig.CallingConv and the
 * .bif interface metadata).  They are metadata only — the native backend
 * currently emits the System V AMD64 convention regardless; the directive is
 * preserved for FFI/tooling fidelity, not yet honoured at the ABI level. *)

ExternalDirective
    = EXTERNAL [ NAME StrLit ]
    ;

(* Standalone procedure/function/constructor/destructor — top-level or within a block.
 * A leading STATIC marks an out-of-line static-method implementation
 * (`static function TName.Method ...`); it must agree with the in-class
 * declaration's static-ness and takes no implicit Self. *)
StandaloneDecl
    = [ STATIC ]
      ( PROCEDURE | FUNCTION | CONSTRUCTOR | DESTRUCTOR ) IDENT
      [ DOT IDENT ]                                          (* TTypeName.MethodName — non-generic class impl *)
      [ LESS TypeParamList GREATER DOT IDENT ]               (* TName<T>.MethodName  — generic class impl    *)
      [ LESS TypeParamList GREATER ]                         (* function Identity<T> — generic standalone    *)
      [ LPAREN ParamList RPAREN ]
      [ COLON TypeName ]
      SEMICOLON
      { MethodDirective SEMICOLON }
      [ Block SEMICOLON ]                 (* body absent for external declarations *)
    ;

(* Disambiguation of the three IDENT forms above:
 *
 *   IDENT DOT IDENT                       — non-generic class method impl (TFoo.Bar)
 *   IDENT LESS TypeParamList GREATER DOT IDENT  — generic class method impl (TList<T>.Add)
 *   IDENT LESS TypeParamList GREATER            — generic standalone function (Identity<T>)
 *
 * The parser reads the IDENT, then checks LESS IDENT (GT|COMMA) to enter the
 * type-param list.  After consuming the closing GREATER it inspects the next
 * token: DOT → generic class method impl; anything else → generic standalone.
 * For the non-generic DOT form, DOT is checked before any LESS test. *)

ProcSignature
    = ( PROCEDURE | FUNCTION ) IDENT
      [ LPAREN ParamList RPAREN ]
      [ COLON TypeName ]
      SEMICOLON
    ;

MethodSignatureList
    = { ProcSignature }
    ;

PropertyDecl
    = [ STATIC ]                                    (* per-member static prefix *)
      "property" IDENT
      [ LBRACKET IDENT COLON TypeName RBRACKET ]   (* optional index parameter *)
      COLON TypeName
      [ "read"  IDENT ]
      [ "write" IDENT ]
      SEMICOLON
      [ "default" SEMICOLON ]                      (* default array property *)
    ;

(* A STATIC property (via the prefix here or an enclosing STATIC section) is
 * sugar over a STATIC getter/setter: it occupies no slot and dispatches with
 * no Self.  A static property's accessors must themselves be static. *)

(* If the index parameter is present, the property is indexed:
 *   property Items[Index: Integer]: Pointer read Get write Put;
 * Indexed reads emit a getter call with one extra argument (the index).
 * Indexed writes emit a setter call with two extra arguments (index, value).
 * Only a single index parameter is supported; multiple-index properties
 * (as in Delphi) are not.
 *
 * The optional "default" directive (only meaningful on an indexed property)
 * marks it as the class's default array property, enabling the Obj[I] sugar:
 *   property Items[I: Integer]: T read Get write Put; default;
 * Obj[I] then lowers to Obj.Items[I] (getter on read, setter on write). *)

(* "property", "read", "write" are contextual soft keywords; they may be used
 * as identifiers outside a class body. *)

ParamList
    = ParamGroup { SEMICOLON ParamGroup }
    ;

ParamGroup
    = [ VAR | OUT | CONST ] IdentList COLON ParamType [ EQUALS DefaultValue ]
    ;

ParamType
    = ARRAY OF CONST                         (* heterogeneous variadic list *)
    | ARRAY OF TypeName
    | TypeName
    ;

DefaultValue
    = IntLiteral
    | FloatLiteral
    | StrLiteral
    | NIL
    | IDENT                                  (* must resolve to a named constant *)
    ;

(* VAR marks a by-reference parameter; OUT marks a write-only by-reference
 * parameter (passed by reference exactly like VAR, but flagged as OUT for
 * tooling/interface metadata); CONST marks a read-only parameter.
 * ARRAY OF TypeName denotes an open-array parameter using the two-register
 * ABI: a data pointer and a high-index value passed as separate arguments.
 *
 * ARRAY OF CONST is a heterogeneous variadic parameter.  Its element type is
 * the intrinsic record TVarRec; a call-site bracket literal [a, b, ...] is
 * boxed into an array of TVarRec (each a VType discriminant + a pointer-sized
 * VValue) and passed via the same open-array ABI.  The callee reads each
 * element by switching on VType.  See language-rationale.adoc.
 *
 * A DefaultValue is permitted only on a single-name ParamGroup that is not
 * VAR/OUT and not an open-array.  A call site may omit any trailing
 * argument whose corresponding ParamGroup carries a DefaultValue; the
 * compiler fills the missing arguments with a clone of the default
 * expression.  Where two overloads resolve equally, the candidate
 * requiring fewer defaulted slots wins (defaulting penalty in
 * ResolveStandaloneOverload / ResolveMethodOverload). *)

TypeParamList
    = IDENT { COMMA IDENT }
    ;


(* ═══════════════════════════════════════════════════════════════════════════
 * CONSTANT SECTION
 * ═══════════════════════════════════════════════════════════════════════════ *)

ConstSection
    = CONST ConstDecl { ConstDecl }
    ;

ConstDecl
    = IDENT [ COLON ConstTypeAnnotation ] EQUALS ConstRhs SEMICOLON
    ;

ConstTypeAnnotation
    = TypeName                                            (* scalar typed const *)
    | ARRAY LBRACKET TypeName RBRACKET OF TypeName        (* array-of-enum const *)
    | ConstArrayType                                      (* range-indexed array const *)
    ;

(* Range-indexed array const types, single- or multi-dimensional.  The comma
 * form (array[L0..H0, L1..H1, ...]) and the nested form (array[L0..H0] of
 * array[L1..H1] of ...) are equivalent and may be mixed; both desugar to a
 * nested static-array type.  Enum-indexed const arrays remain single-dimension
 * (see the ARRAY LBRACKET TypeName ... alternative above). *)
ConstArrayType
    = ARRAY LBRACKET ConstArrayRange { COMMA ConstArrayRange } RBRACKET
      OF ( ConstArrayType | TypeName )
    ;

ConstArrayRange
    = ArrayBound DOTDOT ArrayBound
    ;

ConstRhs
    = ConstExpr                                           (* scalar value *)
    | ConstArrayValue                                     (* array value list *)
    | LBRACKET [ IDENT { COMMA IDENT } ] RBRACKET         (* set value: [a, b] or [] *)
    ;

(* Array value list.  A multi-dimensional const nests one parenthesised group
 * per dimension; values are stored flattened in row-major order. *)
ConstArrayValue
    = LPAREN ( ConstArrayValue | ConstExpr )
             { COMMA ( ConstArrayValue | ConstExpr ) } RPAREN
    ;

(* A constant value position.  A scalar const may be a string/float literal, a
 * compile-time integer expression, or a compile-time float expression.
 *
 * INTEGER expressions: integer literals, named integer-constant references,
 * binary operators ( + - * div mod and or xor shl shr ), unary minus, and
 * parentheses, with the usual operator precedence.
 *
 * FLOAT expressions: any expression that contains at least one float literal,
 * a named float constant, or uses the '/' operator (which always yields a
 * float result, even with integer operands — matching Delphi/FPC semantics).
 * Supports the arithmetic operators + - * / div, unary minus, named-constant
 * references, and parentheses.
 *
 * Both integer and float expressions are folded to a single value by the
 * semantic pass, which resolves named-constant references. *)
ConstExpr
    = ConstArithExpr
    | StrLit { PLUS StrLit }    (* string constant, with concatenation *)
    | IDENT                    (* named constant reference, e.g. True / False *)
    ;

(* Arithmetic constant expression — covers both integer and float.  The
 * semantic pass inspects the AST: if any leaf is a float literal, a named
 * float constant, or the '/' operator is used, the expression is folded as
 * a float; otherwise it is folded as an integer. *)
ConstArithExpr
    = ConstArithTerm { ( PLUS | MINUS | OR | XOR ) ConstArithTerm }
    ;

ConstArithTerm
    = ConstArithFactor { ( STAR | SLASH | DIV | MOD | AND | SHL | SHR ) ConstArithFactor }
    ;

ConstArithFactor
    = IntLit
    | FloatLit
    | IDENT                        (* named integer or float constant *)
    | MINUS ConstArithFactor       (* unary minus *)
    | LPAREN ConstArithExpr RPAREN
    ;

(* ConstDecl supports untyped, scalar-typed, array-typed, and set-typed forms:
 *   Untyped:      const MaxItems = 100;
 *   Scalar-typed: const Pi: Double = 3.14159;
 *   Array-typed:  const Names: array[TWeather] of string = ('Sunny', 'Cloudy', 'Rainy');
 *                 const Days: array[0..6] of string = ('Sun', 'Mon', ...);
 *   Multi-dim:    const M: array[0..1, 0..2] of Integer = ((1,2,3), (4,5,6));
 *                 const N: array[0..1] of array[0..1] of Integer = ((1,2), (3,4));
 *   Set-typed:    const Primary = [cRed, cBlue];          (* type inferred *)
 *                 const Both: TDirSet = [dNorth, dEast];  (* type annotated *)
 *                 const None: TDirSet = [];               (* empty set *)
 *
 * A set-valued const lists enum-constant members between brackets.  Members
 * must all belong to one enum; the const's type is that enum's set type —
 * either the annotated set type or the inferred 'set of <Enum>'.  The empty
 * set [] requires a set-type annotation (there is no member to infer from).
 * The bitmask is folded at compile time and the const is emitted like any
 * integer set value.
 *
 * Array-typed constants accept either an enum type name or an integer
 * range as the index.  The element count in the value list must exactly
 * match the number of index positions (enum member count or High-Low+1).
 * Array consts are emitted as pre-initialised global data sections.
 *
 * ConstSection may appear in: program/unit Block, InterfaceSection,
 * ImplementationSection, standalone procedure/function/method bodies,
 * and class declaration bodies (class-level constants). *)


(* ═══════════════════════════════════════════════════════════════════════════
 * VARIABLE SECTION
 * ═══════════════════════════════════════════════════════════════════════════ *)

VarSection
    = VAR VarDecl { VarDecl }
    ;

ThreadVarSection
    = THREADVAR VarDecl { VarDecl }
    ;

(* ThreadVarSection declares thread-local storage variables.  Only allowed
 * at program/unit scope (not inside procedures).  Each thread gets its own
 * copy of the variable, zero-initialised on thread creation.  Syntactically
 * identical to VarSection except for the keyword. *)

(* A variable declaration may carry an initialiser ( = ConstValue ).  The
 * initialiser uses the same value grammar as a typed constant (scalar,
 * string, set, or parenthesised aggregate list) and is folded at compile
 * time, then emitted into the data section.  An initialised declaration may
 * declare only a single name (IdentList must be one IDENT). *)
VarDecl
    = IdentList COLON TypeName [ EQUALS ConstValue ] SEMICOLON
    ;


(* ═══════════════════════════════════════════════════════════════════════════
 * SHARED HELPERS
 * ═══════════════════════════════════════════════════════════════════════════ *)

IdentList
    = IDENT { COMMA IDENT }
    ;

TypeName
    = ArrayType                   (* static array: array[L..H] of T *)
    | DynArrayType                (* dynamic array: array of T *)
    | SetType                     (* bit-set over an enum base type *)
    | CARET TypeName              (* pointer-to type: ^Integer, ^T, ^^T  *)
    | QualIdent LESS TypeArgList GREATER   (* generic instantiation: TList<Integer> *)
    | QualIdent                   (* simple type, built-in, or UnitName.TypeName *)
    ;

(* A type may be written with a unit qualifier, e.g. SysUtils.TFormatSettings,
 * and the qualifier may itself be dotted (System.SysUtils.TFormatSettings).
 * The parser reads the full dotted path; a type is always named by its FINAL
 * component, with the leading components naming a unit already brought in by
 * the uses clause.  See language-rationale.adoc "Qualified type names". *)
QualIdent
    = IDENT { DOT IDENT }
    ;

(* A static array may declare multiple dimensions either as a comma-separated
 * index list (ARRAY [L1..H1, L2..H2] OF T) or, equivalently, as nested
 * single-dimension arrays (ARRAY [L1..H1] OF ARRAY [L2..H2] OF T).  The
 * comma form is desugared by the parser into the nested form, so the two
 * are interchangeable.
 *
 * Each bound is a ConstArithExpr — an integer literal, a named integer
 * constant, or a compile-time integer expression (e.g. N-1, 2*K).  The
 * semantic pass resolves the bound text to an integer value. *)
ArrayType
    = ARRAY LBRACKET ArrayBound DOTDOT ArrayBound
            { COMMA ArrayBound DOTDOT ArrayBound } RBRACKET OF TypeName
    | ARRAY LBRACKET IDENT RBRACKET OF TypeName   (* ordinal-indexed *)
    ;

(* The IDENT form names an ORDINAL index type.  Two ordinal kinds are admitted:
 *   - an enumeration type (array[TColor] of T folds to array[0..N-1] of T,
 *     where N is the member count), and
 *   - a NAMED integer subrange (type TIdx = lo..hi;  array[TIdx] of T folds to
 *     array[lo..hi] of T).  The subrange supplies only the index range; the
 *     values it yields remain ordinary unchecked integers of the subrange's
 *     underlying storage type.
 * See language-rationale.adoc "Array index types". *)

ArrayBound
    = ConstArithExpr
    ;

DynArrayType
    = ARRAY OF TypeName
    ;

EnumDef
    = LPAREN EnumMember { COMMA EnumMember } RPAREN
    ;

EnumMember
    = IDENT [ EQUALS [ MINUS ] IntLit ]
    ;

(* When EQUALS is absent, the member's ordinal is the previous member's
 * ordinal plus one (or zero for the first member).  When EQUALS is present,
 * the specified integer (optionally negated by MINUS) becomes the ordinal;
 * subsequent members without EQUALS continue incrementing from that value.
 * This matches Delphi and FPC semantics. *)

(* A set's element type is either an enumeration (named or anonymous inline)
 * or a small ordinal type (Byte, Boolean).  Inline set types are accepted
 * anywhere a TypeName appears (var, parameter, record field, …), not only
 * in a named type declaration — e.g. var S: set of TColour;
 * var T: set of (a, b, c);  var F: set of Byte;
 * The anonymous enum form synthesises an enum type whose members become
 * ordinary enum constants in scope. *)
SetType
    = SET OF ( IDENT | EnumDef | Subrange )
    ;

Subrange
    = ConstExpr DOTDOT ConstExpr
    ;

(* The base type must have at most 256 ordinal values.  Storage scales with
 * the value count:
 *   <= 32 values  : 4 bytes, a QBE 'w' register bitmask
 *   <= 64 values  : 8 bytes, a QBE 'l' register bitmask
 *   <= 256 values : an inline byte-array bitmap of ceil(N/8) bytes (a "jumbo"
 *                   set), operated on via the _Set* RTL helpers like an
 *                   aggregate (record/static array)
 * Value ordinal N maps to bit (N mod 8) of byte (N div 8) in all forms.
 * Types with more than 256 possible values (e.g. Integer, Word) are rejected
 * by the semantic analyser — only Byte (256 values), Boolean (2 values),
 * enumerations with at most 256 members, and integer subranges within
 * 0..255 are accepted.  The subrange form `set of L..H` (e.g. `set of 0..255`,
 * `set of 1..10`) sizes the bitmap to H+1 bits; both bounds may be integer
 * literals, named constants, or constant expressions.  L must be >= 0, H must
 * be <= 255, and the range must be ascending (H >= L) — a descending or
 * out-of-range subrange is a semantic error.  Set literals with ordinal base
 * types accept integer literals and ranges: [1, 2, 10..20]. *)

(* Procedural type — bare or method pointer.
 *
 * Without 'of object', the type is a bare function/procedure pointer
 * stored as a single 8-byte code pointer (QBE 'l').  Sufficient for
 * callback APIs such as the punit test framework's TTestRun and
 * TTestSetup hooks.
 *
 * With 'of object', the type is a method pointer carrying both a code
 * pointer and a data pointer (Self).  Storage is a 16-byte block:
 * Code at offset 0, Data at offset 8.  This layout is byte-for-byte
 * identical to the intrinsic TMethod record, so the cast TMyMethod(m)
 * (m: TMethod) is a no-op at the QBE level; the call site loads both
 * halves and emits 'call code(l data, args...)' — Data is passed as
 * the implicit first argument so the callee sees it as Self.
 *
 * Used by xUnit-style frameworks (fpcunit, fptest) for test-method
 * dispatch:
 *     type
 *       TRunMethod = procedure of object;
 *     m.Code := MethodAddress(Self, FName);
 *     m.Data := Self;
 *     RunMethod := TRunMethod(m);
 *     RunMethod;                       (* invokes Self.<FName>      *)
 *
 * 'reference to procedure' (anonymous method / closure) is NOT
 * supported — closures need an environment record, which adds a
 * lifetime story we have deliberately deferred.
 *)
ProceduralType
    = ( FUNCTION  [ LPAREN ParamList RPAREN ] COLON TypeName [ OF "object" ] )
    | ( PROCEDURE [ LPAREN ParamList RPAREN ]                 [ OF "object" ] )
    ;

(* Set literals use the same bracket notation as array literals.
   The semantic pass distinguishes them by assignment-target type context:
   when the LHS is a set type, LBRACKET ExprList RBRACKET is a set literal;
   otherwise it is an array literal.  An empty LBRACKET RBRACKET is valid
   only as a set literal (the empty set).

   A set element may be a single value or a range `lo .. hi` (issue #105).
   Both range bounds must be compile-time constants of the set's base type;
   the semantic pass expands the range into its individual members and
   rejects a reversed range (low ordinal > high ordinal).  The range form is
   meaningful only in set context — in array-literal context DOTDOT is a
   syntax error. *)
SetLiteral
    = LBRACKET [ SetElement { COMMA SetElement } ] RBRACKET
    ;

SetElement
    = Expr [ DOTDOT Expr ]
    ;

(* Each type argument is itself a (possibly-generic) type name, so type
 * arguments nest: TList<TList<Integer>>, TBox<TPair<Integer, string>>. *)
TypeArgList
    = GenericName { COMMA GenericName }
    ;


(* ═══════════════════════════════════════════════════════════════════════════
 * STATEMENTS
 * ═══════════════════════════════════════════════════════════════════════════ *)

StmtList
    = { Stmt SEMICOLON } [ Stmt ]
    ;

Stmt
    = IfStmt
    | WhileStmt
    | ForStmt
    | TryFinallyStmt
    | TryExceptStmt
    | RaiseStmt
    | ExitStmt
    | CompoundStmt
    | PointerWriteStmt
    | StaticSubscriptAssign
    | SubscriptFieldAssign
    | SubscriptMethodCall
    | FieldAssignment
    | MethodCall
    | Assignment
    | ProcCall
    | (* empty *)
    ;

(* A multi-dimensional element write addresses nested static arrays.  Both the
 * comma form a[i, j] := v and the chained form a[i][j] := v are accepted and
 * lower to the same element write through the inner-array address.
 *
 * The same single-subscript form also covers an indexed write into a string
 * (S[I] := <byte>), a PChar (P[I] := <byte>), a dynamic array, and a default
 * indexed property.  The base kind is resolved by the semantic pass, not the
 * grammar.  For a string the write is copy-on-write (the buffer is made
 * uniquely owned before the byte store) — see the "String Subscript S[N]"
 * section of language-rationale.adoc. *)
StaticSubscriptAssign
    = IDENT LBRACKET Expr { COMMA Expr } RBRACKET
            { LBRACKET Expr { COMMA Expr } RBRACKET } ASSIGN Expr
    ;

(* A statement starting with IDENT LBRACKET Expr RBRACKET that is NOT
 * directly followed by ASSIGN continues as a postfix chain on the element:
 * field accesses and further subscripts, terminated by either a field
 * assignment (SubscriptFieldAssign) or a method call on the element
 * (SubscriptMethodCall).  Examples:
 *   a[0].Name := 'x';        a[i].Inner.N := 5;
 *   a[0].Bump();             a[i].Items.Add(x);
 *   a[i].Arr[j] := v;        — element write into an array-typed field
 * A terminating subscript directly over another subscript (a[i][j] := v, or
 * the comma form a[i, j] := v) is a multi-dimensional element write — see
 * StaticSubscriptAssign. *)
SubscriptFieldAssign
    = IDENT LBRACKET Expr RBRACKET { SubscriptSuffix }
      DOT IDENT [ LBRACKET Expr RBRACKET ] ASSIGN Expr
    ;

SubscriptMethodCall
    = IDENT LBRACKET Expr RBRACKET { SubscriptSuffix }
      DOT IDENT LPAREN [ ArgList ] RPAREN
    ;

(* A subscript suffix indexes one or more dimensions.  The comma form
 * [i, j] is desugared into chained single-index subscripts [i][j], so a
 * multi-dimensional read a[i, j] and a[i][j] are equivalent. *)
SubscriptSuffix
    = DOT IDENT
    | LBRACKET Expr { COMMA Expr } RBRACKET
    ;

IfStmt
    = IF Expr THEN Stmt [ ELSE Stmt ]
    ;

WhileStmt
    = WHILE Expr DO Stmt
    ;

ForStmt
    = FOR IDENT ASSIGN Expr ( TO | DOWNTO ) Expr DO Stmt
    | FOR IDENT IN Expr DO Stmt
    ;

(* FOR IDENT IN Expr is an enhanced for loop.  The element type depends on
 * the collection expression:
 *   - Static array (array[L..H] of T):  IDENT must be compatible with T.
 *   - Class with GetEnumerator protocol: IDENT must be compatible with
 *     the Current property type of the enumerator.
 *   - String: IDENT type selects iteration mode:
 *       * Byte — iterates raw UTF-8 bytes (one byte per iteration).
 *       * Integer — iterates Unicode codepoints (one codepoint per
 *         iteration, advancing 1–4 bytes via _Utf8DecodeAt).
 *       * Any other type — compile-time error.
 *     There is no Char type in Blaise.
 *   - Set (set of TEnum): IDENT must be an ordinal type; each iteration
 *     yields one enum member whose bit is set, in ascending ordinal order.
 *     The set expression is evaluated once before iteration begins. *)

TryFinallyStmt
    = TRY StmtList FINALLY StmtList END
    ;

TryExceptStmt
    = TRY StmtList EXCEPT ExceptBody END
    ;

ExceptBody
    = OnClause { ";" OnClause } [ ";" ] [ ELSE StmtList ]
    | StmtList
    ;

OnClause
    = "on" [ IDENT ":" ] TypeRef DO Stmt
    ;

RaiseStmt
    = RAISE [ Expr ]
    ;

(* Exit returns from the current routine.  The optional Exit(X) form is the
 * function-result shorthand: it assigns X to Result, then returns.  X must be
 * assignment-compatible with the return type; Exit(X) is only valid inside a
 * function (a procedure has no Result). *)
ExitStmt
    = EXIT [ LPAREN Expr RPAREN ]
    ;

CompoundStmt
    = BEGIN StmtList END
    ;

(* Pointer write: P^ := Expr — store through pointer *)
PointerWriteStmt
    = IDENT CARET ASSIGN Expr
    ;

FieldAssignment
    = IDENT DOT IDENT { Selector } ASSIGN Expr
    | LPAREN Expr RPAREN DOT IDENT { Selector } ASSIGN Expr  (* parenthesised lvalue, e.g. (a as TB).FX := v *)
    ;

(* A Selector continues an l-value path after the first member: a further
 * .Field, an [Index] subscript (indexed property or array-typed field
 * element), or a .Method(...) call whose result is itself selected into.
 * This covers chains such as  Self.FStack[0].Count := value  and
 * r.A[0] := 10 — the subscript-then-field chain the parser added in
 * baf5079.  See language-rationale.adoc "Chained l-value assignment". *)
Selector
    = DOT IDENT
    | LBRACKET Expr RBRACKET
    | DOT IDENT LPAREN [ ArgList ] RPAREN
    ;

MethodCall
    = IDENT DOT IDENT
      [ LESS TypeArgList GREATER ]            (* explicit args for a generic method *)
      LPAREN [ ArgList ] RPAREN
    ;

(* Generic method call: Obj.Method<Integer>(args).  The optional
 * LESS TypeArgList GREATER supplies explicit type arguments for a method that
 * declares its own type parameters (MethodDecl's [ LESS TypeParamList GREATER ]).
 * The parser folds the type args into the method name (Method<Integer>) and the
 * semantic pass monomorphises the method on first use.  The leading-'<' is
 * disambiguated from a comparison by the same two-token lookahead used for
 * generic free-function calls.  Both the inline-body method form and the
 * out-of-line impl  function TOwner.Method<T>(...)  are supported. *)

(* Semantic disambiguation of  IDENT DOT IDENT  in Stmt:
 *   - Followed by ASSIGN or LBRACKET ASSIGN → FieldAssignment (plain or indexed)
 *   - Otherwise                              → MethodCall
 *
 * The LBRACKET Expr RBRACKET ASSIGN form covers two cases, disambiguated by
 * the semantic pass: when the member is an indexed property the subscript is
 * the property index (setter call); when the member is an ARRAY-TYPED FIELD
 * (dynamic or static) the subscript selects the element to assign:
 *   r.A[0] := 10;    c.Buf[i] := b;    Self.A[1] := x;
 * The same applies after a chain (c.N.A[0] := 7 — see MethodCall chains) and
 * to a bare implicit-Self field inside a method (A[0] := v parses as
 * StaticSubscriptAssign and resolves to the Self field).
 *
 * When IDENT DOT IDENT is followed by LBRACKET in a READ position, the parser
 * checks whether the member is an indexed property (symbol table lookup).  If
 * it is, LBRACKET Expr RBRACKET is consumed as the index; the result is an
 * indexed property access. *)

Assignment
    = IDENT ASSIGN Expr
    ;

ProcCall
    = IDENT LPAREN [ ArgList ] RPAREN
    ;

ArgList
    = Expr { COMMA Expr }
    ;


(* ═══════════════════════════════════════════════════════════════════════════
 * EXPRESSIONS
 * ═══════════════════════════════════════════════════════════════════════════ *)

Expr
    = Comparison [ ( IS | AS ) IDENT ]
    ;

(* IS and AS have the lowest precedence.  IDENT is the target type name. *)

Comparison
    = Additive [ ( EQUALS | NOT_EQUALS | LESS | GREATER | LESS_EQ | GREATER_EQ ) Additive ]
    ;

Additive
    = Term { ( PLUS | MINUS ) Term }
    ;

Term
    = Factor { ( STAR | SLASH | DIV | MOD | AND | XOR | SHL | SHR | SAR ) Factor }
    ;

Factor
    = INT_LIT
    | STRING_LIT
    | TEXT_BLOCK_LIT
    | NIL
    | ArrayLiteral
    | AddrOf
    | NOT Factor                                       (* Boolean or bitwise NOT *)
    | INHERITED IDENT LPAREN [ ArgList ] RPAREN        (* inherited function call in expr position; parens mandatory *)
    | "Supports" LPAREN Expr COMMA IDENT [ COMMA IDENT ] RPAREN  (* compiler intrinsic *)
    | IDENT DOT IDENT [ LBRACKET Expr RBRACKET ]      (* indexed property read *)
               LPAREN [ ArgList ] RPAREN               (* constructor/method call *)
    | IDENT DOT IDENT                                  (* field read *)
    | IDENT LESS TypeArgList GREATER LPAREN [ ArgList ] RPAREN   (* generic func call *)
    | IDENT LPAREN [ ArgList ] RPAREN                  (* func call, type cast *)
    | IDENT                                            (* variable read *)
    | LPAREN Expr RPAREN
    ;

AddrOf
    = AT Factor
    ;

ArrayLiteral
    = LBRACKET Expr { COMMA Expr } RBRACKET
    ;

(* ArrayLiteral is a stack-allocated temporary array passed to open-array formals.
 * The element type is inferred from the first element; all elements must match.
 * Empty literals [] are a semantic error (element type cannot be inferred).
 * The postfix subscript Expr[N] is not available on an ArrayLiteral directly. *)

(* Pointer dereference P^ is a postfix on any Factor result:
 *
 *   Deref
 *       = Factor CARET
 *       ;
 *
 * For simplicity this is shown as a postfix suffix on Factor above rather
 * than a separate rule; the parser handles it by wrapping the parsed Factor
 * in a TDerefExpr node when CARET follows.
 *
 * Type cast: TypeName LPAREN Expr RPAREN — parsed as an IDENT followed by
 * LPAREN, identical to a function call.  The semantic pass distinguishes casts
 * from calls by checking whether the symbol resolves to a type or a function.
 *)


(* ═══════════════════════════════════════════════════════════════════════════
 * BUILT-IN PROCEDURES, FUNCTIONS, AND TYPES
 * ═══════════════════════════════════════════════════════════════════════════ *)

(*
 * Built-in procedures (recognised by the code generator, not the parser):
 *
 *   WriteLn  ( [ Expr ] )   — print with trailing newline; calls libc printf
 *   Write    ( [ Expr ] )   — print without newline
 *   FreeMem  ( Pointer )    — calls libc free()
 *
 * Built-in functions (recognised by the code generator):
 *
 *   Supports  ( Obj, IntfType ) : Boolean
 *             — returns True if Obj's class implements IntfType.
 *               Obj must be a class or interface reference;
 *               IntfType is a type name (not a runtime value).
 *   Supports  ( Obj, IntfType, out OutVar ) : Boolean
 *             — same as above; on success also assigns Obj and its
 *               itab for IntfType to OutVar's fat-pointer slots (ARC).
 *               OutVar must be a variable of an interface type.
 *
 *   GetMem    ( Integer ) : Pointer    — calls libc malloc()
 *   ReallocMem( Pointer, Integer ) : Pointer  — calls libc realloc()
 *   MethodAddress( Obj, Name )  : Pointer  — published-method lookup
 *   ClassCreate  ( Cls, ...args ) : Cls.BaseClass
 *                              — runtime construction from a metaclass
 *                                value; lowers to _ClassCreate(Cls)
 *                                followed by a vtable-indirect call to
 *                                the constructor on Cls.BaseClass.
 *   Low  ( Ordinal | Array | String ) : T  — for ordinal type/expression
 *                                             returns lower bound of T;
 *                                             0 for open/dynamic arrays and
 *                                             strings; LowBound for static
 *                                             arrays.  Float types: error.
 *   High ( Ordinal | Array | String ) : T  — for ordinal type/expression
 *                                             returns upper bound of T;
 *                                             HighBound for static arrays;
 *                                             high-index slot for open arrays;
 *                                             Length(S)-1 for strings (runtime).
 *                                             Float types: error.
 *
 * Built-in types (pre-populated in the symbol table):
 *
 *   Integer   — 32-bit signed integer   (QBE type: w)
 *   Int64     — 64-bit signed integer   (QBE type: l)
 *   UInt32    — 32-bit unsigned integer (QBE type: w)
 *               Alias: Cardinal
 *   UInt64    — 64-bit unsigned integer (QBE type: l)
 *               Aliases: QWord, PtrUInt (the pointer-sized unsigned)
 *   SmallInt  — 16-bit signed integer   (QBE type: w, stored as h)
 *               Alias: Int16
 *   Word      — 16-bit unsigned integer (QBE type: w, stored as h)
 *               Alias: UInt16
 *   Boolean   — 1-byte boolean          (QBE type: w; 0 = false, 1 = true)
 *   Byte      — 8-bit unsigned integer  (QBE type: w, stored as b)
 *   String    — ARC-managed UTF-8 string pointer (QBE type: l)
 *   Pointer   — untyped pointer         (QBE type: l)
 *   ^T        — typed pointer to T; created on demand (QBE type: l)
 *
 * Integer literal typing rules (untyped constant promotion):
 *
 *   - Decimal/hex/oct/bin literal whose value fits in [MinInt32..MaxInt32]
 *     resolves to Integer.
 *   - Literal in [MinInt64..MaxInt64] but outside Int32 range resolves
 *     to Int64.
 *   - Literal in (MaxInt64..MaxUInt64] resolves to UInt64.  The bit
 *     pattern is preserved; signedness is selected by the type.
 *   - Mixing Int64 and UInt64 in an arithmetic or comparison expression
 *     requires an explicit cast (UInt64(x) / Int64(x)).  All other
 *     integer types widen implicitly into both.
 *)


(* ═══════════════════════════════════════════════════════════════════════════
 * SEMANTIC DISAMBIGUATION NOTES
 * ═══════════════════════════════════════════════════════════════════════════ *)

(*
 * IDENT LPAREN ArgList RPAREN in Factor:
 *   - Symbol resolves to a function/procedure → function call
 *   - Symbol resolves to a type → type cast (e.g. Integer(x), Pointer(p))
 *
 * IDENT DOT IDENT in Factor:
 *   - Left IDENT resolves to a type symbol → constructor call (TypeName.Create)
 *   - Left IDENT resolves to a metaclass variable and right IDENT is a
 *     constructor name → metaclass constructor dispatch (_ClassCreate +
 *     vtable-indirect call to the most-derived Create body)
 *   - Left IDENT resolves to a class variable → class field read
 *   - Left IDENT resolves to a record variable → record field read
 *
 * IDENT DOT IDENT in Stmt:
 *   - Followed by ASSIGN → FieldAssignment (record/class field write)
 *   - Otherwise → MethodCall
 *
 * Generic instantiation vs. comparison:
 *   IDENT LESS ... is disambiguated by two-token lookahead.  LESS is treated
 *   as opening a type-arg list only when both: (a) the token after LESS is an
 *   IDENT, and (b) the token after that is GREATER or COMMA.  Otherwise LESS
 *   is a comparison operator (e.g. "if A < B then").
 *
 * Diamond operator (type-argument inference):
 *   IDENT NOTEQUALS DOT ... is parsed as IDENT '<>' DOT — the lexer folds '<>'
 *   into a single tkNotEquals token.  When '<>' appears as generic type args in
 *   an assignment RHS constructor call (TFoo<>.Create), the semantic pass infers
 *   all type arguments from the declared type of the LHS variable.
 *   Only valid in assignment context; the LHS must be a concrete generic
 *   instantiation whose base name matches the constructor type name.
 *   Examples:
 *     var S: TStack<string>;   S := TStack<>.Create;
 *     var D: TDictionary<string,Integer>;  D := TDictionary<>.Create;
 *
 * Virtual dispatch vs. static call:
 *   Determined at semantic time from the VTableSlot field on the resolved
 *   method declaration.  Grammar is identical for both cases.
 *
 * Interface method call:
 *   IDENT DOT IDENT where the left IDENT resolves to an interface variable.
 *   Generates an indirect call through the itab pointer at the appropriate slot.
 *)


(* ═══════════════════════════════════════════════════════════════════════════
 * ARC IMPLICIT OPERATIONS
 * ═══════════════════════════════════════════════════════════════════════════ *)

(*
 * The compiler inserts reference-counting calls automatically.
 * These are not visible in source syntax but are part of the language semantics.
 *
 * String assignment:
 *   _StringAddRef  ( new_value )
 *   _StringRelease ( old_value )
 *   store new_value → target
 *
 * Block exit — for every String variable in scope:
 *   _StringRelease ( variable )
 *
 * Exception path (try/finally, try/except):
 *   _StringRelease called and slot zeroed on exception unwind.
 *
 * String literal layout: { w -1, w len, w cap, b "data", b 0 }
 *   refcount = -1 → immortal (never released); char data starts at ptr+12.
 *)
