Commit graph

11 commits

Author SHA1 Message Date
Graeme Geldenhuys 49a1a0f478 Add implicit Self field access, OrdAt builtin, and migration infrastructure
Implicit Self: bare field names inside methods (e.g. FPos := FPos + 1)
now work without explicit Self. prefix. TIdentExpr.IsImplicitSelf /
ImplicitFieldInfo flags set by AnalyseExpr when lookup fails but a class
field matches; TAssignment.ImplicitSelfField set by AnalyseAssignment.
Codegen emits loadl %_var_Self + offset for reads, storew/storel through
Self + offset for writes. Required for migrating the compiler source which
was written in FPC style without explicit Self.

OrdAt(s, i): integer RTL function — returns ASCII ordinal of character at
1-based position i. Replaces FSource[FPos] (array indexing) in the
migrated uPasTokeniser.

Migration script (tests/blaise-compiler.pas): initial work-in-progress
self-hosting source. Script handles: const→class body stripping, inline
qualifier removal, inline-var removal, Exit(expr) expansion, shr/shl
conversion, constructor/destructor→procedure, access modifier stripping,
keyword array → TStringList/InitKeywords, FSource[expr] → OrdAt(FSource, expr),
char-range in [...] → integer comparisons, repeat...until → while True.

834 tests pass.
2026-04-23 15:24:13 +01:00
Graeme Geldenhuys 5c0083abee Add constructor-with-args, const blocks; begin self-hosting source
Constructor calls with arguments: TMethodCallExpr.IsConstructorCall flag;
AnalyseMethodCallExpr handles TypeName.Create(args) → allocate via
_ClassAlloc then call user-defined Create method; EmitCaseStmt generates
dispatch, EmitExpr handles the new constructor path.

const block: TConstDecl AST node; TBlock.ConstDecls list; ParseConstBlock;
AnalyseConstDecls registers each constant as skConstant in the symbol table.

tests/blaise-compiler.pas: initial self-hosting source stub — program header,
CHR_* character constants, Exception hierarchy, and Classes.pas RTL content.
The file is built incrementally; this commit has sections 1-2.

834 tests pass.
2026-04-23 15:04:01 +01:00
Graeme Geldenhuys 926b594516 Add const blocks, fix ParseBlock ordering, and stub blaise-compiler.pas
const: TConstDecl AST node; ParseConstBlock (integer and string literal values);
AnalyseConstDecls (registers each as skConstant in symbol table). Enables
CHR_* character constants and dupAccept/dupIgnore/dupError in self-hosting source.

ParseBlock: add tkConst to the multi-section loop.
Blaise now supports: case, enum, const, multi-type/var blocks, all file I/O
builtins — the complete language surface needed for the source migration.

834 tests pass.
2026-04-23 14:37:39 +01:00
Graeme Geldenhuys 6688f32926 Add TObjectList and TStringList to RTL; fix string ARC via typed pointers
New builtins: CompareStr, CompareText (→ _StringCompare/_StringCompareText),
ZeroMem (→ memset), _ClassAddRef/_ClassRelease for manual ARC management.

EmitPointerWrite now emits retain/release when the base type is tyString,
enabling ARC-correct string storage in ^string arrays. ZeroMem is used to
zero-initialise newly grown string slots so the "release old" half of ARC
never sees uninitialised memory.

Classes.pas: TObjectList (^Pointer array, no ARC overhead) and TStringList
(^string + ^Pointer parallel arrays, sorted binary search via CompareText,
complete Delete/Insert/Clear with correct ARC). Generics.Collections and
phase3_milestone updated to zero-init new array slots, fixing previously
hidden memory safety issues revealed by the EmitPointerWrite ARC fix.

785 tests pass, zero Valgrind errors.
2026-04-23 12:32:59 +01:00
Graeme Geldenhuys 0508b6cdc6 Rewrite RTL under ARC rules; add Destroy destructor hook
Compiler:
- TRecordTypeDesc gains HasDestroyMethod boolean property, set by the
  semantic analyser whenever a class (regular or generic instantiation)
  declares a 'Destroy' method.
- EmitFieldCleanupFn emits a call to $<TypeName>_Destroy before the
  ARC field-release loop when HasDestroyMethod is set, giving classes a
  hook to free raw resources (malloc buffers etc.) at refcount zero.

