2026-04-22 21:00:46 +03:00
|
|
|
|
{
|
|
|
|
|
|
Blaise - An Object Pascal Compiler
|
|
|
|
|
|
Copyright (c) 2026 Graeme Geldenhuys
|
2026-05-03 21:45:29 +03:00
|
|
|
|
SPDX-License-Identifier: Apache-2.0 WITH Swift-exception
|
|
|
|
|
|
Licensed under the Apache License v2.0 with Runtime Library Exception.
|
2026-04-22 21:00:46 +03:00
|
|
|
|
See LICENSE file in the project root for full license terms.
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-20 19:52:09 +03:00
|
|
|
|
unit uSymbolTable;
|
|
|
|
|
|
|
|
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
|
|
|
|
|
|
|
|
interface
|
|
|
|
|
|
|
|
|
|
|
|
uses
|
|
|
|
|
|
Classes, SysUtils, contnrs;
|
|
|
|
|
|
|
|
|
|
|
|
type
|
|
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
{ Type descriptors }
|
|
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
|
|
|
|
|
|
TTypeKind = (
|
|
|
|
|
|
tyInteger, { Int32 — QBE 'w' }
|
|
|
|
|
|
tyInt64, { Int64 — QBE 'l' }
|
|
|
|
|
|
tyUInt32, { UInt32 — QBE 'w' unsigned }
|
|
|
|
|
|
tyByte, { Byte — QBE 'b' }
|
|
|
|
|
|
tyBoolean, { Boolean — stored as byte, 0/1 only }
|
|
|
|
|
|
tyString, { ARC-managed UTF-8 string }
|
|
|
|
|
|
tyRecord, { Stack-allocated aggregate (Phase 2) }
|
|
|
|
|
|
tyClass, { Heap-allocated, single-inheritance (Phase 2) }
|
2026-04-21 20:24:55 +03:00
|
|
|
|
tyInterface, { Zero-GUID interface reference (Phase 3) }
|
2026-04-21 00:41:28 +03:00
|
|
|
|
tyVoid, { No value — used as procedure return type }
|
Add pointer type infrastructure: ^T types, dereference, GetMem/FreeMem/ReallocMem
- uSymbolTable: tyPointer kind, TPointerTypeDesc(BaseType), GetMem/FreeMem/ReallocMem builtins
- uLexer/uPasTokeniser: tkCaret token for '^'; suppress ^X string escape (not in Clean Pascal)
- uParser: ParseTypeName handles '^TypeName' prefix (uses Self. for explicit recursion);
TDerefExpr for P^ postfix; TPointerWriteStmt for P^ := V lhs
- uAST: TDerefExpr, TPointerWriteStmt nodes
- uSemantic: FindTypeOrInstantiate creates TPointerTypeDesc on demand for '^T';
AnalyseDerefExpr, AnalysePointerWriteStmt; GetMem/ReallocMem/type-cast handling
- uCodeGenQBE: tyPointer→'l', EmitPointerWrite (storew/storel), TDerefExpr (loadw/loadl),
GetMem→malloc, FreeMem→free, ReallocMem→realloc, pointer arithmetic (extsw + add/sub),
type-cast via copy
- 15 new tests (556 total), all passing
2026-04-22 09:42:55 +03:00
|
|
|
|
tyNil, { Pseudo-type for the nil literal; compatible with tyClass }
|
Add case statements and enum types — required for self-hosting migration
case statement: TCaseStmt + TCaseBranch AST nodes; tkCase/tkOf tokens;
ParseCaseStmt (multi-value branches, optional else); AnalyseCaseStmt
(ordinal selector check); EmitCaseStmt (ceqw dispatch chain + jmp).
enum types: TEnumTypeDef AST node; TEnumTypeDesc symbol-table descriptor
(tyEnum, ByteSize=4, QBE 'w'); ParseEnumDef; AnalyseTypeDecls enum path
registers each member as skConstant with ordinal value 0..N-1; IsNumeric,
IsOrdinal, ByteSize, AllocAlign, QbeTypeOf all updated for tyEnum.
Variable alloc path handles tyEnum identically to tyInteger.
These two features unblock the self-hosting source migration: the compiler
source uses TTokenKind, TTypeKind, TBinaryOp, and TSymbolKind enums
throughout, all wrapped in case dispatch.
834 tests pass, zero Valgrind errors.
2026-04-23 15:46:06 +03:00
|
|
|
|
tyPointer, { Typed or untyped pointer — QBE 'l'; see TPointerTypeDesc }
|
2026-04-27 18:31:04 +03:00
|
|
|
|
tyEnum, { Enumeration type — stored as QBE 'w' (Integer); see TEnumTypeDesc }
|
feat: static array array[L..H] of T — stack-allocated fixed-size arrays
Adds TStaticSubscriptAssign AST node, ParseTypeName extension for
array[L..H] of T syntax (tkDotDot token), TStaticArrayTypeDesc with
RawSize for correct element-footprint sizing (1 for Byte, 4 for Integer),
and full codegen: alloc4/alloc8 + memset zero-init, storeb/storew for
writes, loadub/loadw for reads, LowBound subtraction for non-zero ranges.
Also includes array literal ['a','b','c'] call-site support (step 4):
TArrayLiteralExpr, open-array formal matching, stack buffer allocation,
and codegen emitting two QBE args (data ptr + compile-time high index).
13 new static-array tests + 9 new array-literal tests; all 888 tests pass.
2026-04-27 19:22:29 +03:00
|
|
|
|
tyOpenArray, { Open-array parameter — two-register ABI: data ptr + high index }
|
2026-04-28 15:20:43 +03:00
|
|
|
|
tyStaticArray, { Fixed-size array: stack-allocated, compile-time bounds }
|
feat: set types — set of TEnum, set literals, in, Include/Exclude, arithmetic
Implements Pascal bit-set types end-to-end:
- Lexer: tkSet, tkIn keywords
- AST: TSetTypeDef node; boIn binary operator
- Symbol table: tySet, TSetTypeDesc (w ≤32 members, l ≤64); NewSetType factory;
Include/Exclude registered as built-in procedures
- Parser: ParseSetDef; in operator in expression parser
- Semantic: set type registration; set literal validation; boIn membership check;
set arithmetic (+/-/*) and equality (=/≠) operator analysis; Include/Exclude
argument validation
- Codegen: compile-time bitmask for set literals; shr+and for `in`; or/xor-and/and
for union/difference/intersection; OR-in-place (Include), AND-NOT-in-place (Exclude)
- Tests: 32 new tests in cp.test.sets covering parse, semantic, and codegen
- Fix: rename Set→Assign method in genericmethodimpls test (set is now reserved)
- Docs: SetType/SetLiteral grammar rules; Set Types rationale section; resolve
enum underlying type open question (confirmed w)
2026-04-28 20:07:40 +03:00
|
|
|
|
tyPChar, { Opaque C pointer for interop: PChar(str) / string(pchar) }
|
|
|
|
|
|
tySet { Bit-set over an enum base type — QBE 'w' (≤32 members) or 'l' (≤64) }
|
2026-04-20 19:52:09 +03:00
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
TTypeDesc = class
|
|
|
|
|
|
public
|
|
|
|
|
|
Kind: TTypeKind;
|
|
|
|
|
|
Name: string;
|
|
|
|
|
|
function IsNumeric: Boolean;
|
|
|
|
|
|
function IsString: Boolean;
|
|
|
|
|
|
function IsOrdinal: Boolean;
|
Add Phase 2: record/class types, ARC strings, RTL stubs, and grammar doc
Symbol table:
- Added tyClass kind and NewClassType factory; TRecordTypeDesc now
accepts an optional kind parameter so records and classes share the
same descriptor with distinct semantics.
Semantic analyser:
- AnalyseTypeDecls runs before PushScope so type symbols land in global
scope and survive PopScope.
- Handles TRecordTypeDef and TClassTypeDef; sets IsConstructorCall on
TypeName.Create expressions and IsClassAccess on class-variable field
access and assignment nodes.
Code generator (QBE IR):
- Class variables: 8-byte pointer slot, zeroed on entry.
- Constructor calls: malloc(sizeof fields), store pointer.
- Class field access/write: load pointer, add field offset, load/store.
- ARC string assignment: AddRef new value, Release old value, then store.
- Block exit: Release every string variable in scope (EmitStringCleanup).
Lexer / Parser / AST:
- Added tkClass keyword and ParseClassDef; FieldDecl parsing refactored
into ParseFieldDecl(AFields) shared by both record and class.
- AST gains TClassTypeDef, and IsConstructorCall/IsClassAccess flags on
TFieldAccessExpr and TFieldAssignment.
RTL:
- blaise_arc.c: Phase 2 no-op stubs for _StringAddRef / _StringRelease.
- rtl/Makefile: builds blaise_rtl.a; `make install` copies it next to
the compiler binary for automatic discovery by FindRTL.
- Blaise.pas driver: FindRTL checks BLAISE_RTL env var then binary dir;
links the RTL archive when present.
Tests:
- 180 unit tests (records ×23, classes ×22, ARC ×9, codegen ×9, …).
- 4 end-to-end integration tests in tests/integration/test_arc_strings.sh
covering string assignment, two-var cleanup, reassignment cycle, and
empty-program linkage.
Docs:
- design.adoc updated with Phase 2 implementation status table.
- docs/grammar.ebnf: new authoritative EBNF grammar for the Blaise
language as implemented, including ARC semantic annotations.
2026-04-20 21:04:12 +03:00
|
|
|
|
function IsRecord: Boolean;
|
|
|
|
|
|
{ Size in bytes for QBE allocation. }
|
|
|
|
|
|
function ByteSize: Integer;
|
|
|
|
|
|
{ QBE alloc alignment: 4 for integer-only records, 8 for pointer/string. }
|
|
|
|
|
|
function AllocAlign: Integer;
|
feat: static array array[L..H] of T — stack-allocated fixed-size arrays
Adds TStaticSubscriptAssign AST node, ParseTypeName extension for
array[L..H] of T syntax (tkDotDot token), TStaticArrayTypeDesc with
RawSize for correct element-footprint sizing (1 for Byte, 4 for Integer),
and full codegen: alloc4/alloc8 + memset zero-init, storeb/storew for
writes, loadub/loadw for reads, LowBound subtraction for non-zero ranges.
Also includes array literal ['a','b','c'] call-site support (step 4):
TArrayLiteralExpr, open-array formal matching, stack buffer allocation,
and codegen emitting two QBE args (data ptr + compile-time high index).
13 new static-array tests + 9 new array-literal tests; all 888 tests pass.
2026-04-27 19:22:29 +03:00
|
|
|
|
{ True storage footprint for a single value: 1 for Byte/Boolean, 4 for Integer,
|
|
|
|
|
|
8 for pointer/string/Int64. Use for array element sizing; ByteSize pads
|
|
|
|
|
|
small scalars to 4 for standalone-variable alignment. }
|
|
|
|
|
|
function RawSize: Integer;
|
Add Phase 2: record/class types, ARC strings, RTL stubs, and grammar doc
Symbol table:
- Added tyClass kind and NewClassType factory; TRecordTypeDesc now
accepts an optional kind parameter so records and classes share the
same descriptor with distinct semantics.
Semantic analyser:
- AnalyseTypeDecls runs before PushScope so type symbols land in global
scope and survive PopScope.
- Handles TRecordTypeDef and TClassTypeDef; sets IsConstructorCall on
TypeName.Create expressions and IsClassAccess on class-variable field
access and assignment nodes.
Code generator (QBE IR):
- Class variables: 8-byte pointer slot, zeroed on entry.
- Constructor calls: malloc(sizeof fields), store pointer.
- Class field access/write: load pointer, add field offset, load/store.
- ARC string assignment: AddRef new value, Release old value, then store.
- Block exit: Release every string variable in scope (EmitStringCleanup).
Lexer / Parser / AST:
- Added tkClass keyword and ParseClassDef; FieldDecl parsing refactored
into ParseFieldDecl(AFields) shared by both record and class.
- AST gains TClassTypeDef, and IsConstructorCall/IsClassAccess flags on
TFieldAccessExpr and TFieldAssignment.
RTL:
- blaise_arc.c: Phase 2 no-op stubs for _StringAddRef / _StringRelease.
- rtl/Makefile: builds blaise_rtl.a; `make install` copies it next to
the compiler binary for automatic discovery by FindRTL.
- Blaise.pas driver: FindRTL checks BLAISE_RTL env var then binary dir;
links the RTL archive when present.
Tests:
- 180 unit tests (records ×23, classes ×22, ARC ×9, codegen ×9, …).
- 4 end-to-end integration tests in tests/integration/test_arc_strings.sh
covering string assignment, two-var cleanup, reassignment cycle, and
empty-program linkage.
Docs:
- design.adoc updated with Phase 2 implementation status table.
- docs/grammar.ebnf: new authoritative EBNF grammar for the Blaise
language as implemented, including ARC semantic annotations.
2026-04-20 21:04:12 +03:00
|
|
|
|
end;
|
|
|
|
|
|
|
Add pointer type infrastructure: ^T types, dereference, GetMem/FreeMem/ReallocMem
- uSymbolTable: tyPointer kind, TPointerTypeDesc(BaseType), GetMem/FreeMem/ReallocMem builtins
- uLexer/uPasTokeniser: tkCaret token for '^'; suppress ^X string escape (not in Clean Pascal)
- uParser: ParseTypeName handles '^TypeName' prefix (uses Self. for explicit recursion);
TDerefExpr for P^ postfix; TPointerWriteStmt for P^ := V lhs
- uAST: TDerefExpr, TPointerWriteStmt nodes
- uSemantic: FindTypeOrInstantiate creates TPointerTypeDesc on demand for '^T';
AnalyseDerefExpr, AnalysePointerWriteStmt; GetMem/ReallocMem/type-cast handling
- uCodeGenQBE: tyPointer→'l', EmitPointerWrite (storew/storel), TDerefExpr (loadw/loadl),
GetMem→malloc, FreeMem→free, ReallocMem→realloc, pointer arithmetic (extsw + add/sub),
type-cast via copy
- 15 new tests (556 total), all passing
2026-04-22 09:42:55 +03:00
|
|
|
|
{ Typed or untyped pointer descriptor.
|
|
|
|
|
|
BaseType = nil means untyped 'Pointer'; non-nil means '^BaseType'. }
|
|
|
|
|
|
TPointerTypeDesc = class(TTypeDesc)
|
|
|
|
|
|
public
|
|
|
|
|
|
BaseType: TTypeDesc; { not owned; nil = untyped Pointer }
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
2026-04-27 18:31:04 +03:00
|
|
|
|
{ Open-array parameter descriptor.
|
|
|
|
|
|
Carries the element type; the two-register ABI (data ptr + high index)
|
|
|
|
|
|
is handled entirely in the parser, semantic pass, and codegen. }
|
|
|
|
|
|
TOpenArrayTypeDesc = class(TTypeDesc)
|
|
|
|
|
|
public
|
|
|
|
|
|
ElementType: TTypeDesc; { not owned }
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
feat: static array array[L..H] of T — stack-allocated fixed-size arrays
Adds TStaticSubscriptAssign AST node, ParseTypeName extension for
array[L..H] of T syntax (tkDotDot token), TStaticArrayTypeDesc with
RawSize for correct element-footprint sizing (1 for Byte, 4 for Integer),
and full codegen: alloc4/alloc8 + memset zero-init, storeb/storew for
writes, loadub/loadw for reads, LowBound subtraction for non-zero ranges.
Also includes array literal ['a','b','c'] call-site support (step 4):
TArrayLiteralExpr, open-array formal matching, stack buffer allocation,
and codegen emitting two QBE args (data ptr + compile-time high index).
13 new static-array tests + 9 new array-literal tests; all 888 tests pass.
2026-04-27 19:22:29 +03:00
|
|
|
|
{ Static array descriptor: fixed-size, stack-allocated, compile-time bounds.
|
|
|
|
|
|
Element access: base_ptr + (I − LowBound) × ElementType.ByteSize. }
|
|
|
|
|
|
TStaticArrayTypeDesc = class(TTypeDesc)
|
|
|
|
|
|
public
|
|
|
|
|
|
ElementType: TTypeDesc; { not owned }
|
|
|
|
|
|
LowBound: Integer;
|
|
|
|
|
|
HighBound: Integer;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
Add case statements and enum types — required for self-hosting migration
case statement: TCaseStmt + TCaseBranch AST nodes; tkCase/tkOf tokens;
ParseCaseStmt (multi-value branches, optional else); AnalyseCaseStmt
(ordinal selector check); EmitCaseStmt (ceqw dispatch chain + jmp).
enum types: TEnumTypeDef AST node; TEnumTypeDesc symbol-table descriptor
(tyEnum, ByteSize=4, QBE 'w'); ParseEnumDef; AnalyseTypeDecls enum path
registers each member as skConstant with ordinal value 0..N-1; IsNumeric,
IsOrdinal, ByteSize, AllocAlign, QbeTypeOf all updated for tyEnum.
Variable alloc path handles tyEnum identically to tyInteger.
These two features unblock the self-hosting source migration: the compiler
source uses TTokenKind, TTypeKind, TBinaryOp, and TSymbolKind enums
throughout, all wrapped in case dispatch.
834 tests pass, zero Valgrind errors.
2026-04-23 15:46:06 +03:00
|
|
|
|
{ Enum type descriptor. Members are ordered; ordinal values are 0..N-1.
|
|
|
|
|
|
Stored as QBE 'w' (same as Integer). Each member is also registered in
|
|
|
|
|
|
the symbol table as a skConstant with this type descriptor. }
|
|
|
|
|
|
TEnumTypeDesc = class(TTypeDesc)
|
|
|
|
|
|
public
|
|
|
|
|
|
Members: TStringList; { owned — ordered member names }
|
|
|
|
|
|
constructor Create(const AName: string);
|
|
|
|
|
|
destructor Destroy; override;
|
|
|
|
|
|
function OrdinalOf(const AMember: string): Integer;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
feat: set types — set of TEnum, set literals, in, Include/Exclude, arithmetic
Implements Pascal bit-set types end-to-end:
- Lexer: tkSet, tkIn keywords
- AST: TSetTypeDef node; boIn binary operator
- Symbol table: tySet, TSetTypeDesc (w ≤32 members, l ≤64); NewSetType factory;
Include/Exclude registered as built-in procedures
- Parser: ParseSetDef; in operator in expression parser
- Semantic: set type registration; set literal validation; boIn membership check;
set arithmetic (+/-/*) and equality (=/≠) operator analysis; Include/Exclude
argument validation
- Codegen: compile-time bitmask for set literals; shr+and for `in`; or/xor-and/and
for union/difference/intersection; OR-in-place (Include), AND-NOT-in-place (Exclude)
- Tests: 32 new tests in cp.test.sets covering parse, semantic, and codegen
- Fix: rename Set→Assign method in genericmethodimpls test (set is now reserved)
- Docs: SetType/SetLiteral grammar rules; Set Types rationale section; resolve
enum underlying type open question (confirmed w)
2026-04-28 20:07:40 +03:00
|
|
|
|
{ Set type descriptor. BaseType is the element enum; BitCount is the number
|
|
|
|
|
|
of bits required (= BaseType.Members.Count). Stored as QBE 'w' for ≤32
|
|
|
|
|
|
members, 'l' for 33–64. Each member ordinal N maps to bit (1 shl N). }
|
|
|
|
|
|
TSetTypeDesc = class(TTypeDesc)
|
|
|
|
|
|
public
|
|
|
|
|
|
BaseType: TEnumTypeDesc; { not owned }
|
|
|
|
|
|
BitCount: Integer; { = BaseType.Members.Count }
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
Add Phase 2: record/class types, ARC strings, RTL stubs, and grammar doc
Symbol table:
- Added tyClass kind and NewClassType factory; TRecordTypeDesc now
accepts an optional kind parameter so records and classes share the
same descriptor with distinct semantics.
Semantic analyser:
- AnalyseTypeDecls runs before PushScope so type symbols land in global
scope and survive PopScope.
- Handles TRecordTypeDef and TClassTypeDef; sets IsConstructorCall on
TypeName.Create expressions and IsClassAccess on class-variable field
access and assignment nodes.
Code generator (QBE IR):
- Class variables: 8-byte pointer slot, zeroed on entry.
- Constructor calls: malloc(sizeof fields), store pointer.
- Class field access/write: load pointer, add field offset, load/store.
- ARC string assignment: AddRef new value, Release old value, then store.
- Block exit: Release every string variable in scope (EmitStringCleanup).
Lexer / Parser / AST:
- Added tkClass keyword and ParseClassDef; FieldDecl parsing refactored
into ParseFieldDecl(AFields) shared by both record and class.
- AST gains TClassTypeDef, and IsConstructorCall/IsClassAccess flags on
TFieldAccessExpr and TFieldAssignment.
RTL:
- blaise_arc.c: Phase 2 no-op stubs for _StringAddRef / _StringRelease.
- rtl/Makefile: builds blaise_rtl.a; `make install` copies it next to
the compiler binary for automatic discovery by FindRTL.
- Blaise.pas driver: FindRTL checks BLAISE_RTL env var then binary dir;
links the RTL archive when present.
Tests:
- 180 unit tests (records ×23, classes ×22, ARC ×9, codegen ×9, …).
- 4 end-to-end integration tests in tests/integration/test_arc_strings.sh
covering string assignment, two-var cleanup, reassignment cycle, and
empty-program linkage.
Docs:
- design.adoc updated with Phase 2 implementation status table.
- docs/grammar.ebnf: new authoritative EBNF grammar for the Blaise
language as implemented, including ARC semantic annotations.
2026-04-20 21:04:12 +03:00
|
|
|
|
{ Field entry inside a record type descriptor. }
|
|
|
|
|
|
TFieldInfo = class
|
|
|
|
|
|
public
|
|
|
|
|
|
Name: string;
|
|
|
|
|
|
TypeDesc: TTypeDesc; { not owned }
|
|
|
|
|
|
Offset: Integer; { byte offset from record base }
|
Implement [Weak] attribute for cycle-breaking under ARC
A weak reference is a slot that does not contribute to the target's
refcount and is automatically nil'd when the target is freed. This
breaks reference cycles that would otherwise leak under universal ARC.
Attribute model (Delphi-compatible)
type
TNode = class
Other: TNode; // strong, default
[Weak] Back: TNode; // weak, no refcount, zeroed on free
end;
The parser accepts `[Ident]` and `[Ident(args)]` attribute lists before
var and class-field declarations. Attribute names are captured
verbatim on TVarDecl.Attributes / TFieldDecl.Attributes; unknown
attributes are accepted silently so user-defined attributes (which
will materialise with RTTI) don't need to wait for the syntax.
Semantic analysis resolves `[Weak]` with the Delphi convention that
the `Attribute` suffix is optional — `[Weak]` and `[WeakAttribute]`
both resolve to the recognised marker. Applying [Weak] to a non-
reference type (Integer, string, record) is an error at declaration
time. Resolved weakness is surfaced on TVarDecl.IsWeak / TFieldInfo.IsWeak
and TSymbol.IsWeak for codegen consumption.
Lexer: tkLBracket / tkRBracket added.
RTL (blaise_weak.c): global chained hash table maps target pointers to
lists of registered weak slot addresses. _WeakAssign registers a new
slot (and unregisters any prior), _WeakClear unregisters and zeros,
_WeakZeroSlots nils all slots registered against a target. The last
is called from _ClassRelease at refcount zero, before field cleanup
and free, so weak readers never see dangling memory.
Codegen: weak class/interface vars bypass addref/release entirely.
Variable assignment, field assignment, scope-exit cleanup, exception-
path cleanup, and per-class _FieldCleanup functions all branch on the
weak flag to emit _WeakAssign / _WeakClear against the slot address
instead of strong refcount operations.
Tests
* cp.test.weakref.pas — 14 unit tests covering lexer, parser,
semantic validation (including the suffix-drop rule), and codegen
emission at every insertion point.
* cp.test.e2e.TestRun_WeakRef_BreaksCycle_Valgrind — compile and run
a two-node mutual-reference program with [Weak]. Without the
attribute, valgrind reports both nodes as definitely lost; with it,
the run is leak-free.
All 711 compiler tests pass.
Deferred: TCustomAttribute / WeakAttribute Pascal-side class
declarations. The compiler resolves `[Weak]` purely by attribute
name, so the RTL classes are not load-bearing today. They will be
added as empty marker types when RTTI arrives and RTTI-queryable
attributes become meaningful.
2026-04-23 12:15:00 +03:00
|
|
|
|
IsWeak: Boolean; { set when the class field was declared [Weak];
|
|
|
|
|
|
field cleanup emits _WeakClear for weak fields
|
|
|
|
|
|
and field assignment bypasses addref/release. }
|
Add Phase 2: record/class types, ARC strings, RTL stubs, and grammar doc
Symbol table:
- Added tyClass kind and NewClassType factory; TRecordTypeDesc now
accepts an optional kind parameter so records and classes share the
same descriptor with distinct semantics.
Semantic analyser:
- AnalyseTypeDecls runs before PushScope so type symbols land in global
scope and survive PopScope.
- Handles TRecordTypeDef and TClassTypeDef; sets IsConstructorCall on
TypeName.Create expressions and IsClassAccess on class-variable field
access and assignment nodes.
Code generator (QBE IR):
- Class variables: 8-byte pointer slot, zeroed on entry.
- Constructor calls: malloc(sizeof fields), store pointer.
- Class field access/write: load pointer, add field offset, load/store.
- ARC string assignment: AddRef new value, Release old value, then store.
- Block exit: Release every string variable in scope (EmitStringCleanup).
Lexer / Parser / AST:
- Added tkClass keyword and ParseClassDef; FieldDecl parsing refactored
into ParseFieldDecl(AFields) shared by both record and class.
- AST gains TClassTypeDef, and IsConstructorCall/IsClassAccess flags on
TFieldAccessExpr and TFieldAssignment.
RTL:
- blaise_arc.c: Phase 2 no-op stubs for _StringAddRef / _StringRelease.
- rtl/Makefile: builds blaise_rtl.a; `make install` copies it next to
the compiler binary for automatic discovery by FindRTL.
- Blaise.pas driver: FindRTL checks BLAISE_RTL env var then binary dir;
links the RTL archive when present.
Tests:
- 180 unit tests (records ×23, classes ×22, ARC ×9, codegen ×9, …).
- 4 end-to-end integration tests in tests/integration/test_arc_strings.sh
covering string assignment, two-var cleanup, reassignment cycle, and
empty-program linkage.
Docs:
- design.adoc updated with Phase 2 implementation status table.
- docs/grammar.ebnf: new authoritative EBNF grammar for the Blaise
language as implemented, including ARC semantic annotations.
2026-04-20 21:04:12 +03:00
|
|
|
|
end;
|
|
|
|
|
|
|
2026-04-21 02:05:57 +03:00
|
|
|
|
{ One entry in a class vtable — tracks slot index and implementing method. }
|
|
|
|
|
|
TVTableEntry = class
|
|
|
|
|
|
public
|
|
|
|
|
|
Slot: Integer; { index in vtable }
|
|
|
|
|
|
MethName: string; { unqualified method name }
|
|
|
|
|
|
ImplName: string; { fully-qualified QBE label, e.g. $TDog_Speak }
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
2026-04-22 01:15:15 +03:00
|
|
|
|
{ Property descriptor — one per declared property on a class. }
|
|
|
|
|
|
TPropertyInfo = class
|
|
|
|
|
|
public
|
2026-04-29 14:18:45 +03:00
|
|
|
|
Name: string;
|
|
|
|
|
|
TypeDesc: TTypeDesc; { not owned — resolved by semantic analysis }
|
|
|
|
|
|
ReadField: string; { '' if method-backed read }
|
|
|
|
|
|
ReadMethod: string; { '' if field-backed read }
|
|
|
|
|
|
WriteField: string; { '' if method-backed write or read-only }
|
|
|
|
|
|
WriteMethod: string; { '' if field-backed write or read-only }
|
|
|
|
|
|
IndexParamName: string; { '' = non-indexed property }
|
|
|
|
|
|
IndexTypeDesc: TTypeDesc; { not owned; non-nil when IndexParamName <> '' }
|
2026-04-22 01:15:15 +03:00
|
|
|
|
end;
|
|
|
|
|
|
|
2026-05-01 08:52:48 +03:00
|
|
|
|
{ Type descriptor for zero-GUID interface types (Phase 3). }
|
|
|
|
|
|
TInterfaceTypeDesc = class(TTypeDesc)
|
|
|
|
|
|
private
|
|
|
|
|
|
FMethods: TStringList; { method names, case-insensitive }
|
|
|
|
|
|
FReturnTypes: TStringList; { parallel: return type name, '' = procedure }
|
|
|
|
|
|
FParent: TInterfaceTypeDesc; { not owned; nil if no parent }
|
|
|
|
|
|
public
|
|
|
|
|
|
constructor Create(const AName: string);
|
|
|
|
|
|
destructor Destroy; override;
|
|
|
|
|
|
procedure AddMethod(const AName: string; const AReturnTypeName: string);
|
|
|
|
|
|
function HasMethod(const AName: string): Boolean;
|
|
|
|
|
|
function MethodCount: Integer;
|
|
|
|
|
|
function MethodName(AIndex: Integer): string;
|
|
|
|
|
|
function MethodReturnTypeName(AIndex: Integer): string;
|
|
|
|
|
|
function MethodIndex(const AName: string): Integer;
|
|
|
|
|
|
property Parent: TInterfaceTypeDesc read FParent write FParent;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
Add Phase 2: record/class types, ARC strings, RTL stubs, and grammar doc
Symbol table:
- Added tyClass kind and NewClassType factory; TRecordTypeDesc now
accepts an optional kind parameter so records and classes share the
same descriptor with distinct semantics.
Semantic analyser:
- AnalyseTypeDecls runs before PushScope so type symbols land in global
scope and survive PopScope.
- Handles TRecordTypeDef and TClassTypeDef; sets IsConstructorCall on
TypeName.Create expressions and IsClassAccess on class-variable field
access and assignment nodes.
Code generator (QBE IR):
- Class variables: 8-byte pointer slot, zeroed on entry.
- Constructor calls: malloc(sizeof fields), store pointer.
- Class field access/write: load pointer, add field offset, load/store.
- ARC string assignment: AddRef new value, Release old value, then store.
- Block exit: Release every string variable in scope (EmitStringCleanup).
Lexer / Parser / AST:
- Added tkClass keyword and ParseClassDef; FieldDecl parsing refactored
into ParseFieldDecl(AFields) shared by both record and class.
- AST gains TClassTypeDef, and IsConstructorCall/IsClassAccess flags on
TFieldAccessExpr and TFieldAssignment.
RTL:
- blaise_arc.c: Phase 2 no-op stubs for _StringAddRef / _StringRelease.
- rtl/Makefile: builds blaise_rtl.a; `make install` copies it next to
the compiler binary for automatic discovery by FindRTL.
- Blaise.pas driver: FindRTL checks BLAISE_RTL env var then binary dir;
links the RTL archive when present.
Tests:
- 180 unit tests (records ×23, classes ×22, ARC ×9, codegen ×9, …).
- 4 end-to-end integration tests in tests/integration/test_arc_strings.sh
covering string assignment, two-var cleanup, reassignment cycle, and
empty-program linkage.
Docs:
- design.adoc updated with Phase 2 implementation status table.
- docs/grammar.ebnf: new authoritative EBNF grammar for the Blaise
language as implemented, including ARC semantic annotations.
2026-04-20 21:04:12 +03:00
|
|
|
|
{ Extended type descriptor for record types. }
|
|
|
|
|
|
TRecordTypeDesc = class(TTypeDesc)
|
|
|
|
|
|
private
|
2026-04-23 12:51:56 +03:00
|
|
|
|
FFields: TObjectList; { owned TFieldInfo }
|
|
|
|
|
|
FKeys: TStringList; { sorted, case-insensitive; Objects[] = TFieldInfo (not owned) }
|
|
|
|
|
|
FParent: TRecordTypeDesc; { not owned; nil for root classes }
|
|
|
|
|
|
FVTable: TObjectList; { owned TVTableEntry; nil if no virtual methods }
|
|
|
|
|
|
FImplements: TObjectList; { not owned — TInterfaceTypeDesc references }
|
|
|
|
|
|
FProperties: TObjectList; { owned TPropertyInfo }
|
|
|
|
|
|
FHasDestroyMethod: Boolean; { True when the class declares a 'Destroy' method }
|
Add Phase 2: record/class types, ARC strings, RTL stubs, and grammar doc
Symbol table:
- Added tyClass kind and NewClassType factory; TRecordTypeDesc now
accepts an optional kind parameter so records and classes share the
same descriptor with distinct semantics.
Semantic analyser:
- AnalyseTypeDecls runs before PushScope so type symbols land in global
scope and survive PopScope.
- Handles TRecordTypeDef and TClassTypeDef; sets IsConstructorCall on
TypeName.Create expressions and IsClassAccess on class-variable field
access and assignment nodes.
Code generator (QBE IR):
- Class variables: 8-byte pointer slot, zeroed on entry.
- Constructor calls: malloc(sizeof fields), store pointer.
- Class field access/write: load pointer, add field offset, load/store.
- ARC string assignment: AddRef new value, Release old value, then store.
- Block exit: Release every string variable in scope (EmitStringCleanup).
Lexer / Parser / AST:
- Added tkClass keyword and ParseClassDef; FieldDecl parsing refactored
into ParseFieldDecl(AFields) shared by both record and class.
- AST gains TClassTypeDef, and IsConstructorCall/IsClassAccess flags on
TFieldAccessExpr and TFieldAssignment.
RTL:
- blaise_arc.c: Phase 2 no-op stubs for _StringAddRef / _StringRelease.
- rtl/Makefile: builds blaise_rtl.a; `make install` copies it next to
the compiler binary for automatic discovery by FindRTL.
- Blaise.pas driver: FindRTL checks BLAISE_RTL env var then binary dir;
links the RTL archive when present.
Tests:
- 180 unit tests (records ×23, classes ×22, ARC ×9, codegen ×9, …).
- 4 end-to-end integration tests in tests/integration/test_arc_strings.sh
covering string assignment, two-var cleanup, reassignment cycle, and
empty-program linkage.
Docs:
- design.adoc updated with Phase 2 implementation status table.
- docs/grammar.ebnf: new authoritative EBNF grammar for the Blaise
language as implemented, including ARC semantic annotations.
2026-04-20 21:04:12 +03:00
|
|
|
|
public
|
feat(compiler): self-hosting fixpoint reached — Step 10
Parser, lexer, tokeniser, semantic analyser and symbol table extended
to compile the multi-file Blaise source with the Blaise compiler itself.
Stage-2 and stage-3 IR are byte-identical; fixpoint confirmed.
Changes to reach fixpoint:
- uLexer: source filename parameter for actionable parse error messages
- uParser: method directive loop (inline/stdcall/abstract/…), unit
interface/implementation loops for var/const/type blocks, chained
field-assignment (A.B.C := v), filename in all EParseError messages
- uPasTokeniser: full rewrite to remove FPC-only features (char sets,
shr, Exit(value), private/public visibility) so Blaise can compile it
- uSymbolTable: remove unsupported default parameter values
- uSemantic: replace Exit(value) with Result+Exit; expand five `with`
statement blocks to explicit typed local variables
- uAST, uCodeGenQBE: minor additions supporting the above
- uUnitLoader: pass filename to TLexer.Create
- classes.pas: inline dupIgnore literal (const section deferred)
2026-04-30 15:30:30 +03:00
|
|
|
|
constructor Create(const AName: string; AKind: TTypeKind);
|
Add Phase 2: record/class types, ARC strings, RTL stubs, and grammar doc
Symbol table:
- Added tyClass kind and NewClassType factory; TRecordTypeDesc now
accepts an optional kind parameter so records and classes share the
same descriptor with distinct semantics.
Semantic analyser:
- AnalyseTypeDecls runs before PushScope so type symbols land in global
scope and survive PopScope.
- Handles TRecordTypeDef and TClassTypeDef; sets IsConstructorCall on
TypeName.Create expressions and IsClassAccess on class-variable field
access and assignment nodes.
Code generator (QBE IR):
- Class variables: 8-byte pointer slot, zeroed on entry.
- Constructor calls: malloc(sizeof fields), store pointer.
- Class field access/write: load pointer, add field offset, load/store.
- ARC string assignment: AddRef new value, Release old value, then store.
- Block exit: Release every string variable in scope (EmitStringCleanup).
Lexer / Parser / AST:
- Added tkClass keyword and ParseClassDef; FieldDecl parsing refactored
into ParseFieldDecl(AFields) shared by both record and class.
- AST gains TClassTypeDef, and IsConstructorCall/IsClassAccess flags on
TFieldAccessExpr and TFieldAssignment.
RTL:
- blaise_arc.c: Phase 2 no-op stubs for _StringAddRef / _StringRelease.
- rtl/Makefile: builds blaise_rtl.a; `make install` copies it next to
the compiler binary for automatic discovery by FindRTL.
- Blaise.pas driver: FindRTL checks BLAISE_RTL env var then binary dir;
links the RTL archive when present.
Tests:
- 180 unit tests (records ×23, classes ×22, ARC ×9, codegen ×9, …).
- 4 end-to-end integration tests in tests/integration/test_arc_strings.sh
covering string assignment, two-var cleanup, reassignment cycle, and
empty-program linkage.
Docs:
- design.adoc updated with Phase 2 implementation status table.
- docs/grammar.ebnf: new authoritative EBNF grammar for the Blaise
language as implemented, including ARC semantic annotations.
2026-04-20 21:04:12 +03:00
|
|
|
|
destructor Destroy; override;
|
|
|
|
|
|
procedure AddField(const AName: string; AType: TTypeDesc);
|
|
|
|
|
|
function FindField(const AName: string): TFieldInfo;
|
|
|
|
|
|
function TotalSize: Integer;
|
|
|
|
|
|
function MaxAlign: Integer;
|
2026-04-21 02:05:57 +03:00
|
|
|
|
|
|
|
|
|
|
{ Vtable management }
|
|
|
|
|
|
function HasVTable: Boolean;
|
|
|
|
|
|
function VTableCount: Integer;
|
|
|
|
|
|
function VTableEntryAt(ASlot: Integer): TVTableEntry;
|
|
|
|
|
|
function FindVTableSlot(const AMethodName: string): Integer;
|
|
|
|
|
|
function AddVTableSlot(const AMethodName, AImplName: string): Integer;
|
|
|
|
|
|
procedure OverrideVTableSlot(ASlot: Integer; const AImplName: string);
|
|
|
|
|
|
procedure CopyVTableFrom(AParent: TRecordTypeDesc);
|
|
|
|
|
|
|
2026-04-21 20:24:55 +03:00
|
|
|
|
{ Interface implements tracking }
|
|
|
|
|
|
procedure AddImplements(AIntf: TInterfaceTypeDesc);
|
|
|
|
|
|
function ImplementsCount: Integer;
|
|
|
|
|
|
function ImplementsIntfAt(AIndex: Integer): TInterfaceTypeDesc;
|
|
|
|
|
|
|
2026-04-22 01:15:15 +03:00
|
|
|
|
{ Property tracking }
|
|
|
|
|
|
procedure AddProperty(AProp: TPropertyInfo);
|
|
|
|
|
|
function FindProperty(const AName: string): TPropertyInfo;
|
|
|
|
|
|
|
2026-05-02 01:37:07 +03:00
|
|
|
|
property Fields: TObjectList read FFields;
|
|
|
|
|
|
property Properties: TObjectList read FProperties;
|
2026-04-21 00:41:28 +03:00
|
|
|
|
property Parent: TRecordTypeDesc read FParent write FParent;
|
2026-04-23 12:51:56 +03:00
|
|
|
|
property HasDestroyMethod: Boolean
|
|
|
|
|
|
read FHasDestroyMethod write FHasDestroyMethod;
|
2026-04-20 19:52:09 +03:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
{ Symbols }
|
|
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
|
|
|
|
|
|
TSymbolKind = (
|
|
|
|
|
|
skVariable,
|
|
|
|
|
|
skType,
|
|
|
|
|
|
skProcedure,
|
|
|
|
|
|
skFunction,
|
2026-04-21 01:18:23 +03:00
|
|
|
|
skParameter,
|
2026-04-22 18:37:31 +03:00
|
|
|
|
skVarParameter,
|
|
|
|
|
|
skConstant { built-in or user-declared constant; ConstValue holds the integer value }
|
2026-04-20 19:52:09 +03:00
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
TParamDesc = class
|
|
|
|
|
|
public
|
|
|
|
|
|
Name: string;
|
|
|
|
|
|
TypeDesc: TTypeDesc; { not owned }
|
|
|
|
|
|
IsConst: Boolean;
|
|
|
|
|
|
IsVar: Boolean;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
TSymbol = class
|
|
|
|
|
|
public
|
2026-04-22 18:37:31 +03:00
|
|
|
|
Name: string;
|
|
|
|
|
|
Kind: TSymbolKind;
|
|
|
|
|
|
TypeDesc: TTypeDesc; { not owned; nil for procedures }
|
|
|
|
|
|
Params: TObjectList; { owned TParamDesc; populated for procedures/functions }
|
2026-04-24 02:21:43 +03:00
|
|
|
|
ConstValue: Int64; { valid when Kind = skConstant; integer/bool/enum value }
|
|
|
|
|
|
ConstString: string; { valid when Kind = skConstant and type is tyString }
|
Implement [Weak] attribute for cycle-breaking under ARC
A weak reference is a slot that does not contribute to the target's
refcount and is automatically nil'd when the target is freed. This
breaks reference cycles that would otherwise leak under universal ARC.
Attribute model (Delphi-compatible)
type
TNode = class
Other: TNode; // strong, default
[Weak] Back: TNode; // weak, no refcount, zeroed on free
end;
The parser accepts `[Ident]` and `[Ident(args)]` attribute lists before
var and class-field declarations. Attribute names are captured
verbatim on TVarDecl.Attributes / TFieldDecl.Attributes; unknown
attributes are accepted silently so user-defined attributes (which
will materialise with RTTI) don't need to wait for the syntax.
Semantic analysis resolves `[Weak]` with the Delphi convention that
the `Attribute` suffix is optional — `[Weak]` and `[WeakAttribute]`
both resolve to the recognised marker. Applying [Weak] to a non-
reference type (Integer, string, record) is an error at declaration
time. Resolved weakness is surfaced on TVarDecl.IsWeak / TFieldInfo.IsWeak
and TSymbol.IsWeak for codegen consumption.
Lexer: tkLBracket / tkRBracket added.
RTL (blaise_weak.c): global chained hash table maps target pointers to
lists of registered weak slot addresses. _WeakAssign registers a new
slot (and unregisters any prior), _WeakClear unregisters and zeros,
_WeakZeroSlots nils all slots registered against a target. The last
is called from _ClassRelease at refcount zero, before field cleanup
and free, so weak readers never see dangling memory.
Codegen: weak class/interface vars bypass addref/release entirely.
Variable assignment, field assignment, scope-exit cleanup, exception-
path cleanup, and per-class _FieldCleanup functions all branch on the
weak flag to emit _WeakAssign / _WeakClear against the slot address
instead of strong refcount operations.
Tests
* cp.test.weakref.pas — 14 unit tests covering lexer, parser,
semantic validation (including the suffix-drop rule), and codegen
emission at every insertion point.
* cp.test.e2e.TestRun_WeakRef_BreaksCycle_Valgrind — compile and run
a two-node mutual-reference program with [Weak]. Without the
attribute, valgrind reports both nodes as definitely lost; with it,
the run is leak-free.
All 711 compiler tests pass.
Deferred: TCustomAttribute / WeakAttribute Pascal-side class
declarations. The compiler resolves `[Weak]` purely by attribute
name, so the RTL classes are not load-bearing today. They will be
added as empty marker types when RTTI arrives and RTTI-queryable
attributes become meaningful.
2026-04-23 12:15:00 +03:00
|
|
|
|
IsWeak: Boolean; { true for variables declared [Weak]; codegen
|
|
|
|
|
|
keys off this to emit _WeakAssign instead
|
|
|
|
|
|
of the strong addref/release pattern. }
|
2026-04-24 02:21:43 +03:00
|
|
|
|
IsGlobal: Boolean; { true for program-level variables; codegen uses
|
|
|
|
|
|
QBE data-section storage instead of stack alloc }
|
2026-04-20 19:52:09 +03:00
|
|
|
|
constructor Create(const AName: string; AKind: TSymbolKind; AType: TTypeDesc);
|
|
|
|
|
|
destructor Destroy; override;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
{ Scope }
|
|
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
|
|
|
|
|
|
TScope = class
|
|
|
|
|
|
private
|
|
|
|
|
|
FParent: TScope;
|
|
|
|
|
|
FSymbols: TObjectList; { owns all TSymbol in this scope }
|
|
|
|
|
|
FKeys: TStringList; { sorted, case-insensitive; Objects[] = TSymbol (not owned) }
|
|
|
|
|
|
public
|
|
|
|
|
|
constructor Create(AParent: TScope);
|
|
|
|
|
|
destructor Destroy; override;
|
|
|
|
|
|
property Parent: TScope read FParent;
|
|
|
|
|
|
{ Returns False if name already defined in this scope; caller must free ASymbol. }
|
|
|
|
|
|
function Define(ASymbol: TSymbol): Boolean;
|
|
|
|
|
|
function LookupLocal(const AName: string): TSymbol;
|
|
|
|
|
|
function Lookup(const AName: string): TSymbol;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
{ Symbol table }
|
|
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
|
|
|
|
|
|
TSymbolTable = class
|
|
|
|
|
|
private
|
|
|
|
|
|
FScopeStack: TObjectList; { owned TScope, index 0 = global }
|
|
|
|
|
|
FAllTypes: TObjectList; { owned TTypeDesc }
|
2026-04-22 01:02:23 +03:00
|
|
|
|
FGenerics: TStringList; { non-owning: base name → TGenericTypeDef* (void ptr) }
|
2026-04-20 19:52:09 +03:00
|
|
|
|
|
|
|
|
|
|
FTypeInteger: TTypeDesc;
|
|
|
|
|
|
FTypeInt64: TTypeDesc;
|
|
|
|
|
|
FTypeUInt32: TTypeDesc;
|
|
|
|
|
|
FTypeByte: TTypeDesc;
|
|
|
|
|
|
FTypeBoolean: TTypeDesc;
|
|
|
|
|
|
FTypeString: TTypeDesc;
|
|
|
|
|
|
FTypeVoid: TTypeDesc;
|
2026-04-21 00:41:28 +03:00
|
|
|
|
FTypeNil: TTypeDesc;
|
Add pointer type infrastructure: ^T types, dereference, GetMem/FreeMem/ReallocMem
- uSymbolTable: tyPointer kind, TPointerTypeDesc(BaseType), GetMem/FreeMem/ReallocMem builtins
- uLexer/uPasTokeniser: tkCaret token for '^'; suppress ^X string escape (not in Clean Pascal)
- uParser: ParseTypeName handles '^TypeName' prefix (uses Self. for explicit recursion);
TDerefExpr for P^ postfix; TPointerWriteStmt for P^ := V lhs
- uAST: TDerefExpr, TPointerWriteStmt nodes
- uSemantic: FindTypeOrInstantiate creates TPointerTypeDesc on demand for '^T';
AnalyseDerefExpr, AnalysePointerWriteStmt; GetMem/ReallocMem/type-cast handling
- uCodeGenQBE: tyPointer→'l', EmitPointerWrite (storew/storel), TDerefExpr (loadw/loadl),
GetMem→malloc, FreeMem→free, ReallocMem→realloc, pointer arithmetic (extsw + add/sub),
type-cast via copy
- 15 new tests (556 total), all passing
2026-04-22 09:42:55 +03:00
|
|
|
|
FTypePointer: TPointerTypeDesc; { untyped Pointer }
|
2026-04-28 15:20:43 +03:00
|
|
|
|
FTypePChar: TTypeDesc; { opaque C pointer }
|
2026-04-20 19:52:09 +03:00
|
|
|
|
|
|
|
|
|
|
function GetCurrentScope: TScope;
|
|
|
|
|
|
function GetScopeDepth: Integer;
|
|
|
|
|
|
function NewType(AKind: TTypeKind; const AName: string): TTypeDesc;
|
|
|
|
|
|
procedure RegisterBuiltins;
|
|
|
|
|
|
public
|
|
|
|
|
|
constructor Create;
|
|
|
|
|
|
destructor Destroy; override;
|
|
|
|
|
|
|
|
|
|
|
|
{ Scope management }
|
|
|
|
|
|
function PushScope: TScope;
|
|
|
|
|
|
procedure PopScope;
|
|
|
|
|
|
property CurrentScope: TScope read GetCurrentScope;
|
|
|
|
|
|
property ScopeDepth: Integer read GetScopeDepth;
|
|
|
|
|
|
|
|
|
|
|
|
{ Symbol management — owns ASymbol on success; caller must free on False }
|
|
|
|
|
|
function Define(ASymbol: TSymbol): Boolean;
|
2026-04-22 01:02:23 +03:00
|
|
|
|
{ Define in global (outermost) scope regardless of current push depth. }
|
|
|
|
|
|
function DefineGlobal(ASymbol: TSymbol): Boolean;
|
2026-04-20 19:52:09 +03:00
|
|
|
|
function Lookup(const AName: string): TSymbol;
|
|
|
|
|
|
|
|
|
|
|
|
{ Type lookup — case-insensitive, returns nil if not found }
|
|
|
|
|
|
function FindType(const AName: string): TTypeDesc;
|
|
|
|
|
|
|
Add Phase 2: record/class types, ARC strings, RTL stubs, and grammar doc
Symbol table:
- Added tyClass kind and NewClassType factory; TRecordTypeDesc now
accepts an optional kind parameter so records and classes share the
same descriptor with distinct semantics.
Semantic analyser:
- AnalyseTypeDecls runs before PushScope so type symbols land in global
scope and survive PopScope.
- Handles TRecordTypeDef and TClassTypeDef; sets IsConstructorCall on
TypeName.Create expressions and IsClassAccess on class-variable field
access and assignment nodes.
Code generator (QBE IR):
- Class variables: 8-byte pointer slot, zeroed on entry.
- Constructor calls: malloc(sizeof fields), store pointer.
- Class field access/write: load pointer, add field offset, load/store.
- ARC string assignment: AddRef new value, Release old value, then store.
- Block exit: Release every string variable in scope (EmitStringCleanup).
Lexer / Parser / AST:
- Added tkClass keyword and ParseClassDef; FieldDecl parsing refactored
into ParseFieldDecl(AFields) shared by both record and class.
- AST gains TClassTypeDef, and IsConstructorCall/IsClassAccess flags on
TFieldAccessExpr and TFieldAssignment.
RTL:
- blaise_arc.c: Phase 2 no-op stubs for _StringAddRef / _StringRelease.
- rtl/Makefile: builds blaise_rtl.a; `make install` copies it next to
the compiler binary for automatic discovery by FindRTL.
- Blaise.pas driver: FindRTL checks BLAISE_RTL env var then binary dir;
links the RTL archive when present.
Tests:
- 180 unit tests (records ×23, classes ×22, ARC ×9, codegen ×9, …).
- 4 end-to-end integration tests in tests/integration/test_arc_strings.sh
covering string assignment, two-var cleanup, reassignment cycle, and
empty-program linkage.
Docs:
- design.adoc updated with Phase 2 implementation status table.
- docs/grammar.ebnf: new authoritative EBNF grammar for the Blaise
language as implemented, including ARC semantic annotations.
2026-04-20 21:04:12 +03:00
|
|
|
|
{ Creates a new TRecordTypeDesc, registers it in FAllTypes, and returns it.
|
|
|
|
|
|
Caller must then add fields and register it as a skType symbol. }
|
|
|
|
|
|
function NewRecordType(const AName: string): TRecordTypeDesc;
|
|
|
|
|
|
|
|
|
|
|
|
{ Creates a new class type descriptor (tyClass, heap-allocated). }
|
|
|
|
|
|
function NewClassType(const AName: string): TRecordTypeDesc;
|
|
|
|
|
|
|
2026-04-21 20:24:55 +03:00
|
|
|
|
{ Creates a new interface type descriptor (tyInterface). }
|
|
|
|
|
|
function NewInterfaceType(const AName: string): TInterfaceTypeDesc;
|
|
|
|
|
|
|
Add pointer type infrastructure: ^T types, dereference, GetMem/FreeMem/ReallocMem
- uSymbolTable: tyPointer kind, TPointerTypeDesc(BaseType), GetMem/FreeMem/ReallocMem builtins
- uLexer/uPasTokeniser: tkCaret token for '^'; suppress ^X string escape (not in Clean Pascal)
- uParser: ParseTypeName handles '^TypeName' prefix (uses Self. for explicit recursion);
TDerefExpr for P^ postfix; TPointerWriteStmt for P^ := V lhs
- uAST: TDerefExpr, TPointerWriteStmt nodes
- uSemantic: FindTypeOrInstantiate creates TPointerTypeDesc on demand for '^T';
AnalyseDerefExpr, AnalysePointerWriteStmt; GetMem/ReallocMem/type-cast handling
- uCodeGenQBE: tyPointer→'l', EmitPointerWrite (storew/storel), TDerefExpr (loadw/loadl),
GetMem→malloc, FreeMem→free, ReallocMem→realloc, pointer arithmetic (extsw + add/sub),
type-cast via copy
- 15 new tests (556 total), all passing
2026-04-22 09:42:55 +03:00
|
|
|
|
{ Creates a typed pointer descriptor '^BaseType'. Registered in FAllTypes. }
|
|
|
|
|
|
function NewPointerType(const AName: string; ABase: TTypeDesc): TPointerTypeDesc;
|
Add case statements and enum types — required for self-hosting migration
case statement: TCaseStmt + TCaseBranch AST nodes; tkCase/tkOf tokens;
ParseCaseStmt (multi-value branches, optional else); AnalyseCaseStmt
(ordinal selector check); EmitCaseStmt (ceqw dispatch chain + jmp).
enum types: TEnumTypeDef AST node; TEnumTypeDesc symbol-table descriptor
(tyEnum, ByteSize=4, QBE 'w'); ParseEnumDef; AnalyseTypeDecls enum path
registers each member as skConstant with ordinal value 0..N-1; IsNumeric,
IsOrdinal, ByteSize, AllocAlign, QbeTypeOf all updated for tyEnum.
Variable alloc path handles tyEnum identically to tyInteger.
These two features unblock the self-hosting source migration: the compiler
source uses TTokenKind, TTypeKind, TBinaryOp, and TSymbolKind enums
throughout, all wrapped in case dispatch.
834 tests pass, zero Valgrind errors.
2026-04-23 15:46:06 +03:00
|
|
|
|
function NewEnumType(const AName: string): TEnumTypeDesc;
|
feat: set types — set of TEnum, set literals, in, Include/Exclude, arithmetic
Implements Pascal bit-set types end-to-end:
- Lexer: tkSet, tkIn keywords
- AST: TSetTypeDef node; boIn binary operator
- Symbol table: tySet, TSetTypeDesc (w ≤32 members, l ≤64); NewSetType factory;
Include/Exclude registered as built-in procedures
- Parser: ParseSetDef; in operator in expression parser
- Semantic: set type registration; set literal validation; boIn membership check;
set arithmetic (+/-/*) and equality (=/≠) operator analysis; Include/Exclude
argument validation
- Codegen: compile-time bitmask for set literals; shr+and for `in`; or/xor-and/and
for union/difference/intersection; OR-in-place (Include), AND-NOT-in-place (Exclude)
- Tests: 32 new tests in cp.test.sets covering parse, semantic, and codegen
- Fix: rename Set→Assign method in genericmethodimpls test (set is now reserved)
- Docs: SetType/SetLiteral grammar rules; Set Types rationale section; resolve
enum underlying type open question (confirmed w)
2026-04-28 20:07:40 +03:00
|
|
|
|
function NewSetType(const AName: string; ABase: TEnumTypeDesc): TSetTypeDesc;
|
Add pointer type infrastructure: ^T types, dereference, GetMem/FreeMem/ReallocMem
- uSymbolTable: tyPointer kind, TPointerTypeDesc(BaseType), GetMem/FreeMem/ReallocMem builtins
- uLexer/uPasTokeniser: tkCaret token for '^'; suppress ^X string escape (not in Clean Pascal)
- uParser: ParseTypeName handles '^TypeName' prefix (uses Self. for explicit recursion);
TDerefExpr for P^ postfix; TPointerWriteStmt for P^ := V lhs
- uAST: TDerefExpr, TPointerWriteStmt nodes
- uSemantic: FindTypeOrInstantiate creates TPointerTypeDesc on demand for '^T';
AnalyseDerefExpr, AnalysePointerWriteStmt; GetMem/ReallocMem/type-cast handling
- uCodeGenQBE: tyPointer→'l', EmitPointerWrite (storew/storel), TDerefExpr (loadw/loadl),
GetMem→malloc, FreeMem→free, ReallocMem→realloc, pointer arithmetic (extsw + add/sub),
type-cast via copy
- 15 new tests (556 total), all passing
2026-04-22 09:42:55 +03:00
|
|
|
|
|
2026-04-27 18:31:04 +03:00
|
|
|
|
{ Creates an open-array type descriptor for element type AElementType.
|
|
|
|
|
|
Registered in FAllTypes so the table owns the lifetime. }
|
|
|
|
|
|
function NewOpenArrayType(AElementType: TTypeDesc): TOpenArrayTypeDesc;
|
|
|
|
|
|
|
feat: static array array[L..H] of T — stack-allocated fixed-size arrays
Adds TStaticSubscriptAssign AST node, ParseTypeName extension for
array[L..H] of T syntax (tkDotDot token), TStaticArrayTypeDesc with
RawSize for correct element-footprint sizing (1 for Byte, 4 for Integer),
and full codegen: alloc4/alloc8 + memset zero-init, storeb/storew for
writes, loadub/loadw for reads, LowBound subtraction for non-zero ranges.
Also includes array literal ['a','b','c'] call-site support (step 4):
TArrayLiteralExpr, open-array formal matching, stack buffer allocation,
and codegen emitting two QBE args (data ptr + compile-time high index).
13 new static-array tests + 9 new array-literal tests; all 888 tests pass.
2026-04-27 19:22:29 +03:00
|
|
|
|
{ Creates a static array type descriptor. Registered in FAllTypes. }
|
|
|
|
|
|
function NewStaticArrayType(AElementType: TTypeDesc;
|
|
|
|
|
|
ALow, AHigh: Integer): TStaticArrayTypeDesc;
|
|
|
|
|
|
|
2026-04-22 01:02:23 +03:00
|
|
|
|
{ Generic template registry — stores TGenericTypeDef as TObject to avoid
|
|
|
|
|
|
circular unit dependency with uAST. Callers cast the result. }
|
|
|
|
|
|
procedure RegisterGeneric(const AName: string; ATempl: TObject);
|
|
|
|
|
|
function FindGeneric(const AName: string): TObject;
|
|
|
|
|
|
|
2026-04-20 19:52:09 +03:00
|
|
|
|
{ Convenience type accessors }
|
|
|
|
|
|
property TypeInteger: TTypeDesc read FTypeInteger;
|
|
|
|
|
|
property TypeInt64: TTypeDesc read FTypeInt64;
|
|
|
|
|
|
property TypeUInt32: TTypeDesc read FTypeUInt32;
|
|
|
|
|
|
property TypeByte: TTypeDesc read FTypeByte;
|
|
|
|
|
|
property TypeBoolean: TTypeDesc read FTypeBoolean;
|
|
|
|
|
|
property TypeString: TTypeDesc read FTypeString;
|
Add pointer type infrastructure: ^T types, dereference, GetMem/FreeMem/ReallocMem
- uSymbolTable: tyPointer kind, TPointerTypeDesc(BaseType), GetMem/FreeMem/ReallocMem builtins
- uLexer/uPasTokeniser: tkCaret token for '^'; suppress ^X string escape (not in Clean Pascal)
- uParser: ParseTypeName handles '^TypeName' prefix (uses Self. for explicit recursion);
TDerefExpr for P^ postfix; TPointerWriteStmt for P^ := V lhs
- uAST: TDerefExpr, TPointerWriteStmt nodes
- uSemantic: FindTypeOrInstantiate creates TPointerTypeDesc on demand for '^T';
AnalyseDerefExpr, AnalysePointerWriteStmt; GetMem/ReallocMem/type-cast handling
- uCodeGenQBE: tyPointer→'l', EmitPointerWrite (storew/storel), TDerefExpr (loadw/loadl),
GetMem→malloc, FreeMem→free, ReallocMem→realloc, pointer arithmetic (extsw + add/sub),
type-cast via copy
- 15 new tests (556 total), all passing
2026-04-22 09:42:55 +03:00
|
|
|
|
property TypeVoid: TTypeDesc read FTypeVoid;
|
|
|
|
|
|
property TypeNil: TTypeDesc read FTypeNil;
|
|
|
|
|
|
property TypePointer: TPointerTypeDesc read FTypePointer;
|
2026-04-28 15:20:43 +03:00
|
|
|
|
property TypePChar: TTypeDesc read FTypePChar;
|
2026-04-20 19:52:09 +03:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
implementation
|
|
|
|
|
|
|
|
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
{ TTypeDesc }
|
|
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
|
|
|
|
|
|
function TTypeDesc.IsNumeric: Boolean;
|
|
|
|
|
|
begin
|
Add case statements and enum types — required for self-hosting migration
case statement: TCaseStmt + TCaseBranch AST nodes; tkCase/tkOf tokens;
ParseCaseStmt (multi-value branches, optional else); AnalyseCaseStmt
(ordinal selector check); EmitCaseStmt (ceqw dispatch chain + jmp).
enum types: TEnumTypeDef AST node; TEnumTypeDesc symbol-table descriptor
(tyEnum, ByteSize=4, QBE 'w'); ParseEnumDef; AnalyseTypeDecls enum path
registers each member as skConstant with ordinal value 0..N-1; IsNumeric,
IsOrdinal, ByteSize, AllocAlign, QbeTypeOf all updated for tyEnum.
Variable alloc path handles tyEnum identically to tyInteger.
These two features unblock the self-hosting source migration: the compiler
source uses TTokenKind, TTypeKind, TBinaryOp, and TSymbolKind enums
throughout, all wrapped in case dispatch.
834 tests pass, zero Valgrind errors.
2026-04-23 15:46:06 +03:00
|
|
|
|
Result := Kind in [tyInteger, tyInt64, tyUInt32, tyByte, tyEnum];
|
2026-04-20 19:52:09 +03:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TTypeDesc.IsString: Boolean;
|
|
|
|
|
|
begin
|
|
|
|
|
|
Result := Kind = tyString;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TTypeDesc.IsOrdinal: Boolean;
|
|
|
|
|
|
begin
|
Add case statements and enum types — required for self-hosting migration
case statement: TCaseStmt + TCaseBranch AST nodes; tkCase/tkOf tokens;
ParseCaseStmt (multi-value branches, optional else); AnalyseCaseStmt
(ordinal selector check); EmitCaseStmt (ceqw dispatch chain + jmp).
enum types: TEnumTypeDef AST node; TEnumTypeDesc symbol-table descriptor
(tyEnum, ByteSize=4, QBE 'w'); ParseEnumDef; AnalyseTypeDecls enum path
registers each member as skConstant with ordinal value 0..N-1; IsNumeric,
IsOrdinal, ByteSize, AllocAlign, QbeTypeOf all updated for tyEnum.
Variable alloc path handles tyEnum identically to tyInteger.
These two features unblock the self-hosting source migration: the compiler
source uses TTokenKind, TTypeKind, TBinaryOp, and TSymbolKind enums
throughout, all wrapped in case dispatch.
834 tests pass, zero Valgrind errors.
2026-04-23 15:46:06 +03:00
|
|
|
|
Result := Kind in [tyInteger, tyInt64, tyUInt32, tyByte, tyBoolean, tyEnum];
|
2026-04-20 19:52:09 +03:00
|
|
|
|
end;
|
|
|
|
|
|
|
Add Phase 2: record/class types, ARC strings, RTL stubs, and grammar doc
Symbol table:
- Added tyClass kind and NewClassType factory; TRecordTypeDesc now
accepts an optional kind parameter so records and classes share the
same descriptor with distinct semantics.
Semantic analyser:
- AnalyseTypeDecls runs before PushScope so type symbols land in global
scope and survive PopScope.
- Handles TRecordTypeDef and TClassTypeDef; sets IsConstructorCall on
TypeName.Create expressions and IsClassAccess on class-variable field
access and assignment nodes.
Code generator (QBE IR):
- Class variables: 8-byte pointer slot, zeroed on entry.
- Constructor calls: malloc(sizeof fields), store pointer.
- Class field access/write: load pointer, add field offset, load/store.
- ARC string assignment: AddRef new value, Release old value, then store.
- Block exit: Release every string variable in scope (EmitStringCleanup).
Lexer / Parser / AST:
- Added tkClass keyword and ParseClassDef; FieldDecl parsing refactored
into ParseFieldDecl(AFields) shared by both record and class.
- AST gains TClassTypeDef, and IsConstructorCall/IsClassAccess flags on
TFieldAccessExpr and TFieldAssignment.
RTL:
- blaise_arc.c: Phase 2 no-op stubs for _StringAddRef / _StringRelease.
- rtl/Makefile: builds blaise_rtl.a; `make install` copies it next to
the compiler binary for automatic discovery by FindRTL.
- Blaise.pas driver: FindRTL checks BLAISE_RTL env var then binary dir;
links the RTL archive when present.
Tests:
- 180 unit tests (records ×23, classes ×22, ARC ×9, codegen ×9, …).
- 4 end-to-end integration tests in tests/integration/test_arc_strings.sh
covering string assignment, two-var cleanup, reassignment cycle, and
empty-program linkage.
Docs:
- design.adoc updated with Phase 2 implementation status table.
- docs/grammar.ebnf: new authoritative EBNF grammar for the Blaise
language as implemented, including ARC semantic annotations.
2026-04-20 21:04:12 +03:00
|
|
|
|
function TTypeDesc.IsRecord: Boolean;
|
|
|
|
|
|
begin
|
|
|
|
|
|
Result := Kind = tyRecord;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
feat: static array array[L..H] of T — stack-allocated fixed-size arrays
Adds TStaticSubscriptAssign AST node, ParseTypeName extension for
array[L..H] of T syntax (tkDotDot token), TStaticArrayTypeDesc with
RawSize for correct element-footprint sizing (1 for Byte, 4 for Integer),
and full codegen: alloc4/alloc8 + memset zero-init, storeb/storew for
writes, loadub/loadw for reads, LowBound subtraction for non-zero ranges.
Also includes array literal ['a','b','c'] call-site support (step 4):
TArrayLiteralExpr, open-array formal matching, stack buffer allocation,
and codegen emitting two QBE args (data ptr + compile-time high index).
13 new static-array tests + 9 new array-literal tests; all 888 tests pass.
2026-04-27 19:22:29 +03:00
|
|
|
|
function TTypeDesc.RawSize: Integer;
|
|
|
|
|
|
begin
|
|
|
|
|
|
case Kind of
|
|
|
|
|
|
tyByte, tyBoolean: Result := 1;
|
|
|
|
|
|
tyInteger, tyUInt32, tyEnum: Result := 4;
|
|
|
|
|
|
tyInt64, tyString, tyClass, tyPointer, tyNil: Result := 8;
|
|
|
|
|
|
tyRecord: Result := TRecordTypeDesc(Self).TotalSize;
|
feat: set types — set of TEnum, set literals, in, Include/Exclude, arithmetic
Implements Pascal bit-set types end-to-end:
- Lexer: tkSet, tkIn keywords
- AST: TSetTypeDef node; boIn binary operator
- Symbol table: tySet, TSetTypeDesc (w ≤32 members, l ≤64); NewSetType factory;
Include/Exclude registered as built-in procedures
- Parser: ParseSetDef; in operator in expression parser
- Semantic: set type registration; set literal validation; boIn membership check;
set arithmetic (+/-/*) and equality (=/≠) operator analysis; Include/Exclude
argument validation
- Codegen: compile-time bitmask for set literals; shr+and for `in`; or/xor-and/and
for union/difference/intersection; OR-in-place (Include), AND-NOT-in-place (Exclude)
- Tests: 32 new tests in cp.test.sets covering parse, semantic, and codegen
- Fix: rename Set→Assign method in genericmethodimpls test (set is now reserved)
- Docs: SetType/SetLiteral grammar rules; Set Types rationale section; resolve
enum underlying type open question (confirmed w)
2026-04-28 20:07:40 +03:00
|
|
|
|
tySet: if TSetTypeDesc(Self).BitCount <= 32 then Result := 4 else Result := 8;
|
feat: static array array[L..H] of T — stack-allocated fixed-size arrays
Adds TStaticSubscriptAssign AST node, ParseTypeName extension for
array[L..H] of T syntax (tkDotDot token), TStaticArrayTypeDesc with
RawSize for correct element-footprint sizing (1 for Byte, 4 for Integer),
and full codegen: alloc4/alloc8 + memset zero-init, storeb/storew for
writes, loadub/loadw for reads, LowBound subtraction for non-zero ranges.
Also includes array literal ['a','b','c'] call-site support (step 4):
TArrayLiteralExpr, open-array formal matching, stack buffer allocation,
and codegen emitting two QBE args (data ptr + compile-time high index).
13 new static-array tests + 9 new array-literal tests; all 888 tests pass.
2026-04-27 19:22:29 +03:00
|
|
|
|
tyStaticArray:
|
|
|
|
|
|
Result := (TStaticArrayTypeDesc(Self).HighBound -
|
|
|
|
|
|
TStaticArrayTypeDesc(Self).LowBound + 1) *
|
|
|
|
|
|
TStaticArrayTypeDesc(Self).ElementType.RawSize;
|
|
|
|
|
|
else
|
|
|
|
|
|
Result := 8;
|
|
|
|
|
|
end;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
Add Phase 2: record/class types, ARC strings, RTL stubs, and grammar doc
Symbol table:
- Added tyClass kind and NewClassType factory; TRecordTypeDesc now
accepts an optional kind parameter so records and classes share the
same descriptor with distinct semantics.
Semantic analyser:
- AnalyseTypeDecls runs before PushScope so type symbols land in global
scope and survive PopScope.
- Handles TRecordTypeDef and TClassTypeDef; sets IsConstructorCall on
TypeName.Create expressions and IsClassAccess on class-variable field
access and assignment nodes.
Code generator (QBE IR):
- Class variables: 8-byte pointer slot, zeroed on entry.
- Constructor calls: malloc(sizeof fields), store pointer.
- Class field access/write: load pointer, add field offset, load/store.
- ARC string assignment: AddRef new value, Release old value, then store.
- Block exit: Release every string variable in scope (EmitStringCleanup).
Lexer / Parser / AST:
- Added tkClass keyword and ParseClassDef; FieldDecl parsing refactored
into ParseFieldDecl(AFields) shared by both record and class.
- AST gains TClassTypeDef, and IsConstructorCall/IsClassAccess flags on
TFieldAccessExpr and TFieldAssignment.
RTL:
- blaise_arc.c: Phase 2 no-op stubs for _StringAddRef / _StringRelease.
- rtl/Makefile: builds blaise_rtl.a; `make install` copies it next to
the compiler binary for automatic discovery by FindRTL.
- Blaise.pas driver: FindRTL checks BLAISE_RTL env var then binary dir;
links the RTL archive when present.
Tests:
- 180 unit tests (records ×23, classes ×22, ARC ×9, codegen ×9, …).
- 4 end-to-end integration tests in tests/integration/test_arc_strings.sh
covering string assignment, two-var cleanup, reassignment cycle, and
empty-program linkage.
Docs:
- design.adoc updated with Phase 2 implementation status table.
- docs/grammar.ebnf: new authoritative EBNF grammar for the Blaise
language as implemented, including ARC semantic annotations.
2026-04-20 21:04:12 +03:00
|
|
|
|
function TTypeDesc.ByteSize: Integer;
|
|
|
|
|
|
begin
|
|
|
|
|
|
case Kind of
|
Add case statements and enum types — required for self-hosting migration
case statement: TCaseStmt + TCaseBranch AST nodes; tkCase/tkOf tokens;
ParseCaseStmt (multi-value branches, optional else); AnalyseCaseStmt
(ordinal selector check); EmitCaseStmt (ceqw dispatch chain + jmp).
enum types: TEnumTypeDef AST node; TEnumTypeDesc symbol-table descriptor
(tyEnum, ByteSize=4, QBE 'w'); ParseEnumDef; AnalyseTypeDecls enum path
registers each member as skConstant with ordinal value 0..N-1; IsNumeric,
IsOrdinal, ByteSize, AllocAlign, QbeTypeOf all updated for tyEnum.
Variable alloc path handles tyEnum identically to tyInteger.
These two features unblock the self-hosting source migration: the compiler
source uses TTokenKind, TTypeKind, TBinaryOp, and TSymbolKind enums
throughout, all wrapped in case dispatch.
834 tests pass, zero Valgrind errors.
2026-04-23 15:46:06 +03:00
|
|
|
|
tyInteger, tyUInt32, tyEnum: Result := 4;
|
Add Phase 2: record/class types, ARC strings, RTL stubs, and grammar doc
Symbol table:
- Added tyClass kind and NewClassType factory; TRecordTypeDesc now
accepts an optional kind parameter so records and classes share the
same descriptor with distinct semantics.
Semantic analyser:
- AnalyseTypeDecls runs before PushScope so type symbols land in global
scope and survive PopScope.
- Handles TRecordTypeDef and TClassTypeDef; sets IsConstructorCall on
TypeName.Create expressions and IsClassAccess on class-variable field
access and assignment nodes.
Code generator (QBE IR):
- Class variables: 8-byte pointer slot, zeroed on entry.
- Constructor calls: malloc(sizeof fields), store pointer.
- Class field access/write: load pointer, add field offset, load/store.
- ARC string assignment: AddRef new value, Release old value, then store.
- Block exit: Release every string variable in scope (EmitStringCleanup).
Lexer / Parser / AST:
- Added tkClass keyword and ParseClassDef; FieldDecl parsing refactored
into ParseFieldDecl(AFields) shared by both record and class.
- AST gains TClassTypeDef, and IsConstructorCall/IsClassAccess flags on
TFieldAccessExpr and TFieldAssignment.
RTL:
- blaise_arc.c: Phase 2 no-op stubs for _StringAddRef / _StringRelease.
- rtl/Makefile: builds blaise_rtl.a; `make install` copies it next to
the compiler binary for automatic discovery by FindRTL.
- Blaise.pas driver: FindRTL checks BLAISE_RTL env var then binary dir;
links the RTL archive when present.
Tests:
- 180 unit tests (records ×23, classes ×22, ARC ×9, codegen ×9, …).
- 4 end-to-end integration tests in tests/integration/test_arc_strings.sh
covering string assignment, two-var cleanup, reassignment cycle, and
empty-program linkage.
Docs:
- design.adoc updated with Phase 2 implementation status table.
- docs/grammar.ebnf: new authoritative EBNF grammar for the Blaise
language as implemented, including ARC semantic annotations.
2026-04-20 21:04:12 +03:00
|
|
|
|
tyInt64: Result := 8;
|
2026-04-24 02:21:43 +03:00
|
|
|
|
tyByte, tyBoolean: Result := 4; { stored as word, same as AllocAlign }
|
Add Phase 2: record/class types, ARC strings, RTL stubs, and grammar doc
Symbol table:
- Added tyClass kind and NewClassType factory; TRecordTypeDesc now
accepts an optional kind parameter so records and classes share the
same descriptor with distinct semantics.
Semantic analyser:
- AnalyseTypeDecls runs before PushScope so type symbols land in global
scope and survive PopScope.
- Handles TRecordTypeDef and TClassTypeDef; sets IsConstructorCall on
TypeName.Create expressions and IsClassAccess on class-variable field
access and assignment nodes.
Code generator (QBE IR):
- Class variables: 8-byte pointer slot, zeroed on entry.
- Constructor calls: malloc(sizeof fields), store pointer.
- Class field access/write: load pointer, add field offset, load/store.
- ARC string assignment: AddRef new value, Release old value, then store.
- Block exit: Release every string variable in scope (EmitStringCleanup).
Lexer / Parser / AST:
- Added tkClass keyword and ParseClassDef; FieldDecl parsing refactored
into ParseFieldDecl(AFields) shared by both record and class.
- AST gains TClassTypeDef, and IsConstructorCall/IsClassAccess flags on
TFieldAccessExpr and TFieldAssignment.
RTL:
- blaise_arc.c: Phase 2 no-op stubs for _StringAddRef / _StringRelease.
- rtl/Makefile: builds blaise_rtl.a; `make install` copies it next to
the compiler binary for automatic discovery by FindRTL.
- Blaise.pas driver: FindRTL checks BLAISE_RTL env var then binary dir;
links the RTL archive when present.
Tests:
- 180 unit tests (records ×23, classes ×22, ARC ×9, codegen ×9, …).
- 4 end-to-end integration tests in tests/integration/test_arc_strings.sh
covering string assignment, two-var cleanup, reassignment cycle, and
empty-program linkage.
Docs:
- design.adoc updated with Phase 2 implementation status table.
- docs/grammar.ebnf: new authoritative EBNF grammar for the Blaise
language as implemented, including ARC semantic annotations.
2026-04-20 21:04:12 +03:00
|
|
|
|
tyString: Result := 8; { pointer size on 64-bit }
|
|
|
|
|
|
tyRecord: Result := TRecordTypeDesc(Self).TotalSize;
|
2026-04-21 00:41:28 +03:00
|
|
|
|
tyNil: Result := 8;
|
feat: set types — set of TEnum, set literals, in, Include/Exclude, arithmetic
Implements Pascal bit-set types end-to-end:
- Lexer: tkSet, tkIn keywords
- AST: TSetTypeDef node; boIn binary operator
- Symbol table: tySet, TSetTypeDesc (w ≤32 members, l ≤64); NewSetType factory;
Include/Exclude registered as built-in procedures
- Parser: ParseSetDef; in operator in expression parser
- Semantic: set type registration; set literal validation; boIn membership check;
set arithmetic (+/-/*) and equality (=/≠) operator analysis; Include/Exclude
argument validation
- Codegen: compile-time bitmask for set literals; shr+and for `in`; or/xor-and/and
for union/difference/intersection; OR-in-place (Include), AND-NOT-in-place (Exclude)
- Tests: 32 new tests in cp.test.sets covering parse, semantic, and codegen
- Fix: rename Set→Assign method in genericmethodimpls test (set is now reserved)
- Docs: SetType/SetLiteral grammar rules; Set Types rationale section; resolve
enum underlying type open question (confirmed w)
2026-04-28 20:07:40 +03:00
|
|
|
|
tySet: if TSetTypeDesc(Self).BitCount <= 32 then Result := 4 else Result := 8;
|
feat: static array array[L..H] of T — stack-allocated fixed-size arrays
Adds TStaticSubscriptAssign AST node, ParseTypeName extension for
array[L..H] of T syntax (tkDotDot token), TStaticArrayTypeDesc with
RawSize for correct element-footprint sizing (1 for Byte, 4 for Integer),
and full codegen: alloc4/alloc8 + memset zero-init, storeb/storew for
writes, loadub/loadw for reads, LowBound subtraction for non-zero ranges.
Also includes array literal ['a','b','c'] call-site support (step 4):
TArrayLiteralExpr, open-array formal matching, stack buffer allocation,
and codegen emitting two QBE args (data ptr + compile-time high index).
13 new static-array tests + 9 new array-literal tests; all 888 tests pass.
2026-04-27 19:22:29 +03:00
|
|
|
|
tyStaticArray:
|
|
|
|
|
|
Result := (TStaticArrayTypeDesc(Self).HighBound -
|
|
|
|
|
|
TStaticArrayTypeDesc(Self).LowBound + 1) *
|
|
|
|
|
|
TStaticArrayTypeDesc(Self).ElementType.RawSize;
|
Add Phase 2: record/class types, ARC strings, RTL stubs, and grammar doc
Symbol table:
- Added tyClass kind and NewClassType factory; TRecordTypeDesc now
accepts an optional kind parameter so records and classes share the
same descriptor with distinct semantics.
Semantic analyser:
- AnalyseTypeDecls runs before PushScope so type symbols land in global
scope and survive PopScope.
- Handles TRecordTypeDef and TClassTypeDef; sets IsConstructorCall on
TypeName.Create expressions and IsClassAccess on class-variable field
access and assignment nodes.
Code generator (QBE IR):
- Class variables: 8-byte pointer slot, zeroed on entry.
- Constructor calls: malloc(sizeof fields), store pointer.
- Class field access/write: load pointer, add field offset, load/store.
- ARC string assignment: AddRef new value, Release old value, then store.
- Block exit: Release every string variable in scope (EmitStringCleanup).
Lexer / Parser / AST:
- Added tkClass keyword and ParseClassDef; FieldDecl parsing refactored
into ParseFieldDecl(AFields) shared by both record and class.
- AST gains TClassTypeDef, and IsConstructorCall/IsClassAccess flags on
TFieldAccessExpr and TFieldAssignment.
RTL:
- blaise_arc.c: Phase 2 no-op stubs for _StringAddRef / _StringRelease.
- rtl/Makefile: builds blaise_rtl.a; `make install` copies it next to
the compiler binary for automatic discovery by FindRTL.
- Blaise.pas driver: FindRTL checks BLAISE_RTL env var then binary dir;
links the RTL archive when present.
Tests:
- 180 unit tests (records ×23, classes ×22, ARC ×9, codegen ×9, …).
- 4 end-to-end integration tests in tests/integration/test_arc_strings.sh
covering string assignment, two-var cleanup, reassignment cycle, and
empty-program linkage.
Docs:
- design.adoc updated with Phase 2 implementation status table.
- docs/grammar.ebnf: new authoritative EBNF grammar for the Blaise
language as implemented, including ARC semantic annotations.
2026-04-20 21:04:12 +03:00
|
|
|
|
else
|
|
|
|
|
|
Result := 8;
|
|
|
|
|
|
end;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TTypeDesc.AllocAlign: Integer;
|
|
|
|
|
|
begin
|
|
|
|
|
|
case Kind of
|
Add case statements and enum types — required for self-hosting migration
case statement: TCaseStmt + TCaseBranch AST nodes; tkCase/tkOf tokens;
ParseCaseStmt (multi-value branches, optional else); AnalyseCaseStmt
(ordinal selector check); EmitCaseStmt (ceqw dispatch chain + jmp).
enum types: TEnumTypeDef AST node; TEnumTypeDesc symbol-table descriptor
(tyEnum, ByteSize=4, QBE 'w'); ParseEnumDef; AnalyseTypeDecls enum path
registers each member as skConstant with ordinal value 0..N-1; IsNumeric,
IsOrdinal, ByteSize, AllocAlign, QbeTypeOf all updated for tyEnum.
Variable alloc path handles tyEnum identically to tyInteger.
These two features unblock the self-hosting source migration: the compiler
source uses TTokenKind, TTypeKind, TBinaryOp, and TSymbolKind enums
throughout, all wrapped in case dispatch.
834 tests pass, zero Valgrind errors.
2026-04-23 15:46:06 +03:00
|
|
|
|
tyInteger, tyUInt32, tyEnum: Result := 4;
|
Add Phase 2: record/class types, ARC strings, RTL stubs, and grammar doc
Symbol table:
- Added tyClass kind and NewClassType factory; TRecordTypeDesc now
accepts an optional kind parameter so records and classes share the
same descriptor with distinct semantics.
Semantic analyser:
- AnalyseTypeDecls runs before PushScope so type symbols land in global
scope and survive PopScope.
- Handles TRecordTypeDef and TClassTypeDef; sets IsConstructorCall on
TypeName.Create expressions and IsClassAccess on class-variable field
access and assignment nodes.
Code generator (QBE IR):
- Class variables: 8-byte pointer slot, zeroed on entry.
- Constructor calls: malloc(sizeof fields), store pointer.
- Class field access/write: load pointer, add field offset, load/store.
- ARC string assignment: AddRef new value, Release old value, then store.
- Block exit: Release every string variable in scope (EmitStringCleanup).
Lexer / Parser / AST:
- Added tkClass keyword and ParseClassDef; FieldDecl parsing refactored
into ParseFieldDecl(AFields) shared by both record and class.
- AST gains TClassTypeDef, and IsConstructorCall/IsClassAccess flags on
TFieldAccessExpr and TFieldAssignment.
RTL:
- blaise_arc.c: Phase 2 no-op stubs for _StringAddRef / _StringRelease.
- rtl/Makefile: builds blaise_rtl.a; `make install` copies it next to
the compiler binary for automatic discovery by FindRTL.
- Blaise.pas driver: FindRTL checks BLAISE_RTL env var then binary dir;
links the RTL archive when present.
Tests:
- 180 unit tests (records ×23, classes ×22, ARC ×9, codegen ×9, …).
- 4 end-to-end integration tests in tests/integration/test_arc_strings.sh
covering string assignment, two-var cleanup, reassignment cycle, and
empty-program linkage.
Docs:
- design.adoc updated with Phase 2 implementation status table.
- docs/grammar.ebnf: new authoritative EBNF grammar for the Blaise
language as implemented, including ARC semantic annotations.
2026-04-20 21:04:12 +03:00
|
|
|
|
tyByte, tyBoolean: Result := 4; { round up to word boundary }
|
|
|
|
|
|
tyInt64, tyString: Result := 8;
|
|
|
|
|
|
tyRecord: Result := TRecordTypeDesc(Self).MaxAlign;
|
feat: set types — set of TEnum, set literals, in, Include/Exclude, arithmetic
Implements Pascal bit-set types end-to-end:
- Lexer: tkSet, tkIn keywords
- AST: TSetTypeDef node; boIn binary operator
- Symbol table: tySet, TSetTypeDesc (w ≤32 members, l ≤64); NewSetType factory;
Include/Exclude registered as built-in procedures
- Parser: ParseSetDef; in operator in expression parser
- Semantic: set type registration; set literal validation; boIn membership check;
set arithmetic (+/-/*) and equality (=/≠) operator analysis; Include/Exclude
argument validation
- Codegen: compile-time bitmask for set literals; shr+and for `in`; or/xor-and/and
for union/difference/intersection; OR-in-place (Include), AND-NOT-in-place (Exclude)
- Tests: 32 new tests in cp.test.sets covering parse, semantic, and codegen
- Fix: rename Set→Assign method in genericmethodimpls test (set is now reserved)
- Docs: SetType/SetLiteral grammar rules; Set Types rationale section; resolve
enum underlying type open question (confirmed w)
2026-04-28 20:07:40 +03:00
|
|
|
|
tySet: if TSetTypeDesc(Self).BitCount <= 32 then Result := 4 else Result := 8;
|
feat: static array array[L..H] of T — stack-allocated fixed-size arrays
Adds TStaticSubscriptAssign AST node, ParseTypeName extension for
array[L..H] of T syntax (tkDotDot token), TStaticArrayTypeDesc with
RawSize for correct element-footprint sizing (1 for Byte, 4 for Integer),
and full codegen: alloc4/alloc8 + memset zero-init, storeb/storew for
writes, loadub/loadw for reads, LowBound subtraction for non-zero ranges.
Also includes array literal ['a','b','c'] call-site support (step 4):
TArrayLiteralExpr, open-array formal matching, stack buffer allocation,
and codegen emitting two QBE args (data ptr + compile-time high index).
13 new static-array tests + 9 new array-literal tests; all 888 tests pass.
2026-04-27 19:22:29 +03:00
|
|
|
|
tyStaticArray: Result := TStaticArrayTypeDesc(Self).ElementType.AllocAlign;
|
Add Phase 2: record/class types, ARC strings, RTL stubs, and grammar doc
Symbol table:
- Added tyClass kind and NewClassType factory; TRecordTypeDesc now
accepts an optional kind parameter so records and classes share the
same descriptor with distinct semantics.
Semantic analyser:
- AnalyseTypeDecls runs before PushScope so type symbols land in global
scope and survive PopScope.
- Handles TRecordTypeDef and TClassTypeDef; sets IsConstructorCall on
TypeName.Create expressions and IsClassAccess on class-variable field
access and assignment nodes.
Code generator (QBE IR):
- Class variables: 8-byte pointer slot, zeroed on entry.
- Constructor calls: malloc(sizeof fields), store pointer.
- Class field access/write: load pointer, add field offset, load/store.
- ARC string assignment: AddRef new value, Release old value, then store.
- Block exit: Release every string variable in scope (EmitStringCleanup).
Lexer / Parser / AST:
- Added tkClass keyword and ParseClassDef; FieldDecl parsing refactored
into ParseFieldDecl(AFields) shared by both record and class.
- AST gains TClassTypeDef, and IsConstructorCall/IsClassAccess flags on
TFieldAccessExpr and TFieldAssignment.
RTL:
- blaise_arc.c: Phase 2 no-op stubs for _StringAddRef / _StringRelease.
- rtl/Makefile: builds blaise_rtl.a; `make install` copies it next to
the compiler binary for automatic discovery by FindRTL.
- Blaise.pas driver: FindRTL checks BLAISE_RTL env var then binary dir;
links the RTL archive when present.
Tests:
- 180 unit tests (records ×23, classes ×22, ARC ×9, codegen ×9, …).
- 4 end-to-end integration tests in tests/integration/test_arc_strings.sh
covering string assignment, two-var cleanup, reassignment cycle, and
empty-program linkage.
Docs:
- design.adoc updated with Phase 2 implementation status table.
- docs/grammar.ebnf: new authoritative EBNF grammar for the Blaise
language as implemented, including ARC semantic annotations.
2026-04-20 21:04:12 +03:00
|
|
|
|
else
|
|
|
|
|
|
Result := 8;
|
|
|
|
|
|
end;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
{ TRecordTypeDesc }
|
|
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
|
feat(compiler): self-hosting fixpoint reached — Step 10
Parser, lexer, tokeniser, semantic analyser and symbol table extended
to compile the multi-file Blaise source with the Blaise compiler itself.
Stage-2 and stage-3 IR are byte-identical; fixpoint confirmed.
Changes to reach fixpoint:
- uLexer: source filename parameter for actionable parse error messages
- uParser: method directive loop (inline/stdcall/abstract/…), unit
interface/implementation loops for var/const/type blocks, chained
field-assignment (A.B.C := v), filename in all EParseError messages
- uPasTokeniser: full rewrite to remove FPC-only features (char sets,
shr, Exit(value), private/public visibility) so Blaise can compile it
- uSymbolTable: remove unsupported default parameter values
- uSemantic: replace Exit(value) with Result+Exit; expand five `with`
statement blocks to explicit typed local variables
- uAST, uCodeGenQBE: minor additions supporting the above
- uUnitLoader: pass filename to TLexer.Create
- classes.pas: inline dupIgnore literal (const section deferred)
2026-04-30 15:30:30 +03:00
|
|
|
|
constructor TRecordTypeDesc.Create(const AName: string; AKind: TTypeKind);
|
Add Phase 2: record/class types, ARC strings, RTL stubs, and grammar doc
Symbol table:
- Added tyClass kind and NewClassType factory; TRecordTypeDesc now
accepts an optional kind parameter so records and classes share the
same descriptor with distinct semantics.
Semantic analyser:
- AnalyseTypeDecls runs before PushScope so type symbols land in global
scope and survive PopScope.
- Handles TRecordTypeDef and TClassTypeDef; sets IsConstructorCall on
TypeName.Create expressions and IsClassAccess on class-variable field
access and assignment nodes.
Code generator (QBE IR):
- Class variables: 8-byte pointer slot, zeroed on entry.
- Constructor calls: malloc(sizeof fields), store pointer.
- Class field access/write: load pointer, add field offset, load/store.
- ARC string assignment: AddRef new value, Release old value, then store.
- Block exit: Release every string variable in scope (EmitStringCleanup).
Lexer / Parser / AST:
- Added tkClass keyword and ParseClassDef; FieldDecl parsing refactored
into ParseFieldDecl(AFields) shared by both record and class.
- AST gains TClassTypeDef, and IsConstructorCall/IsClassAccess flags on
TFieldAccessExpr and TFieldAssignment.
RTL:
- blaise_arc.c: Phase 2 no-op stubs for _StringAddRef / _StringRelease.
- rtl/Makefile: builds blaise_rtl.a; `make install` copies it next to
the compiler binary for automatic discovery by FindRTL.
- Blaise.pas driver: FindRTL checks BLAISE_RTL env var then binary dir;
links the RTL archive when present.
Tests:
- 180 unit tests (records ×23, classes ×22, ARC ×9, codegen ×9, …).
- 4 end-to-end integration tests in tests/integration/test_arc_strings.sh
covering string assignment, two-var cleanup, reassignment cycle, and
empty-program linkage.
Docs:
- design.adoc updated with Phase 2 implementation status table.
- docs/grammar.ebnf: new authoritative EBNF grammar for the Blaise
language as implemented, including ARC semantic annotations.
2026-04-20 21:04:12 +03:00
|
|
|
|
begin
|
|
|
|
|
|
inherited Create;
|
2026-04-21 20:24:55 +03:00
|
|
|
|
Kind := AKind;
|
|
|
|
|
|
Name := AName;
|
|
|
|
|
|
FFields := TObjectList.Create(True);
|
|
|
|
|
|
FKeys := TStringList.Create;
|
Add Phase 2: record/class types, ARC strings, RTL stubs, and grammar doc
Symbol table:
- Added tyClass kind and NewClassType factory; TRecordTypeDesc now
accepts an optional kind parameter so records and classes share the
same descriptor with distinct semantics.
Semantic analyser:
- AnalyseTypeDecls runs before PushScope so type symbols land in global
scope and survive PopScope.
- Handles TRecordTypeDef and TClassTypeDef; sets IsConstructorCall on
TypeName.Create expressions and IsClassAccess on class-variable field
access and assignment nodes.
Code generator (QBE IR):
- Class variables: 8-byte pointer slot, zeroed on entry.
- Constructor calls: malloc(sizeof fields), store pointer.
- Class field access/write: load pointer, add field offset, load/store.
- ARC string assignment: AddRef new value, Release old value, then store.
- Block exit: Release every string variable in scope (EmitStringCleanup).
Lexer / Parser / AST:
- Added tkClass keyword and ParseClassDef; FieldDecl parsing refactored
into ParseFieldDecl(AFields) shared by both record and class.
- AST gains TClassTypeDef, and IsConstructorCall/IsClassAccess flags on
TFieldAccessExpr and TFieldAssignment.
RTL:
- blaise_arc.c: Phase 2 no-op stubs for _StringAddRef / _StringRelease.
- rtl/Makefile: builds blaise_rtl.a; `make install` copies it next to
the compiler binary for automatic discovery by FindRTL.
- Blaise.pas driver: FindRTL checks BLAISE_RTL env var then binary dir;
links the RTL archive when present.
Tests:
- 180 unit tests (records ×23, classes ×22, ARC ×9, codegen ×9, …).
- 4 end-to-end integration tests in tests/integration/test_arc_strings.sh
covering string assignment, two-var cleanup, reassignment cycle, and
empty-program linkage.
Docs:
- design.adoc updated with Phase 2 implementation status table.
- docs/grammar.ebnf: new authoritative EBNF grammar for the Blaise
language as implemented, including ARC semantic annotations.
2026-04-20 21:04:12 +03:00
|
|
|
|
FKeys.Sorted := True;
|
|
|
|
|
|
FKeys.CaseSensitive := False;
|
|
|
|
|
|
FKeys.Duplicates := dupIgnore;
|
2026-04-21 20:24:55 +03:00
|
|
|
|
FVTable := nil; { allocated on first use }
|
|
|
|
|
|
FImplements := TObjectList.Create(False); { not owned }
|
2026-04-22 01:15:15 +03:00
|
|
|
|
FProperties := TObjectList.Create(True); { owned TPropertyInfo }
|
Add Phase 2: record/class types, ARC strings, RTL stubs, and grammar doc
Symbol table:
- Added tyClass kind and NewClassType factory; TRecordTypeDesc now
accepts an optional kind parameter so records and classes share the
same descriptor with distinct semantics.
Semantic analyser:
- AnalyseTypeDecls runs before PushScope so type symbols land in global
scope and survive PopScope.
- Handles TRecordTypeDef and TClassTypeDef; sets IsConstructorCall on
TypeName.Create expressions and IsClassAccess on class-variable field
access and assignment nodes.
Code generator (QBE IR):
- Class variables: 8-byte pointer slot, zeroed on entry.
- Constructor calls: malloc(sizeof fields), store pointer.
- Class field access/write: load pointer, add field offset, load/store.
- ARC string assignment: AddRef new value, Release old value, then store.
- Block exit: Release every string variable in scope (EmitStringCleanup).
Lexer / Parser / AST:
- Added tkClass keyword and ParseClassDef; FieldDecl parsing refactored
into ParseFieldDecl(AFields) shared by both record and class.
- AST gains TClassTypeDef, and IsConstructorCall/IsClassAccess flags on
TFieldAccessExpr and TFieldAssignment.
RTL:
- blaise_arc.c: Phase 2 no-op stubs for _StringAddRef / _StringRelease.
- rtl/Makefile: builds blaise_rtl.a; `make install` copies it next to
the compiler binary for automatic discovery by FindRTL.
- Blaise.pas driver: FindRTL checks BLAISE_RTL env var then binary dir;
links the RTL archive when present.
Tests:
- 180 unit tests (records ×23, classes ×22, ARC ×9, codegen ×9, …).
- 4 end-to-end integration tests in tests/integration/test_arc_strings.sh
covering string assignment, two-var cleanup, reassignment cycle, and
empty-program linkage.
Docs:
- design.adoc updated with Phase 2 implementation status table.
- docs/grammar.ebnf: new authoritative EBNF grammar for the Blaise
language as implemented, including ARC semantic annotations.
2026-04-20 21:04:12 +03:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
destructor TRecordTypeDesc.Destroy;
|
|
|
|
|
|
begin
|
2026-04-22 01:15:15 +03:00
|
|
|
|
FProperties.Free;
|
2026-04-21 20:24:55 +03:00
|
|
|
|
FImplements.Free;
|
Add Phase 2: record/class types, ARC strings, RTL stubs, and grammar doc
Symbol table:
- Added tyClass kind and NewClassType factory; TRecordTypeDesc now
accepts an optional kind parameter so records and classes share the
same descriptor with distinct semantics.
Semantic analyser:
- AnalyseTypeDecls runs before PushScope so type symbols land in global
scope and survive PopScope.
- Handles TRecordTypeDef and TClassTypeDef; sets IsConstructorCall on
TypeName.Create expressions and IsClassAccess on class-variable field
access and assignment nodes.
Code generator (QBE IR):
- Class variables: 8-byte pointer slot, zeroed on entry.
- Constructor calls: malloc(sizeof fields), store pointer.
- Class field access/write: load pointer, add field offset, load/store.
- ARC string assignment: AddRef new value, Release old value, then store.
- Block exit: Release every string variable in scope (EmitStringCleanup).
Lexer / Parser / AST:
- Added tkClass keyword and ParseClassDef; FieldDecl parsing refactored
into ParseFieldDecl(AFields) shared by both record and class.
- AST gains TClassTypeDef, and IsConstructorCall/IsClassAccess flags on
TFieldAccessExpr and TFieldAssignment.
RTL:
- blaise_arc.c: Phase 2 no-op stubs for _StringAddRef / _StringRelease.
- rtl/Makefile: builds blaise_rtl.a; `make install` copies it next to
the compiler binary for automatic discovery by FindRTL.
- Blaise.pas driver: FindRTL checks BLAISE_RTL env var then binary dir;
links the RTL archive when present.
Tests:
- 180 unit tests (records ×23, classes ×22, ARC ×9, codegen ×9, …).
- 4 end-to-end integration tests in tests/integration/test_arc_strings.sh
covering string assignment, two-var cleanup, reassignment cycle, and
empty-program linkage.
Docs:
- design.adoc updated with Phase 2 implementation status table.
- docs/grammar.ebnf: new authoritative EBNF grammar for the Blaise
language as implemented, including ARC semantic annotations.
2026-04-20 21:04:12 +03:00
|
|
|
|
FKeys.Free;
|
|
|
|
|
|
FFields.Free;
|
2026-04-21 02:05:57 +03:00
|
|
|
|
FVTable.Free;
|
Add Phase 2: record/class types, ARC strings, RTL stubs, and grammar doc
Symbol table:
- Added tyClass kind and NewClassType factory; TRecordTypeDesc now
accepts an optional kind parameter so records and classes share the
same descriptor with distinct semantics.
Semantic analyser:
- AnalyseTypeDecls runs before PushScope so type symbols land in global
scope and survive PopScope.
- Handles TRecordTypeDef and TClassTypeDef; sets IsConstructorCall on
TypeName.Create expressions and IsClassAccess on class-variable field
access and assignment nodes.
Code generator (QBE IR):
- Class variables: 8-byte pointer slot, zeroed on entry.
- Constructor calls: malloc(sizeof fields), store pointer.
- Class field access/write: load pointer, add field offset, load/store.
- ARC string assignment: AddRef new value, Release old value, then store.
- Block exit: Release every string variable in scope (EmitStringCleanup).
Lexer / Parser / AST:
- Added tkClass keyword and ParseClassDef; FieldDecl parsing refactored
into ParseFieldDecl(AFields) shared by both record and class.
- AST gains TClassTypeDef, and IsConstructorCall/IsClassAccess flags on
TFieldAccessExpr and TFieldAssignment.
RTL:
- blaise_arc.c: Phase 2 no-op stubs for _StringAddRef / _StringRelease.
- rtl/Makefile: builds blaise_rtl.a; `make install` copies it next to
the compiler binary for automatic discovery by FindRTL.
- Blaise.pas driver: FindRTL checks BLAISE_RTL env var then binary dir;
links the RTL archive when present.
Tests:
- 180 unit tests (records ×23, classes ×22, ARC ×9, codegen ×9, …).
- 4 end-to-end integration tests in tests/integration/test_arc_strings.sh
covering string assignment, two-var cleanup, reassignment cycle, and
empty-program linkage.
Docs:
- design.adoc updated with Phase 2 implementation status table.
- docs/grammar.ebnf: new authoritative EBNF grammar for the Blaise
language as implemented, including ARC semantic annotations.
2026-04-20 21:04:12 +03:00
|
|
|
|
inherited Destroy;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TRecordTypeDesc.AddField(const AName: string; AType: TTypeDesc);
|
|
|
|
|
|
var
|
|
|
|
|
|
Info: TFieldInfo;
|
|
|
|
|
|
Offset: Integer;
|
|
|
|
|
|
begin
|
2026-04-21 02:05:57 +03:00
|
|
|
|
Offset := TotalSize; { next available byte (includes vptr if present) }
|
Add Phase 2: record/class types, ARC strings, RTL stubs, and grammar doc
Symbol table:
- Added tyClass kind and NewClassType factory; TRecordTypeDesc now
accepts an optional kind parameter so records and classes share the
same descriptor with distinct semantics.
Semantic analyser:
- AnalyseTypeDecls runs before PushScope so type symbols land in global
scope and survive PopScope.
- Handles TRecordTypeDef and TClassTypeDef; sets IsConstructorCall on
TypeName.Create expressions and IsClassAccess on class-variable field
access and assignment nodes.
Code generator (QBE IR):
- Class variables: 8-byte pointer slot, zeroed on entry.
- Constructor calls: malloc(sizeof fields), store pointer.
- Class field access/write: load pointer, add field offset, load/store.
- ARC string assignment: AddRef new value, Release old value, then store.
- Block exit: Release every string variable in scope (EmitStringCleanup).
Lexer / Parser / AST:
- Added tkClass keyword and ParseClassDef; FieldDecl parsing refactored
into ParseFieldDecl(AFields) shared by both record and class.
- AST gains TClassTypeDef, and IsConstructorCall/IsClassAccess flags on
TFieldAccessExpr and TFieldAssignment.
RTL:
- blaise_arc.c: Phase 2 no-op stubs for _StringAddRef / _StringRelease.
- rtl/Makefile: builds blaise_rtl.a; `make install` copies it next to
the compiler binary for automatic discovery by FindRTL.
- Blaise.pas driver: FindRTL checks BLAISE_RTL env var then binary dir;
links the RTL archive when present.
Tests:
- 180 unit tests (records ×23, classes ×22, ARC ×9, codegen ×9, …).
- 4 end-to-end integration tests in tests/integration/test_arc_strings.sh
covering string assignment, two-var cleanup, reassignment cycle, and
empty-program linkage.
Docs:
- design.adoc updated with Phase 2 implementation status table.
- docs/grammar.ebnf: new authoritative EBNF grammar for the Blaise
language as implemented, including ARC semantic annotations.
2026-04-20 21:04:12 +03:00
|
|
|
|
Info := TFieldInfo.Create;
|
|
|
|
|
|
Info.Name := AName;
|
|
|
|
|
|
Info.TypeDesc := AType;
|
|
|
|
|
|
Info.Offset := Offset;
|
|
|
|
|
|
FFields.Add(Info);
|
|
|
|
|
|
FKeys.AddObject(AName, Info);
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TRecordTypeDesc.FindField(const AName: string): TFieldInfo;
|
|
|
|
|
|
var
|
|
|
|
|
|
Idx: Integer;
|
|
|
|
|
|
begin
|
|
|
|
|
|
if FKeys.Find(AName, Idx) then
|
|
|
|
|
|
Result := TFieldInfo(FKeys.Objects[Idx])
|
|
|
|
|
|
else
|
|
|
|
|
|
Result := nil;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TRecordTypeDesc.TotalSize: Integer;
|
|
|
|
|
|
var
|
|
|
|
|
|
I: Integer;
|
|
|
|
|
|
begin
|
2026-04-21 02:05:57 +03:00
|
|
|
|
{ vptr (8 bytes) precedes all fields when this class has a vtable }
|
|
|
|
|
|
if HasVTable then
|
|
|
|
|
|
Result := 8
|
|
|
|
|
|
else
|
|
|
|
|
|
Result := 0;
|
Add Phase 2: record/class types, ARC strings, RTL stubs, and grammar doc
Symbol table:
- Added tyClass kind and NewClassType factory; TRecordTypeDesc now
accepts an optional kind parameter so records and classes share the
same descriptor with distinct semantics.
Semantic analyser:
- AnalyseTypeDecls runs before PushScope so type symbols land in global
scope and survive PopScope.
- Handles TRecordTypeDef and TClassTypeDef; sets IsConstructorCall on
TypeName.Create expressions and IsClassAccess on class-variable field
access and assignment nodes.
Code generator (QBE IR):
- Class variables: 8-byte pointer slot, zeroed on entry.
- Constructor calls: malloc(sizeof fields), store pointer.
- Class field access/write: load pointer, add field offset, load/store.
- ARC string assignment: AddRef new value, Release old value, then store.
- Block exit: Release every string variable in scope (EmitStringCleanup).
Lexer / Parser / AST:
- Added tkClass keyword and ParseClassDef; FieldDecl parsing refactored
into ParseFieldDecl(AFields) shared by both record and class.
- AST gains TClassTypeDef, and IsConstructorCall/IsClassAccess flags on
TFieldAccessExpr and TFieldAssignment.
RTL:
- blaise_arc.c: Phase 2 no-op stubs for _StringAddRef / _StringRelease.
- rtl/Makefile: builds blaise_rtl.a; `make install` copies it next to
the compiler binary for automatic discovery by FindRTL.
- Blaise.pas driver: FindRTL checks BLAISE_RTL env var then binary dir;
links the RTL archive when present.
Tests:
- 180 unit tests (records ×23, classes ×22, ARC ×9, codegen ×9, …).
- 4 end-to-end integration tests in tests/integration/test_arc_strings.sh
covering string assignment, two-var cleanup, reassignment cycle, and
empty-program linkage.
Docs:
- design.adoc updated with Phase 2 implementation status table.
- docs/grammar.ebnf: new authoritative EBNF grammar for the Blaise
language as implemented, including ARC semantic annotations.
2026-04-20 21:04:12 +03:00
|
|
|
|
for I := 0 to FFields.Count - 1 do
|
2026-05-01 08:52:48 +03:00
|
|
|
|
Inc(Result, TFieldInfo(FFields.Items[I]).TypeDesc.ByteSize);
|
Add Phase 2: record/class types, ARC strings, RTL stubs, and grammar doc
Symbol table:
- Added tyClass kind and NewClassType factory; TRecordTypeDesc now
accepts an optional kind parameter so records and classes share the
same descriptor with distinct semantics.
Semantic analyser:
- AnalyseTypeDecls runs before PushScope so type symbols land in global
scope and survive PopScope.
- Handles TRecordTypeDef and TClassTypeDef; sets IsConstructorCall on
TypeName.Create expressions and IsClassAccess on class-variable field
access and assignment nodes.
Code generator (QBE IR):
- Class variables: 8-byte pointer slot, zeroed on entry.
- Constructor calls: malloc(sizeof fields), store pointer.
- Class field access/write: load pointer, add field offset, load/store.
- ARC string assignment: AddRef new value, Release old value, then store.
- Block exit: Release every string variable in scope (EmitStringCleanup).
Lexer / Parser / AST:
- Added tkClass keyword and ParseClassDef; FieldDecl parsing refactored
into ParseFieldDecl(AFields) shared by both record and class.
- AST gains TClassTypeDef, and IsConstructorCall/IsClassAccess flags on
TFieldAccessExpr and TFieldAssignment.
RTL:
- blaise_arc.c: Phase 2 no-op stubs for _StringAddRef / _StringRelease.
- rtl/Makefile: builds blaise_rtl.a; `make install` copies it next to
the compiler binary for automatic discovery by FindRTL.
- Blaise.pas driver: FindRTL checks BLAISE_RTL env var then binary dir;
links the RTL archive when present.
Tests:
- 180 unit tests (records ×23, classes ×22, ARC ×9, codegen ×9, …).
- 4 end-to-end integration tests in tests/integration/test_arc_strings.sh
covering string assignment, two-var cleanup, reassignment cycle, and
empty-program linkage.
Docs:
- design.adoc updated with Phase 2 implementation status table.
- docs/grammar.ebnf: new authoritative EBNF grammar for the Blaise
language as implemented, including ARC semantic annotations.
2026-04-20 21:04:12 +03:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TRecordTypeDesc.MaxAlign: Integer;
|
|
|
|
|
|
var
|
|
|
|
|
|
I, A: Integer;
|
|
|
|
|
|
begin
|
|
|
|
|
|
Result := 4;
|
2026-04-21 02:05:57 +03:00
|
|
|
|
if HasVTable then
|
|
|
|
|
|
Result := 8; { vptr requires 8-byte alignment }
|
Add Phase 2: record/class types, ARC strings, RTL stubs, and grammar doc
Symbol table:
- Added tyClass kind and NewClassType factory; TRecordTypeDesc now
accepts an optional kind parameter so records and classes share the
same descriptor with distinct semantics.
Semantic analyser:
- AnalyseTypeDecls runs before PushScope so type symbols land in global
scope and survive PopScope.
- Handles TRecordTypeDef and TClassTypeDef; sets IsConstructorCall on
TypeName.Create expressions and IsClassAccess on class-variable field
access and assignment nodes.
Code generator (QBE IR):
- Class variables: 8-byte pointer slot, zeroed on entry.
- Constructor calls: malloc(sizeof fields), store pointer.
- Class field access/write: load pointer, add field offset, load/store.
- ARC string assignment: AddRef new value, Release old value, then store.
- Block exit: Release every string variable in scope (EmitStringCleanup).
Lexer / Parser / AST:
- Added tkClass keyword and ParseClassDef; FieldDecl parsing refactored
into ParseFieldDecl(AFields) shared by both record and class.
- AST gains TClassTypeDef, and IsConstructorCall/IsClassAccess flags on
TFieldAccessExpr and TFieldAssignment.
RTL:
- blaise_arc.c: Phase 2 no-op stubs for _StringAddRef / _StringRelease.
- rtl/Makefile: builds blaise_rtl.a; `make install` copies it next to
the compiler binary for automatic discovery by FindRTL.
- Blaise.pas driver: FindRTL checks BLAISE_RTL env var then binary dir;
links the RTL archive when present.
Tests:
- 180 unit tests (records ×23, classes ×22, ARC ×9, codegen ×9, …).
- 4 end-to-end integration tests in tests/integration/test_arc_strings.sh
covering string assignment, two-var cleanup, reassignment cycle, and
empty-program linkage.
Docs:
- design.adoc updated with Phase 2 implementation status table.
- docs/grammar.ebnf: new authoritative EBNF grammar for the Blaise
language as implemented, including ARC semantic annotations.
2026-04-20 21:04:12 +03:00
|
|
|
|
for I := 0 to FFields.Count - 1 do
|
|
|
|
|
|
begin
|
2026-05-01 08:52:48 +03:00
|
|
|
|
A := TFieldInfo(FFields.Items[I]).TypeDesc.AllocAlign;
|
Add Phase 2: record/class types, ARC strings, RTL stubs, and grammar doc
Symbol table:
- Added tyClass kind and NewClassType factory; TRecordTypeDesc now
accepts an optional kind parameter so records and classes share the
same descriptor with distinct semantics.
Semantic analyser:
- AnalyseTypeDecls runs before PushScope so type symbols land in global
scope and survive PopScope.
- Handles TRecordTypeDef and TClassTypeDef; sets IsConstructorCall on
TypeName.Create expressions and IsClassAccess on class-variable field
access and assignment nodes.
Code generator (QBE IR):
- Class variables: 8-byte pointer slot, zeroed on entry.
- Constructor calls: malloc(sizeof fields), store pointer.
- Class field access/write: load pointer, add field offset, load/store.
- ARC string assignment: AddRef new value, Release old value, then store.
- Block exit: Release every string variable in scope (EmitStringCleanup).
Lexer / Parser / AST:
- Added tkClass keyword and ParseClassDef; FieldDecl parsing refactored
into ParseFieldDecl(AFields) shared by both record and class.
- AST gains TClassTypeDef, and IsConstructorCall/IsClassAccess flags on
TFieldAccessExpr and TFieldAssignment.
RTL:
- blaise_arc.c: Phase 2 no-op stubs for _StringAddRef / _StringRelease.
- rtl/Makefile: builds blaise_rtl.a; `make install` copies it next to
the compiler binary for automatic discovery by FindRTL.
- Blaise.pas driver: FindRTL checks BLAISE_RTL env var then binary dir;
links the RTL archive when present.
Tests:
- 180 unit tests (records ×23, classes ×22, ARC ×9, codegen ×9, …).
- 4 end-to-end integration tests in tests/integration/test_arc_strings.sh
covering string assignment, two-var cleanup, reassignment cycle, and
empty-program linkage.
Docs:
- design.adoc updated with Phase 2 implementation status table.
- docs/grammar.ebnf: new authoritative EBNF grammar for the Blaise
language as implemented, including ARC semantic annotations.
2026-04-20 21:04:12 +03:00
|
|
|
|
if A > Result then
|
|
|
|
|
|
Result := A;
|
|
|
|
|
|
end;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
2026-04-21 02:05:57 +03:00
|
|
|
|
function TRecordTypeDesc.HasVTable: Boolean;
|
|
|
|
|
|
begin
|
|
|
|
|
|
Result := (FVTable <> nil) and (FVTable.Count > 0);
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TRecordTypeDesc.VTableCount: Integer;
|
|
|
|
|
|
begin
|
|
|
|
|
|
if FVTable = nil then
|
|
|
|
|
|
Result := 0
|
|
|
|
|
|
else
|
|
|
|
|
|
Result := FVTable.Count;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TRecordTypeDesc.VTableEntryAt(ASlot: Integer): TVTableEntry;
|
|
|
|
|
|
begin
|
|
|
|
|
|
if (FVTable = nil) or (ASlot < 0) or (ASlot >= FVTable.Count) then
|
|
|
|
|
|
Result := nil
|
|
|
|
|
|
else
|
2026-05-01 08:52:48 +03:00
|
|
|
|
Result := TVTableEntry(FVTable.Items[ASlot]);
|
2026-04-21 02:05:57 +03:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TRecordTypeDesc.FindVTableSlot(const AMethodName: string): Integer;
|
|
|
|
|
|
var
|
|
|
|
|
|
I: Integer;
|
|
|
|
|
|
E: TVTableEntry;
|
|
|
|
|
|
begin
|
|
|
|
|
|
Result := -1;
|
|
|
|
|
|
if FVTable = nil then Exit;
|
|
|
|
|
|
for I := 0 to FVTable.Count - 1 do
|
|
|
|
|
|
begin
|
2026-05-01 08:52:48 +03:00
|
|
|
|
E := TVTableEntry(FVTable.Items[I]);
|
2026-04-21 02:05:57 +03:00
|
|
|
|
if SameText(E.MethName, AMethodName) then
|
|
|
|
|
|
begin
|
|
|
|
|
|
Result := I;
|
|
|
|
|
|
Exit;
|
|
|
|
|
|
end;
|
|
|
|
|
|
end;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TRecordTypeDesc.AddVTableSlot(const AMethodName, AImplName: string): Integer;
|
|
|
|
|
|
var
|
|
|
|
|
|
E: TVTableEntry;
|
|
|
|
|
|
begin
|
|
|
|
|
|
if FVTable = nil then
|
|
|
|
|
|
FVTable := TObjectList.Create(True);
|
|
|
|
|
|
E := TVTableEntry.Create;
|
|
|
|
|
|
E.Slot := FVTable.Count;
|
|
|
|
|
|
E.MethName := AMethodName;
|
|
|
|
|
|
E.ImplName := AImplName;
|
|
|
|
|
|
FVTable.Add(E);
|
|
|
|
|
|
Result := E.Slot;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TRecordTypeDesc.OverrideVTableSlot(ASlot: Integer; const AImplName: string);
|
|
|
|
|
|
begin
|
|
|
|
|
|
if (FVTable <> nil) and (ASlot >= 0) and (ASlot < FVTable.Count) then
|
2026-05-01 08:52:48 +03:00
|
|
|
|
TVTableEntry(FVTable.Items[ASlot]).ImplName := AImplName;
|
2026-04-21 02:05:57 +03:00
|
|
|
|
end;
|
|
|
|
|
|
|
2026-04-21 20:24:55 +03:00
|
|
|
|
procedure TRecordTypeDesc.AddImplements(AIntf: TInterfaceTypeDesc);
|
|
|
|
|
|
begin
|
|
|
|
|
|
FImplements.Add(AIntf);
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TRecordTypeDesc.ImplementsCount: Integer;
|
|
|
|
|
|
begin
|
|
|
|
|
|
Result := FImplements.Count;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TRecordTypeDesc.ImplementsIntfAt(AIndex: Integer): TInterfaceTypeDesc;
|
|
|
|
|
|
begin
|
2026-05-01 08:52:48 +03:00
|
|
|
|
Result := TInterfaceTypeDesc(FImplements.Items[AIndex]);
|
2026-04-21 20:24:55 +03:00
|
|
|
|
end;
|
|
|
|
|
|
|
2026-04-21 02:05:57 +03:00
|
|
|
|
procedure TRecordTypeDesc.CopyVTableFrom(AParent: TRecordTypeDesc);
|
|
|
|
|
|
var
|
|
|
|
|
|
I: Integer;
|
|
|
|
|
|
Src, Dst: TVTableEntry;
|
|
|
|
|
|
begin
|
|
|
|
|
|
if (AParent = nil) or (AParent.VTableCount = 0) then Exit;
|
|
|
|
|
|
if FVTable = nil then
|
|
|
|
|
|
FVTable := TObjectList.Create(True);
|
|
|
|
|
|
for I := 0 to AParent.VTableCount - 1 do
|
|
|
|
|
|
begin
|
|
|
|
|
|
Src := AParent.VTableEntryAt(I);
|
|
|
|
|
|
Dst := TVTableEntry.Create;
|
|
|
|
|
|
Dst.Slot := Src.Slot;
|
|
|
|
|
|
Dst.MethName := Src.MethName;
|
|
|
|
|
|
Dst.ImplName := Src.ImplName;
|
|
|
|
|
|
FVTable.Add(Dst);
|
|
|
|
|
|
end;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
2026-04-21 20:24:55 +03:00
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
{ TInterfaceTypeDesc }
|
|
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
|
2026-04-22 01:15:15 +03:00
|
|
|
|
procedure TRecordTypeDesc.AddProperty(AProp: TPropertyInfo);
|
|
|
|
|
|
begin
|
|
|
|
|
|
FProperties.Add(AProp);
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TRecordTypeDesc.FindProperty(const AName: string): TPropertyInfo;
|
|
|
|
|
|
var
|
|
|
|
|
|
I: Integer;
|
|
|
|
|
|
begin
|
|
|
|
|
|
for I := 0 to FProperties.Count - 1 do
|
2026-05-01 08:52:48 +03:00
|
|
|
|
if SameText(TPropertyInfo(FProperties.Items[I]).Name, AName) then
|
2026-04-22 01:15:15 +03:00
|
|
|
|
begin
|
2026-05-01 08:52:48 +03:00
|
|
|
|
Result := TPropertyInfo(FProperties.Items[I]);
|
2026-04-22 01:15:15 +03:00
|
|
|
|
Exit;
|
|
|
|
|
|
end;
|
|
|
|
|
|
Result := nil;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
2026-04-21 20:24:55 +03:00
|
|
|
|
constructor TInterfaceTypeDesc.Create(const AName: string);
|
|
|
|
|
|
begin
|
|
|
|
|
|
inherited Create;
|
2026-04-22 12:17:30 +03:00
|
|
|
|
Kind := tyInterface;
|
|
|
|
|
|
Name := AName;
|
|
|
|
|
|
FMethods := TStringList.Create;
|
|
|
|
|
|
FMethods.CaseSensitive := False;
|
|
|
|
|
|
FReturnTypes := TStringList.Create;
|
|
|
|
|
|
FParent := nil;
|
2026-04-21 20:24:55 +03:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
destructor TInterfaceTypeDesc.Destroy;
|
|
|
|
|
|
begin
|
2026-04-22 12:17:30 +03:00
|
|
|
|
FReturnTypes.Free;
|
2026-04-21 20:24:55 +03:00
|
|
|
|
FMethods.Free;
|
|
|
|
|
|
inherited Destroy;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
2026-04-22 12:17:30 +03:00
|
|
|
|
procedure TInterfaceTypeDesc.AddMethod(const AName: string;
|
|
|
|
|
|
const AReturnTypeName: string);
|
2026-04-21 20:24:55 +03:00
|
|
|
|
begin
|
|
|
|
|
|
FMethods.Add(AName);
|
2026-04-22 12:17:30 +03:00
|
|
|
|
FReturnTypes.Add(AReturnTypeName);
|
2026-04-21 20:24:55 +03:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TInterfaceTypeDesc.HasMethod(const AName: string): Boolean;
|
|
|
|
|
|
begin
|
|
|
|
|
|
Result := FMethods.IndexOf(AName) >= 0;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TInterfaceTypeDesc.MethodCount: Integer;
|
|
|
|
|
|
begin
|
|
|
|
|
|
Result := FMethods.Count;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TInterfaceTypeDesc.MethodName(AIndex: Integer): string;
|
|
|
|
|
|
begin
|
2026-05-01 08:52:48 +03:00
|
|
|
|
Result := FMethods.Strings[AIndex];
|
2026-04-21 20:24:55 +03:00
|
|
|
|
end;
|
|
|
|
|
|
|
2026-04-22 12:17:30 +03:00
|
|
|
|
function TInterfaceTypeDesc.MethodReturnTypeName(AIndex: Integer): string;
|
|
|
|
|
|
begin
|
2026-05-01 08:52:48 +03:00
|
|
|
|
Result := FReturnTypes.Strings[AIndex];
|
2026-04-22 12:17:30 +03:00
|
|
|
|
end;
|
|
|
|
|
|
|
2026-04-21 20:24:55 +03:00
|
|
|
|
function TInterfaceTypeDesc.MethodIndex(const AName: string): Integer;
|
|
|
|
|
|
begin
|
|
|
|
|
|
Result := FMethods.IndexOf(AName);
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
2026-04-20 19:52:09 +03:00
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
{ TSymbol }
|
|
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
|
|
|
|
|
|
constructor TSymbol.Create(const AName: string; AKind: TSymbolKind; AType: TTypeDesc);
|
|
|
|
|
|
begin
|
|
|
|
|
|
inherited Create;
|
|
|
|
|
|
Name := AName;
|
|
|
|
|
|
Kind := AKind;
|
|
|
|
|
|
TypeDesc := AType;
|
|
|
|
|
|
Params := TObjectList.Create(True);
|
Implement [Weak] attribute for cycle-breaking under ARC
A weak reference is a slot that does not contribute to the target's
refcount and is automatically nil'd when the target is freed. This
breaks reference cycles that would otherwise leak under universal ARC.
Attribute model (Delphi-compatible)
type
TNode = class
Other: TNode; // strong, default
[Weak] Back: TNode; // weak, no refcount, zeroed on free
end;
The parser accepts `[Ident]` and `[Ident(args)]` attribute lists before
var and class-field declarations. Attribute names are captured
verbatim on TVarDecl.Attributes / TFieldDecl.Attributes; unknown
attributes are accepted silently so user-defined attributes (which
will materialise with RTTI) don't need to wait for the syntax.
Semantic analysis resolves `[Weak]` with the Delphi convention that
the `Attribute` suffix is optional — `[Weak]` and `[WeakAttribute]`
both resolve to the recognised marker. Applying [Weak] to a non-
reference type (Integer, string, record) is an error at declaration
time. Resolved weakness is surfaced on TVarDecl.IsWeak / TFieldInfo.IsWeak
and TSymbol.IsWeak for codegen consumption.
Lexer: tkLBracket / tkRBracket added.
RTL (blaise_weak.c): global chained hash table maps target pointers to
lists of registered weak slot addresses. _WeakAssign registers a new
slot (and unregisters any prior), _WeakClear unregisters and zeros,
_WeakZeroSlots nils all slots registered against a target. The last
is called from _ClassRelease at refcount zero, before field cleanup
and free, so weak readers never see dangling memory.
Codegen: weak class/interface vars bypass addref/release entirely.
Variable assignment, field assignment, scope-exit cleanup, exception-
path cleanup, and per-class _FieldCleanup functions all branch on the
weak flag to emit _WeakAssign / _WeakClear against the slot address
instead of strong refcount operations.
Tests
* cp.test.weakref.pas — 14 unit tests covering lexer, parser,
semantic validation (including the suffix-drop rule), and codegen
emission at every insertion point.
* cp.test.e2e.TestRun_WeakRef_BreaksCycle_Valgrind — compile and run
a two-node mutual-reference program with [Weak]. Without the
attribute, valgrind reports both nodes as definitely lost; with it,
the run is leak-free.
All 711 compiler tests pass.
Deferred: TCustomAttribute / WeakAttribute Pascal-side class
declarations. The compiler resolves `[Weak]` purely by attribute
name, so the RTL classes are not load-bearing today. They will be
added as empty marker types when RTTI arrives and RTTI-queryable
attributes become meaningful.
2026-04-23 12:15:00 +03:00
|
|
|
|
IsWeak := False;
|
2026-04-20 19:52:09 +03:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
destructor TSymbol.Destroy;
|
|
|
|
|
|
begin
|
|
|
|
|
|
Params.Free;
|
|
|
|
|
|
inherited Destroy;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
{ TScope }
|
|
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
|
|
|
|
|
|
constructor TScope.Create(AParent: TScope);
|
|
|
|
|
|
begin
|
|
|
|
|
|
inherited Create;
|
|
|
|
|
|
FParent := AParent;
|
|
|
|
|
|
FSymbols := TObjectList.Create(True);
|
|
|
|
|
|
FKeys := TStringList.Create;
|
|
|
|
|
|
FKeys.Sorted := True;
|
|
|
|
|
|
FKeys.CaseSensitive := False;
|
|
|
|
|
|
FKeys.Duplicates := dupIgnore; { we check manually before inserting }
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
destructor TScope.Destroy;
|
|
|
|
|
|
begin
|
|
|
|
|
|
FKeys.Free;
|
|
|
|
|
|
FSymbols.Free;
|
|
|
|
|
|
inherited Destroy;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TScope.Define(ASymbol: TSymbol): Boolean;
|
|
|
|
|
|
var
|
|
|
|
|
|
Idx: Integer;
|
|
|
|
|
|
begin
|
|
|
|
|
|
if FKeys.Find(ASymbol.Name, Idx) then
|
|
|
|
|
|
begin
|
|
|
|
|
|
Result := False;
|
|
|
|
|
|
Exit;
|
|
|
|
|
|
end;
|
|
|
|
|
|
FSymbols.Add(ASymbol);
|
|
|
|
|
|
{ Store the TSymbol pointer directly in the string list's Objects slot. }
|
|
|
|
|
|
FKeys.AddObject(ASymbol.Name, ASymbol);
|
|
|
|
|
|
Result := True;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TScope.LookupLocal(const AName: string): TSymbol;
|
|
|
|
|
|
var
|
|
|
|
|
|
Idx: Integer;
|
|
|
|
|
|
begin
|
|
|
|
|
|
if FKeys.Find(AName, Idx) then
|
|
|
|
|
|
Result := TSymbol(FKeys.Objects[Idx])
|
|
|
|
|
|
else
|
|
|
|
|
|
Result := nil;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TScope.Lookup(const AName: string): TSymbol;
|
|
|
|
|
|
var
|
|
|
|
|
|
S: TScope;
|
|
|
|
|
|
begin
|
|
|
|
|
|
S := Self;
|
|
|
|
|
|
while S <> nil do
|
|
|
|
|
|
begin
|
|
|
|
|
|
Result := S.LookupLocal(AName);
|
|
|
|
|
|
if Result <> nil then
|
|
|
|
|
|
Exit;
|
|
|
|
|
|
S := S.FParent;
|
|
|
|
|
|
end;
|
|
|
|
|
|
Result := nil;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
Add case statements and enum types — required for self-hosting migration
case statement: TCaseStmt + TCaseBranch AST nodes; tkCase/tkOf tokens;
ParseCaseStmt (multi-value branches, optional else); AnalyseCaseStmt
(ordinal selector check); EmitCaseStmt (ceqw dispatch chain + jmp).
enum types: TEnumTypeDef AST node; TEnumTypeDesc symbol-table descriptor
(tyEnum, ByteSize=4, QBE 'w'); ParseEnumDef; AnalyseTypeDecls enum path
registers each member as skConstant with ordinal value 0..N-1; IsNumeric,
IsOrdinal, ByteSize, AllocAlign, QbeTypeOf all updated for tyEnum.
Variable alloc path handles tyEnum identically to tyInteger.
These two features unblock the self-hosting source migration: the compiler
source uses TTokenKind, TTypeKind, TBinaryOp, and TSymbolKind enums
throughout, all wrapped in case dispatch.
834 tests pass, zero Valgrind errors.
2026-04-23 15:46:06 +03:00
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
{ TEnumTypeDesc }
|
|
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
|
|
|
|
|
|
constructor TEnumTypeDesc.Create(const AName: string);
|
|
|
|
|
|
begin
|
|
|
|
|
|
inherited Create;
|
|
|
|
|
|
Kind := tyEnum;
|
|
|
|
|
|
Name := AName;
|
|
|
|
|
|
Members := TStringList.Create;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
destructor TEnumTypeDesc.Destroy;
|
|
|
|
|
|
begin
|
|
|
|
|
|
Members.Free;
|
|
|
|
|
|
inherited Destroy;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TEnumTypeDesc.OrdinalOf(const AMember: string): Integer;
|
|
|
|
|
|
var
|
|
|
|
|
|
I: Integer;
|
|
|
|
|
|
begin
|
|
|
|
|
|
for I := 0 to Members.Count - 1 do
|
2026-05-01 08:52:48 +03:00
|
|
|
|
if SameText(Members.Strings[I], AMember) then
|
Add case statements and enum types — required for self-hosting migration
case statement: TCaseStmt + TCaseBranch AST nodes; tkCase/tkOf tokens;
ParseCaseStmt (multi-value branches, optional else); AnalyseCaseStmt
(ordinal selector check); EmitCaseStmt (ceqw dispatch chain + jmp).
enum types: TEnumTypeDef AST node; TEnumTypeDesc symbol-table descriptor
(tyEnum, ByteSize=4, QBE 'w'); ParseEnumDef; AnalyseTypeDecls enum path
registers each member as skConstant with ordinal value 0..N-1; IsNumeric,
IsOrdinal, ByteSize, AllocAlign, QbeTypeOf all updated for tyEnum.
Variable alloc path handles tyEnum identically to tyInteger.
These two features unblock the self-hosting source migration: the compiler
source uses TTokenKind, TTypeKind, TBinaryOp, and TSymbolKind enums
throughout, all wrapped in case dispatch.
834 tests pass, zero Valgrind errors.
2026-04-23 15:46:06 +03:00
|
|
|
|
begin
|
|
|
|
|
|
Result := I;
|
|
|
|
|
|
Exit;
|
|
|
|
|
|
end;
|
|
|
|
|
|
Result := -1;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
2026-04-20 19:52:09 +03:00
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
{ TSymbolTable }
|
|
|
|
|
|
{ ------------------------------------------------------------------ }
|
|
|
|
|
|
|
|
|
|
|
|
constructor TSymbolTable.Create;
|
|
|
|
|
|
begin
|
|
|
|
|
|
inherited Create;
|
|
|
|
|
|
FScopeStack := TObjectList.Create(True);
|
|
|
|
|
|
FAllTypes := TObjectList.Create(True);
|
2026-04-22 01:02:23 +03:00
|
|
|
|
FGenerics := TStringList.Create;
|
|
|
|
|
|
FGenerics.CaseSensitive := True;
|
2026-04-20 19:52:09 +03:00
|
|
|
|
{ Global scope — parent = nil }
|
|
|
|
|
|
FScopeStack.Add(TScope.Create(nil));
|
|
|
|
|
|
RegisterBuiltins;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
destructor TSymbolTable.Destroy;
|
|
|
|
|
|
begin
|
2026-04-22 01:02:23 +03:00
|
|
|
|
FGenerics.Free;
|
2026-04-20 19:52:09 +03:00
|
|
|
|
FScopeStack.Free;
|
|
|
|
|
|
FAllTypes.Free;
|
|
|
|
|
|
inherited Destroy;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TSymbolTable.NewType(AKind: TTypeKind; const AName: string): TTypeDesc;
|
|
|
|
|
|
begin
|
|
|
|
|
|
Result := TTypeDesc.Create;
|
|
|
|
|
|
Result.Kind := AKind;
|
|
|
|
|
|
Result.Name := AName;
|
|
|
|
|
|
FAllTypes.Add(Result);
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
Add Phase 2: record/class types, ARC strings, RTL stubs, and grammar doc
Symbol table:
- Added tyClass kind and NewClassType factory; TRecordTypeDesc now
accepts an optional kind parameter so records and classes share the
same descriptor with distinct semantics.
Semantic analyser:
- AnalyseTypeDecls runs before PushScope so type symbols land in global
scope and survive PopScope.
- Handles TRecordTypeDef and TClassTypeDef; sets IsConstructorCall on
TypeName.Create expressions and IsClassAccess on class-variable field
access and assignment nodes.
Code generator (QBE IR):
- Class variables: 8-byte pointer slot, zeroed on entry.
- Constructor calls: malloc(sizeof fields), store pointer.
- Class field access/write: load pointer, add field offset, load/store.
- ARC string assignment: AddRef new value, Release old value, then store.
- Block exit: Release every string variable in scope (EmitStringCleanup).
Lexer / Parser / AST:
- Added tkClass keyword and ParseClassDef; FieldDecl parsing refactored
into ParseFieldDecl(AFields) shared by both record and class.
- AST gains TClassTypeDef, and IsConstructorCall/IsClassAccess flags on
TFieldAccessExpr and TFieldAssignment.
RTL:
- blaise_arc.c: Phase 2 no-op stubs for _StringAddRef / _StringRelease.
- rtl/Makefile: builds blaise_rtl.a; `make install` copies it next to
the compiler binary for automatic discovery by FindRTL.
- Blaise.pas driver: FindRTL checks BLAISE_RTL env var then binary dir;
links the RTL archive when present.
Tests:
- 180 unit tests (records ×23, classes ×22, ARC ×9, codegen ×9, …).
- 4 end-to-end integration tests in tests/integration/test_arc_strings.sh
covering string assignment, two-var cleanup, reassignment cycle, and
empty-program linkage.
Docs:
- design.adoc updated with Phase 2 implementation status table.
- docs/grammar.ebnf: new authoritative EBNF grammar for the Blaise
language as implemented, including ARC semantic annotations.
2026-04-20 21:04:12 +03:00
|
|
|
|
function TSymbolTable.NewRecordType(const AName: string): TRecordTypeDesc;
|
|
|
|
|
|
begin
|
|
|
|
|
|
Result := TRecordTypeDesc.Create(AName, tyRecord);
|
|
|
|
|
|
FAllTypes.Add(Result);
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TSymbolTable.NewClassType(const AName: string): TRecordTypeDesc;
|
|
|
|
|
|
begin
|
|
|
|
|
|
Result := TRecordTypeDesc.Create(AName, tyClass);
|
|
|
|
|
|
FAllTypes.Add(Result);
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
2026-04-21 20:24:55 +03:00
|
|
|
|
function TSymbolTable.NewInterfaceType(const AName: string): TInterfaceTypeDesc;
|
|
|
|
|
|
begin
|
|
|
|
|
|
Result := TInterfaceTypeDesc.Create(AName);
|
|
|
|
|
|
FAllTypes.Add(Result);
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
Add pointer type infrastructure: ^T types, dereference, GetMem/FreeMem/ReallocMem
- uSymbolTable: tyPointer kind, TPointerTypeDesc(BaseType), GetMem/FreeMem/ReallocMem builtins
- uLexer/uPasTokeniser: tkCaret token for '^'; suppress ^X string escape (not in Clean Pascal)
- uParser: ParseTypeName handles '^TypeName' prefix (uses Self. for explicit recursion);
TDerefExpr for P^ postfix; TPointerWriteStmt for P^ := V lhs
- uAST: TDerefExpr, TPointerWriteStmt nodes
- uSemantic: FindTypeOrInstantiate creates TPointerTypeDesc on demand for '^T';
AnalyseDerefExpr, AnalysePointerWriteStmt; GetMem/ReallocMem/type-cast handling
- uCodeGenQBE: tyPointer→'l', EmitPointerWrite (storew/storel), TDerefExpr (loadw/loadl),
GetMem→malloc, FreeMem→free, ReallocMem→realloc, pointer arithmetic (extsw + add/sub),
type-cast via copy
- 15 new tests (556 total), all passing
2026-04-22 09:42:55 +03:00
|
|
|
|
function TSymbolTable.NewPointerType(const AName: string; ABase: TTypeDesc): TPointerTypeDesc;
|
|
|
|
|
|
begin
|
|
|
|
|
|
Result := TPointerTypeDesc.Create;
|
|
|
|
|
|
Result.Kind := tyPointer;
|
|
|
|
|
|
Result.Name := AName;
|
|
|
|
|
|
Result.BaseType := ABase;
|
|
|
|
|
|
FAllTypes.Add(Result);
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
Add case statements and enum types — required for self-hosting migration
case statement: TCaseStmt + TCaseBranch AST nodes; tkCase/tkOf tokens;
ParseCaseStmt (multi-value branches, optional else); AnalyseCaseStmt
(ordinal selector check); EmitCaseStmt (ceqw dispatch chain + jmp).
enum types: TEnumTypeDef AST node; TEnumTypeDesc symbol-table descriptor
(tyEnum, ByteSize=4, QBE 'w'); ParseEnumDef; AnalyseTypeDecls enum path
registers each member as skConstant with ordinal value 0..N-1; IsNumeric,
IsOrdinal, ByteSize, AllocAlign, QbeTypeOf all updated for tyEnum.
Variable alloc path handles tyEnum identically to tyInteger.
These two features unblock the self-hosting source migration: the compiler
source uses TTokenKind, TTypeKind, TBinaryOp, and TSymbolKind enums
throughout, all wrapped in case dispatch.
834 tests pass, zero Valgrind errors.
2026-04-23 15:46:06 +03:00
|
|
|
|
function TSymbolTable.NewEnumType(const AName: string): TEnumTypeDesc;
|
|
|
|
|
|
begin
|
|
|
|
|
|
Result := TEnumTypeDesc.Create(AName);
|
|
|
|
|
|
FAllTypes.Add(Result);
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
feat: set types — set of TEnum, set literals, in, Include/Exclude, arithmetic
Implements Pascal bit-set types end-to-end:
- Lexer: tkSet, tkIn keywords
- AST: TSetTypeDef node; boIn binary operator
- Symbol table: tySet, TSetTypeDesc (w ≤32 members, l ≤64); NewSetType factory;
Include/Exclude registered as built-in procedures
- Parser: ParseSetDef; in operator in expression parser
- Semantic: set type registration; set literal validation; boIn membership check;
set arithmetic (+/-/*) and equality (=/≠) operator analysis; Include/Exclude
argument validation
- Codegen: compile-time bitmask for set literals; shr+and for `in`; or/xor-and/and
for union/difference/intersection; OR-in-place (Include), AND-NOT-in-place (Exclude)
- Tests: 32 new tests in cp.test.sets covering parse, semantic, and codegen
- Fix: rename Set→Assign method in genericmethodimpls test (set is now reserved)
- Docs: SetType/SetLiteral grammar rules; Set Types rationale section; resolve
enum underlying type open question (confirmed w)
2026-04-28 20:07:40 +03:00
|
|
|
|
function TSymbolTable.NewSetType(const AName: string; ABase: TEnumTypeDesc): TSetTypeDesc;
|
|
|
|
|
|
begin
|
|
|
|
|
|
Result := TSetTypeDesc.Create;
|
|
|
|
|
|
Result.Kind := tySet;
|
|
|
|
|
|
Result.Name := AName;
|
|
|
|
|
|
Result.BaseType := ABase;
|
|
|
|
|
|
Result.BitCount := ABase.Members.Count;
|
|
|
|
|
|
FAllTypes.Add(Result);
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
2026-04-27 18:31:04 +03:00
|
|
|
|
function TSymbolTable.NewOpenArrayType(AElementType: TTypeDesc): TOpenArrayTypeDesc;
|
|
|
|
|
|
begin
|
|
|
|
|
|
Result := TOpenArrayTypeDesc.Create;
|
|
|
|
|
|
Result.Kind := tyOpenArray;
|
|
|
|
|
|
Result.Name := 'array of ' + AElementType.Name;
|
|
|
|
|
|
Result.ElementType := AElementType;
|
|
|
|
|
|
FAllTypes.Add(Result);
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
feat: static array array[L..H] of T — stack-allocated fixed-size arrays
Adds TStaticSubscriptAssign AST node, ParseTypeName extension for
array[L..H] of T syntax (tkDotDot token), TStaticArrayTypeDesc with
RawSize for correct element-footprint sizing (1 for Byte, 4 for Integer),
and full codegen: alloc4/alloc8 + memset zero-init, storeb/storew for
writes, loadub/loadw for reads, LowBound subtraction for non-zero ranges.
Also includes array literal ['a','b','c'] call-site support (step 4):
TArrayLiteralExpr, open-array formal matching, stack buffer allocation,
and codegen emitting two QBE args (data ptr + compile-time high index).
13 new static-array tests + 9 new array-literal tests; all 888 tests pass.
2026-04-27 19:22:29 +03:00
|
|
|
|
function TSymbolTable.NewStaticArrayType(AElementType: TTypeDesc;
|
|
|
|
|
|
ALow, AHigh: Integer): TStaticArrayTypeDesc;
|
|
|
|
|
|
begin
|
|
|
|
|
|
Result := TStaticArrayTypeDesc.Create;
|
|
|
|
|
|
Result.Kind := tyStaticArray;
|
|
|
|
|
|
Result.Name := Format('array[%d..%d] of %s', [ALow, AHigh, AElementType.Name]);
|
|
|
|
|
|
Result.ElementType := AElementType;
|
|
|
|
|
|
Result.LowBound := ALow;
|
|
|
|
|
|
Result.HighBound := AHigh;
|
|
|
|
|
|
FAllTypes.Add(Result);
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
2026-04-22 01:02:23 +03:00
|
|
|
|
procedure TSymbolTable.RegisterGeneric(const AName: string; ATempl: TObject);
|
|
|
|
|
|
begin
|
|
|
|
|
|
FGenerics.AddObject(AName, ATempl);
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TSymbolTable.FindGeneric(const AName: string): TObject;
|
|
|
|
|
|
var
|
|
|
|
|
|
Idx: Integer;
|
|
|
|
|
|
begin
|
|
|
|
|
|
Idx := FGenerics.IndexOf(AName);
|
|
|
|
|
|
if Idx >= 0 then
|
|
|
|
|
|
Result := FGenerics.Objects[Idx]
|
|
|
|
|
|
else
|
|
|
|
|
|
Result := nil;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
2026-04-20 19:52:09 +03:00
|
|
|
|
procedure TSymbolTable.RegisterBuiltins;
|
|
|
|
|
|
var
|
2026-05-01 08:52:48 +03:00
|
|
|
|
Sym: TSymbol;
|
|
|
|
|
|
TObjectDesc: TRecordTypeDesc;
|
2026-04-20 19:52:09 +03:00
|
|
|
|
begin
|
|
|
|
|
|
{ Primitive types }
|
|
|
|
|
|
FTypeInteger := NewType(tyInteger, 'Integer');
|
|
|
|
|
|
FTypeInt64 := NewType(tyInt64, 'Int64');
|
|
|
|
|
|
FTypeUInt32 := NewType(tyUInt32, 'UInt32');
|
|
|
|
|
|
FTypeByte := NewType(tyByte, 'Byte');
|
|
|
|
|
|
FTypeBoolean := NewType(tyBoolean, 'Boolean');
|
|
|
|
|
|
FTypeString := NewType(tyString, 'string');
|
|
|
|
|
|
FTypeVoid := NewType(tyVoid, 'void');
|
2026-04-21 00:41:28 +03:00
|
|
|
|
FTypeNil := NewType(tyNil, 'nil');
|
Add pointer type infrastructure: ^T types, dereference, GetMem/FreeMem/ReallocMem
- uSymbolTable: tyPointer kind, TPointerTypeDesc(BaseType), GetMem/FreeMem/ReallocMem builtins
- uLexer/uPasTokeniser: tkCaret token for '^'; suppress ^X string escape (not in Clean Pascal)
- uParser: ParseTypeName handles '^TypeName' prefix (uses Self. for explicit recursion);
TDerefExpr for P^ postfix; TPointerWriteStmt for P^ := V lhs
- uAST: TDerefExpr, TPointerWriteStmt nodes
- uSemantic: FindTypeOrInstantiate creates TPointerTypeDesc on demand for '^T';
AnalyseDerefExpr, AnalysePointerWriteStmt; GetMem/ReallocMem/type-cast handling
- uCodeGenQBE: tyPointer→'l', EmitPointerWrite (storew/storel), TDerefExpr (loadw/loadl),
GetMem→malloc, FreeMem→free, ReallocMem→realloc, pointer arithmetic (extsw + add/sub),
type-cast via copy
- 15 new tests (556 total), all passing
2026-04-22 09:42:55 +03:00
|
|
|
|
FTypePointer := NewPointerType('Pointer', nil); { untyped pointer }
|
2026-04-28 15:20:43 +03:00
|
|
|
|
FTypePChar := NewType(tyPChar, 'PChar');
|
2026-04-20 19:52:09 +03:00
|
|
|
|
|
|
|
|
|
|
{ Register type names as skType symbols in global scope }
|
|
|
|
|
|
Define(TSymbol.Create('Integer', skType, FTypeInteger));
|
|
|
|
|
|
Define(TSymbol.Create('Int64', skType, FTypeInt64));
|
|
|
|
|
|
Define(TSymbol.Create('UInt32', skType, FTypeUInt32));
|
2026-05-02 21:59:04 +03:00
|
|
|
|
Define(TSymbol.Create('Cardinal', skType, FTypeUInt32)); { FPC/Delphi alias }
|
|
|
|
|
|
Define(TSymbol.Create('PtrUInt', skType, FTypeInt64)); { FPC: pointer-sized unsigned = QWord on 64-bit }
|
2026-04-20 19:52:09 +03:00
|
|
|
|
Define(TSymbol.Create('Byte', skType, FTypeByte));
|
|
|
|
|
|
Define(TSymbol.Create('Boolean', skType, FTypeBoolean));
|
|
|
|
|
|
Define(TSymbol.Create('string', skType, FTypeString));
|
Add pointer type infrastructure: ^T types, dereference, GetMem/FreeMem/ReallocMem
- uSymbolTable: tyPointer kind, TPointerTypeDesc(BaseType), GetMem/FreeMem/ReallocMem builtins
- uLexer/uPasTokeniser: tkCaret token for '^'; suppress ^X string escape (not in Clean Pascal)
- uParser: ParseTypeName handles '^TypeName' prefix (uses Self. for explicit recursion);
TDerefExpr for P^ postfix; TPointerWriteStmt for P^ := V lhs
- uAST: TDerefExpr, TPointerWriteStmt nodes
- uSemantic: FindTypeOrInstantiate creates TPointerTypeDesc on demand for '^T';
AnalyseDerefExpr, AnalysePointerWriteStmt; GetMem/ReallocMem/type-cast handling
- uCodeGenQBE: tyPointer→'l', EmitPointerWrite (storew/storel), TDerefExpr (loadw/loadl),
GetMem→malloc, FreeMem→free, ReallocMem→realloc, pointer arithmetic (extsw + add/sub),
type-cast via copy
- 15 new tests (556 total), all passing
2026-04-22 09:42:55 +03:00
|
|
|
|
Define(TSymbol.Create('Pointer', skType, FTypePointer));
|
2026-04-28 15:20:43 +03:00
|
|
|
|
Define(TSymbol.Create('PChar', skType, FTypePChar));
|
2026-04-20 19:52:09 +03:00
|
|
|
|
|
2026-04-21 20:24:55 +03:00
|
|
|
|
{ TObject — root of the class hierarchy; no fields, no parent }
|
2026-05-01 08:52:48 +03:00
|
|
|
|
TObjectDesc := NewClassType('TObject');
|
|
|
|
|
|
TObjectDesc.AddVTableSlot('Destroy', '$TObject_Destroy');
|
|
|
|
|
|
Define(TSymbol.Create('TObject', skType, TObjectDesc));
|
2026-04-21 20:24:55 +03:00
|
|
|
|
|
2026-04-21 23:25:49 +03:00
|
|
|
|
{ IInterface — root of the interface hierarchy; no methods }
|
|
|
|
|
|
Define(TSymbol.Create('IInterface', skType, NewInterfaceType('IInterface')));
|
|
|
|
|
|
|
2026-04-22 18:37:31 +03:00
|
|
|
|
{ Boolean constants }
|
|
|
|
|
|
Sym := TSymbol.Create('True', skConstant, FTypeBoolean);
|
|
|
|
|
|
Sym.ConstValue := 1;
|
|
|
|
|
|
Define(Sym);
|
|
|
|
|
|
Sym := TSymbol.Create('False', skConstant, FTypeBoolean);
|
|
|
|
|
|
Sym.ConstValue := 0;
|
|
|
|
|
|
Define(Sym);
|
|
|
|
|
|
|
2026-05-01 11:26:39 +03:00
|
|
|
|
{ Integer range constants — MaxInt is the 32-bit maximum; all Copy(S,N,MaxInt)
|
|
|
|
|
|
uses mean "rest of string" which works for strings shorter than 2 GB. }
|
|
|
|
|
|
Sym := TSymbol.Create('MaxInt', skConstant, FTypeInteger);
|
|
|
|
|
|
Sym.ConstValue := 2147483647;
|
2026-04-29 14:18:45 +03:00
|
|
|
|
Define(Sym);
|
|
|
|
|
|
|
2026-04-20 19:52:09 +03:00
|
|
|
|
{ Built-in I/O procedures }
|
|
|
|
|
|
Sym := TSymbol.Create('Write', skProcedure, nil);
|
|
|
|
|
|
Define(Sym);
|
|
|
|
|
|
Sym := TSymbol.Create('WriteLn', skProcedure, nil);
|
|
|
|
|
|
Define(Sym);
|
2026-05-01 08:52:48 +03:00
|
|
|
|
Sym := TSymbol.Create('StdErr', skConstant, FTypeInteger);
|
|
|
|
|
|
Sym.ConstValue := 2;
|
|
|
|
|
|
Define(Sym);
|
Add pointer type infrastructure: ^T types, dereference, GetMem/FreeMem/ReallocMem
- uSymbolTable: tyPointer kind, TPointerTypeDesc(BaseType), GetMem/FreeMem/ReallocMem builtins
- uLexer/uPasTokeniser: tkCaret token for '^'; suppress ^X string escape (not in Clean Pascal)
- uParser: ParseTypeName handles '^TypeName' prefix (uses Self. for explicit recursion);
TDerefExpr for P^ postfix; TPointerWriteStmt for P^ := V lhs
- uAST: TDerefExpr, TPointerWriteStmt nodes
- uSemantic: FindTypeOrInstantiate creates TPointerTypeDesc on demand for '^T';
AnalyseDerefExpr, AnalysePointerWriteStmt; GetMem/ReallocMem/type-cast handling
- uCodeGenQBE: tyPointer→'l', EmitPointerWrite (storew/storel), TDerefExpr (loadw/loadl),
GetMem→malloc, FreeMem→free, ReallocMem→realloc, pointer arithmetic (extsw + add/sub),
type-cast via copy
- 15 new tests (556 total), all passing
2026-04-22 09:42:55 +03:00
|
|
|
|
|
feat: set types — set of TEnum, set literals, in, Include/Exclude, arithmetic
Implements Pascal bit-set types end-to-end:
- Lexer: tkSet, tkIn keywords
- AST: TSetTypeDef node; boIn binary operator
- Symbol table: tySet, TSetTypeDesc (w ≤32 members, l ≤64); NewSetType factory;
Include/Exclude registered as built-in procedures
- Parser: ParseSetDef; in operator in expression parser
- Semantic: set type registration; set literal validation; boIn membership check;
set arithmetic (+/-/*) and equality (=/≠) operator analysis; Include/Exclude
argument validation
- Codegen: compile-time bitmask for set literals; shr+and for `in`; or/xor-and/and
for union/difference/intersection; OR-in-place (Include), AND-NOT-in-place (Exclude)
- Tests: 32 new tests in cp.test.sets covering parse, semantic, and codegen
- Fix: rename Set→Assign method in genericmethodimpls test (set is now reserved)
- Docs: SetType/SetLiteral grammar rules; Set Types rationale section; resolve
enum underlying type open question (confirmed w)
2026-04-28 20:07:40 +03:00
|
|
|
|
{ Built-in set procedures }
|
|
|
|
|
|
Sym := TSymbol.Create('Include', skProcedure, nil);
|
|
|
|
|
|
Define(Sym);
|
|
|
|
|
|
Sym := TSymbol.Create('Exclude', skProcedure, nil);
|
|
|
|
|
|
Define(Sym);
|
|
|
|
|
|
|
Add pointer type infrastructure: ^T types, dereference, GetMem/FreeMem/ReallocMem
- uSymbolTable: tyPointer kind, TPointerTypeDesc(BaseType), GetMem/FreeMem/ReallocMem builtins
- uLexer/uPasTokeniser: tkCaret token for '^'; suppress ^X string escape (not in Clean Pascal)
- uParser: ParseTypeName handles '^TypeName' prefix (uses Self. for explicit recursion);
TDerefExpr for P^ postfix; TPointerWriteStmt for P^ := V lhs
- uAST: TDerefExpr, TPointerWriteStmt nodes
- uSemantic: FindTypeOrInstantiate creates TPointerTypeDesc on demand for '^T';
AnalyseDerefExpr, AnalysePointerWriteStmt; GetMem/ReallocMem/type-cast handling
- uCodeGenQBE: tyPointer→'l', EmitPointerWrite (storew/storel), TDerefExpr (loadw/loadl),
GetMem→malloc, FreeMem→free, ReallocMem→realloc, pointer arithmetic (extsw + add/sub),
type-cast via copy
- 15 new tests (556 total), all passing
2026-04-22 09:42:55 +03:00
|
|
|
|
{ Built-in memory management }
|
|
|
|
|
|
Sym := TSymbol.Create('GetMem', skFunction, FTypePointer);
|
|
|
|
|
|
Define(Sym);
|
|
|
|
|
|
Sym := TSymbol.Create('ReallocMem', skFunction, FTypePointer);
|
|
|
|
|
|
Define(Sym);
|
|
|
|
|
|
Sym := TSymbol.Create('FreeMem', skProcedure, nil);
|
|
|
|
|
|
Define(Sym);
|
Add built-in string operation functions (Length, Pos, Copy, etc.)
Implements 8 string RTL functions as compiler built-ins:
Compiler changes (uSymbolTable, uSemantic, uCodeGenQBE):
- Length(s) → $_StringLength → Integer
- Pos(sub, s) → $_StringPos → Integer (1-based)
- Copy(s, from, count) → $_StringCopy → string
- UpperCase(s) → $_StringUpperCase → string
- LowerCase(s) → $_StringLowerCase → string
- SameText(s1, s2) → $_StringSameText → Boolean
- IntToStr(n) → $_IntToStr → string
- StrToInt(s) → $_StrToInt → Integer
RTL (blaise_str.c + Makefile):
Implements all 8 functions against the BlaiseStrHdr layout defined
in blaise_arc.c. All return new strings with RefCount=0 (ARC picks
them up at the assignment site).
Tests: 31 new IR-level tests in cp.test.stringops.pas;
7 new e2e tests in cp.test.e2e.pas verifying correct runtime
output (e.g. Length("hello")=5, Pos=7, Copy=ell, etc.).
All 755 tests pass.
2026-04-23 13:50:06 +03:00
|
|
|
|
|
|
|
|
|
|
{ Built-in string operations }
|
2026-04-23 13:56:00 +03:00
|
|
|
|
Sym := TSymbol.Create('Format', skFunction, FTypeString);
|
|
|
|
|
|
Define(Sym);
|
Add built-in string operation functions (Length, Pos, Copy, etc.)
Implements 8 string RTL functions as compiler built-ins:
Compiler changes (uSymbolTable, uSemantic, uCodeGenQBE):
- Length(s) → $_StringLength → Integer
- Pos(sub, s) → $_StringPos → Integer (1-based)
- Copy(s, from, count) → $_StringCopy → string
- UpperCase(s) → $_StringUpperCase → string
- LowerCase(s) → $_StringLowerCase → string
- SameText(s1, s2) → $_StringSameText → Boolean
- IntToStr(n) → $_IntToStr → string
- StrToInt(s) → $_StrToInt → Integer
RTL (blaise_str.c + Makefile):
Implements all 8 functions against the BlaiseStrHdr layout defined
in blaise_arc.c. All return new strings with RefCount=0 (ARC picks
them up at the assignment site).
Tests: 31 new IR-level tests in cp.test.stringops.pas;
7 new e2e tests in cp.test.e2e.pas verifying correct runtime
output (e.g. Length("hello")=5, Pos=7, Copy=ell, etc.).
All 755 tests pass.
2026-04-23 13:50:06 +03:00
|
|
|
|
Sym := TSymbol.Create('Length', skFunction, FTypeInteger);
|
|
|
|
|
|
Define(Sym);
|
|
|
|
|
|
Sym := TSymbol.Create('Pos', skFunction, FTypeInteger);
|
|
|
|
|
|
Define(Sym);
|
|
|
|
|
|
Sym := TSymbol.Create('Copy', skFunction, FTypeString);
|
|
|
|
|
|
Define(Sym);
|
|
|
|
|
|
Sym := TSymbol.Create('UpperCase', skFunction, FTypeString);
|
|
|
|
|
|
Define(Sym);
|
|
|
|
|
|
Sym := TSymbol.Create('LowerCase', skFunction, FTypeString);
|
|
|
|
|
|
Define(Sym);
|
2026-04-24 02:21:43 +03:00
|
|
|
|
Sym := TSymbol.Create('Trim', skFunction, FTypeString);
|
|
|
|
|
|
Define(Sym);
|
Add built-in string operation functions (Length, Pos, Copy, etc.)
Implements 8 string RTL functions as compiler built-ins:
Compiler changes (uSymbolTable, uSemantic, uCodeGenQBE):
- Length(s) → $_StringLength → Integer
- Pos(sub, s) → $_StringPos → Integer (1-based)
- Copy(s, from, count) → $_StringCopy → string
- UpperCase(s) → $_StringUpperCase → string
- LowerCase(s) → $_StringLowerCase → string
- SameText(s1, s2) → $_StringSameText → Boolean
- IntToStr(n) → $_IntToStr → string
- StrToInt(s) → $_StrToInt → Integer
RTL (blaise_str.c + Makefile):
Implements all 8 functions against the BlaiseStrHdr layout defined
in blaise_arc.c. All return new strings with RefCount=0 (ARC picks
them up at the assignment site).
Tests: 31 new IR-level tests in cp.test.stringops.pas;
7 new e2e tests in cp.test.e2e.pas verifying correct runtime
output (e.g. Length("hello")=5, Pos=7, Copy=ell, etc.).
All 755 tests pass.
2026-04-23 13:50:06 +03:00
|
|
|
|
Sym := TSymbol.Create('SameText', skFunction, FTypeBoolean);
|
|
|
|
|
|
Define(Sym);
|
|
|
|
|
|
Sym := TSymbol.Create('IntToStr', skFunction, FTypeString);
|
|
|
|
|
|
Define(Sym);
|
2026-04-29 14:18:45 +03:00
|
|
|
|
Sym := TSymbol.Create('Int64ToStr', skFunction, FTypeString);
|
|
|
|
|
|
Define(Sym);
|
Add built-in string operation functions (Length, Pos, Copy, etc.)
Implements 8 string RTL functions as compiler built-ins:
Compiler changes (uSymbolTable, uSemantic, uCodeGenQBE):
- Length(s) → $_StringLength → Integer
- Pos(sub, s) → $_StringPos → Integer (1-based)
- Copy(s, from, count) → $_StringCopy → string
- UpperCase(s) → $_StringUpperCase → string
- LowerCase(s) → $_StringLowerCase → string
- SameText(s1, s2) → $_StringSameText → Boolean
- IntToStr(n) → $_IntToStr → string
- StrToInt(s) → $_StrToInt → Integer
RTL (blaise_str.c + Makefile):
Implements all 8 functions against the BlaiseStrHdr layout defined
in blaise_arc.c. All return new strings with RefCount=0 (ARC picks
them up at the assignment site).
Tests: 31 new IR-level tests in cp.test.stringops.pas;
7 new e2e tests in cp.test.e2e.pas verifying correct runtime
output (e.g. Length("hello")=5, Pos=7, Copy=ell, etc.).
All 755 tests pass.
2026-04-23 13:50:06 +03:00
|
|
|
|
Sym := TSymbol.Create('StrToInt', skFunction, FTypeInteger);
|
|
|
|
|
|
Define(Sym);
|
2026-04-29 14:18:45 +03:00
|
|
|
|
Sym := TSymbol.Create('StrToInt64', skFunction, FTypeInt64);
|
|
|
|
|
|
Define(Sym);
|
2026-04-23 14:32:59 +03:00
|
|
|
|
Sym := TSymbol.Create('CompareStr', skFunction, FTypeInteger);
|
|
|
|
|
|
Define(Sym);
|
|
|
|
|
|
Sym := TSymbol.Create('CompareText', skFunction, FTypeInteger);
|
|
|
|
|
|
Define(Sym);
|
2026-04-23 15:50:30 +03:00
|
|
|
|
Sym := TSymbol.Create('OrdAt', skFunction, FTypeInteger);
|
|
|
|
|
|
Define(Sym);
|
2026-05-01 08:52:48 +03:00
|
|
|
|
Sym := TSymbol.Create('Ord', skFunction, FTypeInteger);
|
|
|
|
|
|
Define(Sym);
|
2026-04-24 02:21:43 +03:00
|
|
|
|
Sym := TSymbol.Create('Chr', skFunction, FTypeString);
|
|
|
|
|
|
Define(Sym);
|
2026-05-01 08:52:48 +03:00
|
|
|
|
Sym := TSymbol.Create('UpCase', skFunction, FTypeString);
|
|
|
|
|
|
Define(Sym);
|
|
|
|
|
|
{ Inc/Dec — in-place increment/decrement (var param, 1 or 2 args) }
|
|
|
|
|
|
Sym := TSymbol.Create('Inc', skProcedure, nil); Define(Sym);
|
|
|
|
|
|
Sym := TSymbol.Create('Dec', skProcedure, nil); Define(Sym);
|
2026-04-23 14:32:59 +03:00
|
|
|
|
{ Memory utilities }
|
2026-05-01 18:26:17 +03:00
|
|
|
|
Sym := TSymbol.Create('ZeroMem', skProcedure, nil); Define(Sym);
|
Close self-hosting gaps: multi-section blocks, file I/O, CLI args, process
Parser: allow any number of type/var/procedure/function sections in any
order within a single block. Required for single-file concatenation of
compiler units during self-hosting migration.
RTL (blaise_io.c): _SetArgs, _ParamCount, _ParamStr, _ReadFile, _WriteFile,
_AppendFile, _FileExists, _GetEnvVar, _Exec, _Halt. All exposed as Blaise
builtins via uSymbolTable/uSemantic/uCodeGenQBE.
$main now emits (w %argc, l %argv) and calls _SetArgs at startup so
ParamStr/ParamCount work at runtime.
TIdentExpr: IsNoArgFuncCall flag for builtin functions called without parens
(e.g. ParamCount without ()). Codegen synthesises a temporary TFuncCallExpr
so the existing builtin dispatch handles it transparently.
IR comment: removed stale "(Phase 2)" tag.
815 tests pass, zero Valgrind errors.
2026-04-23 15:00:34 +03:00
|
|
|
|
{ CLI arguments }
|
|
|
|
|
|
Sym := TSymbol.Create('ParamCount', skFunction, FTypeInteger); Define(Sym);
|
|
|
|
|
|
Sym := TSymbol.Create('ParamStr', skFunction, FTypeString); Define(Sym);
|
|
|
|
|
|
{ File I/O }
|
|
|
|
|
|
Sym := TSymbol.Create('ReadFile', skFunction, FTypeString); Define(Sym);
|
|
|
|
|
|
Sym := TSymbol.Create('WriteFile', skProcedure, nil); Define(Sym);
|
|
|
|
|
|
Sym := TSymbol.Create('AppendFile', skProcedure, nil); Define(Sym);
|
2026-04-24 02:21:43 +03:00
|
|
|
|
Sym := TSymbol.Create('FileExists', skFunction, FTypeBoolean); Define(Sym);
|
|
|
|
|
|
Sym := TSymbol.Create('DeleteFile', skProcedure, nil); Define(Sym);
|
|
|
|
|
|
Sym := TSymbol.Create('CurrentExceptionMessage', skFunction, FTypeString); Define(Sym);
|
Close self-hosting gaps: multi-section blocks, file I/O, CLI args, process
Parser: allow any number of type/var/procedure/function sections in any
order within a single block. Required for single-file concatenation of
compiler units during self-hosting migration.
RTL (blaise_io.c): _SetArgs, _ParamCount, _ParamStr, _ReadFile, _WriteFile,
_AppendFile, _FileExists, _GetEnvVar, _Exec, _Halt. All exposed as Blaise
builtins via uSymbolTable/uSemantic/uCodeGenQBE.
$main now emits (w %argc, l %argv) and calls _SetArgs at startup so
ParamStr/ParamCount work at runtime.
TIdentExpr: IsNoArgFuncCall flag for builtin functions called without parens
(e.g. ParamCount without ()). Codegen synthesises a temporary TFuncCallExpr
so the existing builtin dispatch handles it transparently.
IR comment: removed stale "(Phase 2)" tag.
815 tests pass, zero Valgrind errors.
2026-04-23 15:00:34 +03:00
|
|
|
|
{ Environment and process }
|
|
|
|
|
|
Sym := TSymbol.Create('GetEnvVar', skFunction, FTypeString); Define(Sym);
|
2026-04-28 21:04:28 +03:00
|
|
|
|
Sym := TSymbol.Create('GetEnvironmentVariable', skFunction, FTypeString); Define(Sym);
|
Close self-hosting gaps: multi-section blocks, file I/O, CLI args, process
Parser: allow any number of type/var/procedure/function sections in any
order within a single block. Required for single-file concatenation of
compiler units during self-hosting migration.
RTL (blaise_io.c): _SetArgs, _ParamCount, _ParamStr, _ReadFile, _WriteFile,
_AppendFile, _FileExists, _GetEnvVar, _Exec, _Halt. All exposed as Blaise
builtins via uSymbolTable/uSemantic/uCodeGenQBE.
$main now emits (w %argc, l %argv) and calls _SetArgs at startup so
ParamStr/ParamCount work at runtime.
TIdentExpr: IsNoArgFuncCall flag for builtin functions called without parens
(e.g. ParamCount without ()). Codegen synthesises a temporary TFuncCallExpr
so the existing builtin dispatch handles it transparently.
IR comment: removed stale "(Phase 2)" tag.
815 tests pass, zero Valgrind errors.
2026-04-23 15:00:34 +03:00
|
|
|
|
Sym := TSymbol.Create('Exec', skFunction, FTypeInteger); Define(Sym);
|
|
|
|
|
|
Sym := TSymbol.Create('Halt', skProcedure, nil); Define(Sym);
|
2026-04-29 00:08:36 +03:00
|
|
|
|
{ File path manipulation }
|
|
|
|
|
|
Sym := TSymbol.Create('ChangeFileExt', skFunction, FTypeString); Define(Sym);
|
|
|
|
|
|
Sym := TSymbol.Create('ExtractFileName', skFunction, FTypeString); Define(Sym);
|
|
|
|
|
|
Sym := TSymbol.Create('ExtractFilePath', skFunction, FTypeString); Define(Sym);
|
|
|
|
|
|
Sym := TSymbol.Create('IncludeTrailingPathDelimiter', skFunction, FTypeString); Define(Sym);
|
feat(rtl+compiler): Process.pas + C helper — Step 8
Add process management infrastructure for multi-file self-hosting:
rtl/src/main/c/blaise_process.c (new)
- BlaiseProcess struct with per-handle exe, argv, pipe fd, pid
- _ProcessCreate / _ProcessFree: alloc/dealloc
- _ProcessSetExe / _ProcessAddArg: configure before Execute
- _ProcessExecute: fork+exec with stdout+stderr piped
- _ProcessRunning: WNOHANG waitpid check
- _ProcessReadOutput: blocking read, returns '' on EOF
- _ProcessWaitOnExit / _ProcessExitCode: reap child
- Windows stubs via #ifdef _WIN32
rtl/src/main/pascal/Process.pas (new)
- TProcess class backed by the above C helpers via built-ins
- Execute, ReadOutput, WaitOnExit, Running, ExitCode, Parameters
compiler (uSymbolTable / uSemantic / uCodeGenQBE)
- 9 new process built-ins: ProcessCreate, ProcessSetExe,
ProcessAddArg, ProcessExecute, ProcessRunning, ProcessReadOutput,
ProcessWaitOnExit, ProcessExitCode, ProcessFree
compiler/src/main/pascal/Blaise.pas
- RunProcess: open-array → TStringList; loop uses ReadProcessChunk
wrapper (FPC-compatible); call sites rebuilt with explicit TStringList
- ReadProcessChunk wraps FPC's Output.Read for stage-1; will be
replaced by Proc.ReadOutput at Step 10
Tests
- cp.test.process.pas: 18 semantic + codegen tests for all built-ins
- cp.test.e2e.pas: TestRun_ProcessBuiltins_CapturesOutput,
TestRun_ProcessBuiltins_ExitCode (verify fork/exec/pipe at runtime)
2026-04-29 20:31:55 +03:00
|
|
|
|
{ Process management (used by Process.pas RTL) }
|
|
|
|
|
|
Sym := TSymbol.Create('ProcessCreate', skFunction, FTypePointer); Define(Sym);
|
|
|
|
|
|
Sym := TSymbol.Create('ProcessSetExe', skProcedure, nil); Define(Sym);
|
|
|
|
|
|
Sym := TSymbol.Create('ProcessAddArg', skProcedure, nil); Define(Sym);
|
|
|
|
|
|
Sym := TSymbol.Create('ProcessExecute', skProcedure, nil); Define(Sym);
|
|
|
|
|
|
Sym := TSymbol.Create('ProcessRunning', skFunction, FTypeBoolean); Define(Sym);
|
|
|
|
|
|
Sym := TSymbol.Create('ProcessReadOutput', skFunction, FTypeString); Define(Sym);
|
|
|
|
|
|
Sym := TSymbol.Create('ProcessWaitOnExit', skProcedure, nil); Define(Sym);
|
|
|
|
|
|
Sym := TSymbol.Create('ProcessExitCode', skFunction, FTypeInteger); Define(Sym);
|
|
|
|
|
|
Sym := TSymbol.Create('ProcessFree', skProcedure, nil); Define(Sym);
|
2026-04-20 19:52:09 +03:00
|
|
|
|
end;
|
|
|
|
|
|
|
2026-04-22 01:02:23 +03:00
|
|
|
|
function TSymbolTable.DefineGlobal(ASymbol: TSymbol): Boolean;
|
|
|
|
|
|
begin
|
2026-05-01 08:52:48 +03:00
|
|
|
|
Result := TScope(FScopeStack.Items[0]).Define(ASymbol);
|
2026-04-22 01:02:23 +03:00
|
|
|
|
end;
|
|
|
|
|
|
|
2026-04-20 19:52:09 +03:00
|
|
|
|
function TSymbolTable.GetCurrentScope: TScope;
|
|
|
|
|
|
begin
|
2026-05-01 08:52:48 +03:00
|
|
|
|
Result := TScope(FScopeStack.Items[FScopeStack.Count - 1]);
|
2026-04-20 19:52:09 +03:00
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TSymbolTable.GetScopeDepth: Integer;
|
|
|
|
|
|
begin
|
|
|
|
|
|
Result := FScopeStack.Count;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TSymbolTable.PushScope: TScope;
|
|
|
|
|
|
begin
|
|
|
|
|
|
Result := TScope.Create(CurrentScope);
|
|
|
|
|
|
FScopeStack.Add(Result);
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TSymbolTable.PopScope;
|
|
|
|
|
|
begin
|
|
|
|
|
|
if FScopeStack.Count > 1 then
|
|
|
|
|
|
FScopeStack.Delete(FScopeStack.Count - 1);
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TSymbolTable.Define(ASymbol: TSymbol): Boolean;
|
|
|
|
|
|
begin
|
|
|
|
|
|
Result := CurrentScope.Define(ASymbol);
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TSymbolTable.Lookup(const AName: string): TSymbol;
|
|
|
|
|
|
begin
|
|
|
|
|
|
Result := CurrentScope.Lookup(AName);
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
function TSymbolTable.FindType(const AName: string): TTypeDesc;
|
|
|
|
|
|
var
|
|
|
|
|
|
Sym: TSymbol;
|
|
|
|
|
|
begin
|
|
|
|
|
|
Sym := CurrentScope.Lookup(AName);
|
|
|
|
|
|
if (Sym <> nil) and (Sym.Kind = skType) then
|
|
|
|
|
|
Result := Sym.TypeDesc
|
|
|
|
|
|
else
|
|
|
|
|
|
Result := nil;
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
end.
|