Three compiler additions, exercised end-to-end by testpunit2.pas: * Default parameter values — single-name, non-var, non-open-array params may carry '= literal-or-named-constant'. Overload resolution accepts any arity in [MinArity, ParamCount]; the resolver tie-breaks toward the candidate that needs fewer defaulted slots. AnalyseProcCall and AnalyseFuncCallExpr clone the default expression into the call's Args list. Defaults declared on a unit-interface forward decl are ownership-transferred to the matching impl param at reconciliation. * Metaclass references — a bare class type identifier in a value position (e.g. 'EError' as an argument or in 'Pointer(EError)') is now retyped to Pointer with codegen emitting '$typeinfo_<Name>' as an immediate. Matches the value held by vtable[0], so 'A.ClassType = EError' is true exactly when A is an instance of that exact class. * Numeric widening at call sites — the existing CoerceArg helper now covers Integer→Double (swtof/sltof), Single→Double (exts) and Integer→Single, in addition to the pre-existing w→l. Inherited-, constructor-, method-, and function-call sites all route through it. punit.pas gains DefaultDoubleDelta, two Double AssertEquals overloads, and '= ''' defaults on the four-arity AddTest declarations. Docs: grammar.ebnf extends ParamGroup with the optional default; a new DefaultValue rule lists the permitted forms. language-rationale.adoc adds 'Default Parameter Values' and 'Metaclass References' sections recording the decision, alternatives rejected, and the overload tie-break formula. All 1194 existing compiler tests pass; testpunit2 compiles, assembles, links and runs (31 tests; the expected pass/fail/inactive pattern).
703 lines
25 KiB
EBNF
703 lines
25 KiB
EBNF
(* 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 *)
|
|
INT_LIT = digit { digit } ;
|
|
STRING_LIT = "'" { char | "''" } "'" ;
|
|
|
|
(* 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" ;
|
|
CLASS = "class" ;
|
|
VAR = "var" ;
|
|
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" ;
|
|
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 directives — skipped *)
|
|
|
|
|
|
(* ═══════════════════════════════════════════════════════════════════════════
|
|
* COMPILATION UNITS
|
|
* ═══════════════════════════════════════════════════════════════════════════ *)
|
|
|
|
(* A source file is either a program or a unit. *)
|
|
|
|
Program
|
|
= PROGRAM IDENT SEMICOLON
|
|
[ UsesClause ]
|
|
Block
|
|
DOT
|
|
;
|
|
|
|
Unit
|
|
= UNIT IDENT 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 only valid in the UsesClause position. *)
|
|
|
|
|
|
(* ═══════════════════════════════════════════════════════════════════════════
|
|
* BLOCK
|
|
* ═══════════════════════════════════════════════════════════════════════════ *)
|
|
|
|
Block
|
|
= [ TypeSection ]
|
|
{ ConstSection | VarSection | 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
|
|
* ClassDef (→ TGenericTypeDef) or InterfaceDef (→ TGenericInterfaceDef).
|
|
* The parser rejects other TypeDef kinds at that position. *)
|
|
|
|
TypeDef
|
|
= RecordDef
|
|
| ClassDef
|
|
| GenericClassDef
|
|
| InterfaceDef
|
|
| GenericInterfaceDef
|
|
| EnumDef
|
|
| SetType
|
|
| ProceduralType
|
|
;
|
|
|
|
RecordDef
|
|
= RECORD FieldList END
|
|
;
|
|
|
|
ClassDef
|
|
= CLASS [ LPAREN GenericName { COMMA GenericName } RPAREN ]
|
|
{ ConstSection | FieldList }
|
|
MethodDeclList
|
|
END
|
|
;
|
|
|
|
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>) *)
|
|
|
|
GenericClassDef
|
|
= CLASS LESS TypeParamList GREATER (* generic template, e.g. class<T> *)
|
|
[ LPAREN IDENT RPAREN ]
|
|
{ ConstSection | FieldList }
|
|
MethodDeclList
|
|
END
|
|
;
|
|
|
|
InterfaceDef
|
|
= INTERFACE
|
|
[ LPAREN IDENT RPAREN ] (* optional parent interface *)
|
|
MethodSignatureList
|
|
END
|
|
;
|
|
|
|
GenericInterfaceDef
|
|
= INTERFACE
|
|
[ LPAREN IDENT RPAREN ] (* optional parent interface *)
|
|
MethodSignatureList
|
|
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
|
|
= ( 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 SEMICOLON ] (* body absent for forward, external, or abstract decls *)
|
|
;
|
|
|
|
MethodDirective
|
|
= VIRTUAL
|
|
| OVERRIDE
|
|
| ExternalDirective
|
|
| "overload" (* required on every overload of a same-named proc/func/method *)
|
|
| IDENT (* inline, stdcall, cdecl, abstract, reintroduce, … *)
|
|
;
|
|
|
|
ExternalDirective
|
|
= EXTERNAL [ NAME StrLit ]
|
|
;
|
|
|
|
(* Standalone procedure/function/constructor/destructor — top-level or within a block. *)
|
|
StandaloneDecl
|
|
= ( 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
|
|
= "property" IDENT
|
|
[ LBRACKET IDENT COLON TypeName RBRACKET ] (* optional index parameter *)
|
|
COLON TypeName
|
|
[ "read" IDENT ]
|
|
[ "write" IDENT ]
|
|
SEMICOLON
|
|
;
|
|
|
|
(* 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. *)
|
|
|
|
(* "property", "read", "write" are contextual soft keywords; they may be used
|
|
* as identifiers outside a class body. *)
|
|
|
|
ParamList
|
|
= ParamGroup { SEMICOLON ParamGroup }
|
|
;
|
|
|
|
ParamGroup
|
|
= [ VAR | CONST ] IdentList COLON ParamType [ EQUALS DefaultValue ]
|
|
;
|
|
|
|
ParamType
|
|
= ARRAY OF TypeName
|
|
| TypeName
|
|
;
|
|
|
|
DefaultValue
|
|
= IntLiteral
|
|
| FloatLiteral
|
|
| StrLiteral
|
|
| NIL
|
|
| IDENT (* must resolve to a named constant *)
|
|
;
|
|
|
|
(* VAR marks a by-reference parameter; 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.
|
|
*
|
|
* 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 EQUALS ConstExpr SEMICOLON
|
|
;
|
|
|
|
ConstExpr
|
|
= [ MINUS ] IntLit
|
|
| StrLit
|
|
;
|
|
|
|
(* ConstExpr covers the constant initialisers currently supported:
|
|
* - integer literals, optionally negated
|
|
* - string literals
|
|
* 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 }
|
|
;
|
|
|
|
VarDecl
|
|
= IdentList COLON TypeName SEMICOLON
|
|
;
|
|
|
|
|
|
(* ═══════════════════════════════════════════════════════════════════════════
|
|
* SHARED HELPERS
|
|
* ═══════════════════════════════════════════════════════════════════════════ *)
|
|
|
|
IdentList
|
|
= IDENT { COMMA IDENT }
|
|
;
|
|
|
|
TypeName
|
|
= ArrayType (* static array: array[L..H] of T *)
|
|
| SetType (* bit-set over an enum base type *)
|
|
| CARET TypeName (* pointer-to type: ^Integer, ^T, ^^T *)
|
|
| IDENT LESS TypeArgList GREATER (* generic instantiation: TList<Integer> *)
|
|
| IDENT (* simple type or built-in *)
|
|
;
|
|
|
|
ArrayType
|
|
= ARRAY LBRACKET IntLit DOTDOT IntLit RBRACKET OF TypeName
|
|
;
|
|
|
|
SetType
|
|
= SET OF IDENT
|
|
;
|
|
|
|
(* Bare procedural type — function or procedure pointer. Stored as a
|
|
* single 8-byte code pointer (QBE 'l'). Not 'of object' (method pointer)
|
|
* and not 'reference to' (anonymous method / closure); both deferred
|
|
* until a use case requires them. Bare-function pointers are sufficient
|
|
* for callback APIs such as the punit test framework's TTestRun /
|
|
* TTestSetup hooks. Example:
|
|
* type
|
|
* TIntFn = function: Integer;
|
|
* TStrFn = function(const S: string): Integer;
|
|
* TLogProc = procedure(Level: Integer; const Msg: string);
|
|
*)
|
|
ProceduralType
|
|
= ( FUNCTION [ LPAREN ParamList RPAREN ] COLON TypeName )
|
|
| ( PROCEDURE [ LPAREN ParamList RPAREN ] )
|
|
;
|
|
|
|
(* 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). *)
|
|
SetLiteral
|
|
= LBRACKET [ Expr { COMMA Expr } ] RBRACKET
|
|
;
|
|
|
|
TypeArgList
|
|
= IDENT { COMMA IDENT }
|
|
;
|
|
|
|
|
|
(* ═══════════════════════════════════════════════════════════════════════════
|
|
* STATEMENTS
|
|
* ═══════════════════════════════════════════════════════════════════════════ *)
|
|
|
|
StmtList
|
|
= { Stmt SEMICOLON } [ Stmt ]
|
|
;
|
|
|
|
Stmt
|
|
= IfStmt
|
|
| WhileStmt
|
|
| ForStmt
|
|
| TryFinallyStmt
|
|
| TryExceptStmt
|
|
| RaiseStmt
|
|
| CompoundStmt
|
|
| PointerWriteStmt
|
|
| StaticSubscriptAssign
|
|
| FieldAssignment
|
|
| MethodCall
|
|
| Assignment
|
|
| ProcCall
|
|
| (* empty *)
|
|
;
|
|
|
|
StaticSubscriptAssign
|
|
= IDENT LBRACKET Expr RBRACKET ASSIGN Expr
|
|
;
|
|
|
|
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
|
|
;
|
|
|
|
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 ]
|
|
;
|
|
|
|
CompoundStmt
|
|
= BEGIN StmtList END
|
|
;
|
|
|
|
(* Pointer write: P^ := Expr — store through pointer *)
|
|
PointerWriteStmt
|
|
= IDENT CARET ASSIGN Expr
|
|
;
|
|
|
|
FieldAssignment
|
|
= IDENT DOT IDENT [ LBRACKET Expr RBRACKET ] ASSIGN Expr
|
|
;
|
|
|
|
MethodCall
|
|
= IDENT DOT IDENT [ LPAREN [ ArgList ] RPAREN ]
|
|
;
|
|
|
|
(* Semantic disambiguation of IDENT DOT IDENT in Stmt:
|
|
* - Followed by ASSIGN or LBRACKET ASSIGN → FieldAssignment (plain or indexed)
|
|
* - Otherwise → MethodCall
|
|
*
|
|
* When IDENT DOT IDENT is followed by LBRACKET, 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.
|
|
* A following ASSIGN makes it an IndexedPropWrite; absence of ASSIGN is a read. *)
|
|
|
|
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 ) Factor }
|
|
;
|
|
|
|
Factor
|
|
= INT_LIT
|
|
| STRING_LIT
|
|
| NIL
|
|
| ArrayLiteral
|
|
| AddrOf
|
|
| IDENT DOT IDENT [ LBRACKET Expr RBRACKET ] (* indexed property read *)
|
|
[ LPAREN [ ArgList ] RPAREN ] (* field/constructor/method *)
|
|
| IDENT LESS TypeArgList GREATER LPAREN [ ArgList ] RPAREN (* generic func call *)
|
|
| IDENT [ LPAREN [ ArgList ] RPAREN ] (* variable, func call, type cast *)
|
|
| 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):
|
|
*
|
|
* GetMem ( Integer ) : Pointer — calls libc malloc()
|
|
* ReallocMem( Pointer, Integer ) : Pointer — calls libc realloc()
|
|
*
|
|
* 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)
|
|
* Boolean — 32-bit boolean (QBE type: w; 0 = false, 1 = true)
|
|
* Byte — 8-bit unsigned integer (QBE type: w)
|
|
* 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)
|
|
*)
|
|
|
|
|
|
(* ═══════════════════════════════════════════════════════════════════════════
|
|
* 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 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").
|
|
*
|
|
* 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.
|
|
*)
|