RTL (Generics.Collections):
- TList<T> and TDictionary<K,V> replace 'procedure Free' with
  'procedure Destroy'. Destroy frees the internal raw buffers (FData /
  FKeys / FValues) and nils them, making re-entrant calls safe.
  Free is no longer user-defined on these classes so List.Free uses
  the built-in ARC release path (_ClassRelease + slot nil-out).

tests/phase3_milestone.pas:
- TIntList and TStrIntDict migrated to Destroy (removed FreeMem(Self)
  which was unsafe under universal ARC on TObject).

Tests (718 total, 0 failures):
- cp.test.arc: 3 new IR tests for Destroy dispatch in field cleanup fn
  (non-generic, absent-on-class-without-Destroy, generic instantiation).
- cp.test.e2e: 4 new end-to-end / valgrind tests — ClassDestroy frees
  buffer, TList ARC lifecycle, Phase3Milestone stdout and valgrind-clean.
2026-04-23 10:51:56 +01:00
Graeme Geldenhuys 2d4a005f56 Collapse Write+WriteLn pairs into multi-arg WriteLn in milestone tests 2026-04-22 23:19:55 +01:00
Graeme Geldenhuys cbb1af5ffc Replace force-exit workarounds with break now that Break is supported 2026-04-22 23:18:47 +01:00
Graeme Geldenhuys b897f3e08d Add copyright headers and compiler output
Add BSD-3-Clause copyright header to all source files (44 .pas files).
Update compiler to output copyright line in version/usage output.
2026-04-22 19:00:46 +01:00
Graeme Geldenhuys 00e44e8876 Fix try/finally and try/except exception frame size (alloc16 32 → 512)
The exception frame was allocated as alloc16 32 (32 × 16 = 512 bytes on paper,
but QBE's alloc16 N means N items of 16 bytes: so 32 × 16 = 512 is actually
correct). Wait - re-reading: alloc16 N in QBE allocates N bytes aligned to 16.
So alloc16 32 was only 32 bytes — far less than sizeof(BlaiseExcFrame) which
needs ~216 bytes on Linux x86_64 (200-byte jmp_buf + two pointer fields).

setjmp writes its full jmp_buf into the undersized slot, silently corrupting
whatever sat above it on the stack: saved registers, local variables, and the
virtual method pointer loaded for virtual dispatch. Any try block followed by
a virtual method call in expression position crashed with a bad function pointer.

Fix: alloc16 32 → alloc16 512 in both EmitTryFinallyStmt and EmitTryExceptStmt,
matching the RTL contract documented in blaise_exc.c lines 8-13.

Two new regression tests assert alloc16 512 appears in emitted IR for both
try forms. 628 tests pass (was 626).

Also adds tests/phase2_milestone.pas: linked list with virtual dispatch,
inheritance, try/finally, and 'is' type test — zero valgrind leaks.
Updates design.adoc Phase 3 status table to reflect current implementation.
2026-04-22 18:22:48 +01:00
Graeme Geldenhuys d49ebb4f51 Phase 3 milestone: TList<Integer> + TDictionary<string,Integer> zero leaks
Four bugs fixed on the path to the milestone:

1. Non-generic class fields resolved with FindType instead of
   FindTypeOrInstantiate — typed pointer fields (^Integer, ^string) in
   concrete classes failed with 'Unknown type'.

2. Integer/Boolean/Int64 local variables not zero-initialised — QBE
   validator rejected 'slot read but never stored to' for variables
   written only through var-param pointers.

3. Class method var-param signature wrong — EmitMethodDef emitted the
   param type (w for Integer) rather than 'l' (pointer) for var params;
   EmitParamAllocs spilled the pointer with storew instead of storel.

4. Method call sites did not pass addresses for var-param arguments —
   EmitMethodCallExpr always called EmitExpr (value load) rather than
   forwarding %_var_Name (address) for IsVarParam params.

Milestone result (valgrind --leak-check=full):
  7 allocs, 7 frees, 0 bytes in use at exit, ERROR SUMMARY: 0 errors
2026-04-22 16:58:36 +01:00
Graeme Geldenhuys 0df5514623 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 19:04:12 +01:00