Commit graph

234 commits

Author SHA1 Message Date
Graeme Geldenhuys 2e5df5156e
Add Patreon funding configuration
Added Patreon funding support for user 'therealgraeme'.
2026-05-13 08:54:21 +01:00
Graeme Geldenhuys 474e1d71f9
Fix duplicate PayPal entry in FUNDING.yml
Removed duplicate entry for PayPal link in FUNDING.yml.
2026-05-13 08:30:39 +01:00
Graeme Geldenhuys 5edfa9029b
Update FUNDING.yml with funding options
Uncomment funding model options and add PayPal link.
2026-05-13 08:21:59 +01:00
Graeme Geldenhuys 2a66595b9b feat(builtin): extend Length() to accept open-array and static-array params
Length() previously only accepted strings — calling it on an open-array
parameter raised "Length argument must be a string".

Semantic: allow tyOpenArray and tyStaticArray in addition to tyString.

Codegen:
- tyOpenArray: load the _high slot and add 1 (same pattern as High()+1)
- tyStaticArray: emit HighBound - LowBound + 1 as a compile-time constant
- tyString: unchanged — delegates to _StringLength RTL call

Tests: 4 IR-level tests in cp.test.openarray.pas and 1 e2e run test.
2026-05-13 07:40:34 +01:00
Graeme Geldenhuys 4bd16fd431 fix(semantic): use overload resolution on implicit-self expression path
When a method call appears in expression context (result assigned or
passed as an argument) and the receiver is an implicit-self field, the
analyser was calling FindMethodDecl — which returns the first registered
match — instead of ResolveMethodOverload.  This caused the arity check
to fire against the wrong overload (e.g. a 4-param open-array overload
was selected for a 3-arg call, producing a spurious "expects 4 args but
got 3" error).

Fix: mirror the statement path — analyse args first, then call
ResolveMethodOverload to score and pick the correct candidate.

Regression test added in cp.test.overload.pas.  As a result, the
CompileAndRunNoArgs workaround in cp.test.e2e.pas is reverted: the
no-args variant is now a proper overload of CompileAndRun.
2026-05-13 07:28:48 +01:00
Graeme Geldenhuys 53c04711eb docs: document cold-bootstrap RTL chicken-and-egg issue
On a clean checkout, compiler/target/blaise does not exist yet, so
plain `make` in rtl/ produces empty .ssa files and a silently broken
RTL archive. The fix is to pass BLAISE=../releases/v0.7.0/blaise to
make on the first RTL build. Document this in both README.adoc and
CLAUDE.md with the correct three-step cold-bootstrap sequence.
2026-05-13 01:23:07 +01:00
Graeme Geldenhuys a3c706aa9b chore: bump version to 0.8.0-dev 2026-05-13 00:50:08 +01:00
Graeme Geldenhuys af6c5c7c13 docs: update README for FPC-independent build
Replace FPC prerequisites with Blaise release binary. Update build
instructions to show bootstrap-from-release workflow. Update test
count to 1268 and note that the test suite compiles under Blaise.
2026-05-13 00:47:34 +01:00
Graeme Geldenhuys 1083e6c776 fix(fixpoint): add stage-4 fallback for cross-release bootstrap gaps
When stage-1 is an older release with different codegen, stage-2 and
stage-3 IR will differ. The script now automatically extends to
stage-3 -> stage-4 and checks that fixpoint instead of failing.
2026-05-13 00:38:23 +01:00
Graeme Geldenhuys 0cfb9ffa98 release: v0.7.0 — FPC-independent, fully self-hosting
Bump version to 0.7.0. Update fixpoint.sh to use the latest release
binary as stage-1 instead of FPC — FPC is no longer needed anywhere
in the toolchain.
2026-05-13 00:35:56 +01:00
Graeme Geldenhuys 3fd84e43b5 fix: all 1268 tests passing under Blaise-compiled test runner
Five fixes that together bring the Blaise-compiled test suite from
1053 to 1268 tests with zero failures:

- Blaise.pas: remove {$IFDEF FPC} guards; cast exception variable to
  Exception(E) for Blaise compatibility
- uCodeGenQBE.pas: fix implicit-self method calls that pass var/out or
  open-array parameters (two symmetric paths in EmitProcCall and
  EmitExpr TFuncCallExpr)
- cp.test.e2e.pas: rename CompileAndRun no-args overload to
  CompileAndRunNoArgs (overload-resolution workaround); lowercase unit
  names for case-sensitive loader; fix 0-based Pos/Copy indexing in
  TObjectList test assertion; revert debug DeleteFile guard
- blaise_io.c: fix buffer overflow in _GetTempFileName when dir is
  empty and TMPDIR is set
- process.pas: rename from Process.pas for case-sensitive unit loader

Fixpoint verified: stage-2 IR == stage-3 IR (116462 lines).
2026-05-13 00:28:24 +01:00
Graeme Geldenhuys 3ef20b7b8c feat(test): run test suite under Blaise — 1053 tests passing
Switch pasbuild test goal to use Blaise as compiler by adding the RTL
unit path to the build <unitPaths> in compiler/project.xml.  This mirrors
the fixpoint pipeline exactly: Blaise compiles itself, then compiles the
test runner.

Core fixes to make the Blaise-compiled test suite green:

  bcl.testing.pas: rename duplicate on E: variable names (EAF/EIT/ETO)
  to prevent ARC triple-release of the caught exception object (KI-3).

  bcl.testing.runner.text.pas: implement --suite / --suite Class.Method
  CLI filtering (Step 15); also adds TestClassName() via typeinfo[2]
  read for the class name lookup.  Fixes 0-based Pos split for --suite
  argument parsing.

  uParser.pas, uSemantic.pas: guard explicit-receiver .Free + reassign
  patterns with { FPC} so ARC does not double-release the field
  slot (KI-2).  Three call sites: GID.IntfDef, GD.ClassDef in the
  parser, and GII.IntfDef in InstantiateGenericInterface.

  cp.test.sets.pas: rename duplicate on ESE/EEx exception variables.

  cp.test.exceptions.pas, cp.test.multiwrite.pas: fix PosEx loop
  termination — Blaise returns -1 (not 0) for not-found; start index
  is 0-based.

  cp.test.arc.pas and seven other test files: fix absence checks that
  used Pos(...) = 0 (FPC not-found sentinel); changed to < 0.

  cp.test.stringops.pas: replace emoji literal '😀' with 'AB' to work
  around parser limitation with bytes > 127 in string literals (KI-1);
  test still exercises the same CoerceToCharOrd semantic-error path.

Result: pasbuild test -m blaise-compiler --compiler ./compiler/target/blaise
passes with 1053 tests, 0 failures, 0 errors.
2026-05-12 19:28:11 +01:00
Graeme Geldenhuys 8aa578e158 fix(codegen): load intermediate field before indexed property getter
Chained field access like Prog.Block.TypeDecls[0] was passing the
intermediate base pointer (TBlock) directly to TObjectList_Get instead
of first loading the TypeDecls field value.  Fixes the EmitExpr chained
path to match the ImplicitSelf path which already handled this correctly.
2026-05-12 19:28:11 +01:00
Graeme Geldenhuys 543fec1a90 fix(rebased): resolve conflicts and make bcl.testing compile under Blaise
- Move RTL unitPath from <build> to <test> section in project.xml —
  FPC picks up Blaise's system.pas when RTL is on the build path
- Guard IR caching code (TSearchRec/FindFirst) with { FPC} —
  Blaise doesn't support these SysUtils types
- Add LineEnding constant under { FPC} in bcl.testing for
  Blaise-compiled test units
- Replace empty array literal [] with 3-arg CompileAndRun overload
- Migrate cp.test.tokenkindname from fpcunit to bcl.testing; replace
  Format/Low/High with Blaise-compatible alternatives
- Increase QBE NString from 80 to 160 — method names like
  TInterfaceTests_TestSemantic_ClassWithInterfaceAsFirstParent_
  InheritsFromTObject exceed the old limit
2026-05-12 19:28:11 +01:00
Graeme Geldenhuys d3ee08b0da PasBuild project file changes after recent pasbuild improvements. 2026-05-12 19:28:11 +01:00
Graeme Geldenhuys dc3303fab6 feat(test): migrate 57 test units from fpcunit to bcl.testing 2026-05-12 19:28:11 +01:00
Graeme Geldenhuys aa492080a5 fix(codegen): fix 5 compiler bugs found by comprehensive e2e tests
Nested record field assignment (O.A.V := 10):
EmitFieldAssignment called EmitExpr on ObjExpr for record-typed bases,
which loaded the contents instead of returning the storage address.
Fixed to use EmitInstancePtr, which correctly returns the address of
inline record storage and the heap pointer for class-typed bases.

Int64 literals exceeding int32 range:
TIntLiteral always emitted =w copy N even for values like 3000000000
that overflow 32-bit signed range.  The semantic pass also always
assigned TypeInteger to int literals regardless of value.  Both now
check whether the value fits in [-2147483648..2147483647] and use
TypeInt64 / =l copy N when it does not.

Byte(X) type cast not truncating:
The type-cast emit path (ResolvedDecl=nil) used =w copy for all
word-typed targets.  Byte casts now emit =w and %t, 255 to mask to
8 bits, matching the expected truncation semantics.

inherited call not setting Result:
EmitInheritedCall always emitted a plain call statement, discarding
the return value of function overrides.  It now captures the return
value and stores it into %_var_Result when the parent method has a
non-void return type.

@Obj.MethodName compiler crash:
AnalyseAddrOfExpr only handled TIdentExpr; TFieldAccessExpr with a
method field caused AnalyseFieldAccess to return nil (procedure has
no return type), then crashed on nil.Name.  Added a branch that
recognises both RecordName (simple) and Base (chained) forms, builds
a method-pointer TProceduralTypeDesc (IsMethodPtr=True), and sets it
on the expression.  EmitAddrOfExpr now allocates a 16-byte block and
stores the static code pointer and loaded object pointer into it.

Also adds ~50 e2e tests covering these features and more:
control flow, records (nested, string fields), pointers, text blocks,
sets, procedural types, default params, open arrays, var/const params,
string ops, Int64, type casts, is/as, inheritance, and interfaces.
2026-05-12 19:15:13 +01:00
Graeme Geldenhuys f312764055 test(e2e): add for..in run tests; fix global static array codegen bugs
Add four end-to-end for..in tests (string/Byte, string/Integer, array,
class enumerator) that compile through QBE+gcc and assert stdout.
IR-substring tests cannot detect promoted-scalar or invalid-pointer
bugs because QBE only rejects invalid IR at assembly time.

The array test exposed two pre-existing global static array bugs:

- EmitStaticSubscriptAssign hardcoded %%_var_ prefix instead of
  VarRef(name, IsGlobal), breaking writes to global array variables.
- EmitGlobalVarData had no tyStaticArray case, emitting { l 0 }
  (8 bytes) instead of { z N } for the correct element storage size.

Both fixed; all 1382 tests pass and fixpoint verified.
2026-05-12 18:46:50 +01:00
Graeme Geldenhuys da750d96fc fix(codegen): use copy/copy for promoted scalar vars in for..in loops
The synthetic index variable and user loop variable in for..in array
and string iterations are scalar integers/bytes that EmitVarAllocs
promotes to QBE register mode (=w copy 0).  The old codegen
unconditionally emitted storew/loadw, which require a memory pointer
and cause a QBE error when the variable is a promoted register.

Apply the same IsPromoted checks used by the regular for loop: emit
=w copy for promoted scalars, storew/loadw for memory-backed ones.
2026-05-12 18:26:37 +01:00
Graeme Geldenhuys 1763d4f2d0 docs(lang): document that Ord() is not needed for string processing
In FPC/Delphi, for..in over a string yields Char, requiring Ord() to get
the numeric value.  In Blaise, S[N] and for B in S already yield Byte
directly, so Ord() is superfluous for strings.  Added a note to the
No Char Type section explaining this, and clarifying that Ord() remains
relevant for enumerations.
2026-05-12 18:05:25 +01:00
Graeme Geldenhuys f8abb16791 feat(lang): Low/High on strings + document for..in string iteration
Low(S) always returns 0; High(S) returns Length(S)-1, consistent with
0-based string indexing.  The semantic pass previously rejected both with
"must be an array"; codegen for High(S) reads the ARC header length field
directly (data_ptr-8, loadsw) and subtracts 1.

for B in S (B: Byte or Integer) was already implemented; this commit
documents the decision in language-rationale.adoc and grammar.ebnf.

language-rationale.adoc:
- Fix stale "1-based" description in String Subscript section
- Add "Low and High on Strings" section (decision, rationale, alternatives)
- Add "for B in S — String Byte Iteration" section

grammar.ebnf:
- Extend Low/High signatures to show Array | String
- Add ForStmt element-type annotation block

future-improvements.adoc:
- Add migration analyser suggestion to recommend for..in as replacement
  for pure character-walk index loops

7 new tests in cp.test.stringops.pas, all passing (1378 total, 0 failures).
Fixpoint verified.
2026-05-12 17:51:22 +01:00
Graeme Geldenhuys 93bbd21de4 chore: begin v0.7.0-dev cycle 2026-05-11 18:02:00 +01:00
Graeme Geldenhuys 445a72278a chore: release v0.6.0
Self-hosting fixpoint verified (114758 lines, stage-2 == stage-3).
Release binary archived to releases/v0.6.0/blaise.
2026-05-11 18:00:48 +01:00
Graeme Geldenhuys 079a06ea82 feat(lang): switch to 0-based string indexing throughout compiler and RTL
Blaise strings are now 0-based: S[0] is the first character, Pos returns
a 0-based index (-1 = not found), Copy takes a 0-based From argument.

Compiler changes:
- Add uStrCompat.pas bootstrap shim with StrAt, StrHead, StrCopyFrom,
  StrCopyTail, StrPos — thin wrappers that translate between FPC's
  1-based and Blaise's 0-based conventions
- Convert all string operations in Blaise.pas, uLexer.pas, uCodeGenQBE.pas,
  and uSemantic.pas to use the 0-based shims
- Add PosOrd/PosSubstr shims in uPasTokeniser.pas to keep its internal
  1-based FPos convention while translating at the boundary
- Fix UpCase codegen to extract ordinal via OrdAt when argument is a string
- Fix vtable emission in AppendUnit to use StrAt/StrCopyTail instead of
  1-based E.ImplName[1] and Copy

ARC fixes uncovered during self-hosting:
- Add string param AddRef/Release to EmitMethodDef (was only on EmitFuncDef)
- Add string and class ARC to var/out parameter assignment path

RTL changes:
- _StringPos, _StringPosEx: return 0-based index, -1 for not found
- _StringCopy, _StringDelete: accept 0-based From/Idx
- _OrdAt: accept 0-based index
- SplitIntoList in classes.pas: convert to 0-based loop

Tests: add coverage for method string param ARC, var-param string ARC,
constructor prefix matching (CreateFmt), pointer type aliases, metaclass
aliases. Update E2E tests for 0-based Copy/Pos semantics.

Docs: add 0-based string rationale to language-rationale.adoc and
migration analyser checklist to future-improvements.adoc.

Self-hosting fixpoint verified (114758 lines, stage-2 == stage-3).
2026-05-11 17:50:35 +01:00
Graeme Geldenhuys 024b3d86a0 fix(semantic): TFoo = class now correctly sets TObject as implicit parent
A class declared without an explicit parent (TFoo = class) was copying
TObject's vtable slot but leaving RT.Parent nil. This caused the typeinfo
parent pointer to be zero, breaking is/as/InheritsFrom checks against
TObject and any ancestor. Set RT.Parent := TObject's descriptor in the
implicit-parent branch, making TFoo = class identical to TFoo = class(TObject).

Updated the existing typeinfo test to assert the correct parent pointer.
2026-05-11 00:20:36 +01:00
Graeme Geldenhuys 41a94751cb feat(codegen+rtl): emit nil-guard before method dispatch on class variables
Calling a method on an uninitialised (nil) class variable silently
succeeded when the method body didn't dereference Self. Add _CheckNil()
to the RTL (blaise_exc.c) which prints a clear error and aborts if the
pointer is nil. Codegen now emits call $_CheckNil(l %self) immediately
after loading the object pointer for every explicit variable method call.

One new codegen test verifies _CheckNil appears in the IR.
2026-05-11 00:13:38 +01:00
Graeme Geldenhuys 1b73a82f27 feat(semantic): allow class(IFoo) syntax — interface as implicit first parent
When a class declaration lists only interface names in its parent list
(e.g. TFoo = class(IFoo)), the first name was incorrectly rejected with
'Unknown parent class'. Fix: if the first name in class(...) resolves to
an interface type rather than a class, move it to ImplementsNames and
clear ParentName, allowing the existing implicit-TObject logic to fire.

Two new semantic tests cover the fix.
2026-05-11 00:05:51 +01:00
Graeme Geldenhuys bc9434f54b feat(parser): replace raw token ordinals with human-readable names in parse errors
Add TokenKindName() to uLexer, mapping every TTokenKind to a readable
string (e.g. ';', '.', 'begin', 'identifier'). Update TParser.Expect to
use it — errors now read "Expected ';' but got 'var'" instead of
"Expected token 78 but got 79". The actual source value is appended only
when it differs from the kind name (e.g. 'identifier' ('Foo')).

Add cp.test.tokenkindname with 105 tests: a max-ordinal guard on tkAt=82
that fails if a new kind is added without updating TokenKindName, two
completeness sweeps, and a spot-check per token kind.
2026-05-10 23:52:41 +01:00
Graeme Geldenhuys e3732807fd docs: expand concurrency section with ARC + threading design
Add detailed design considerations for TThread under Blaise's automatic
reference counting: the fire-and-forget safety problem and the recommended
self-referential threading solution. Include practical code examples,
async/await vs TThread comparison table, state-machine explanations for
async/await transformation, and effort estimates for implementation.
2026-05-10 23:36:05 +01:00
Graeme Geldenhuys 5504c4a802 perf(codegen): replace TStringList IR output with TIRBuffer byte buffer
gprof showed that 99.95% of stage-2 time was in MemCopy (via _StringFormat),
called 186,116 times from EmitLine(Format(...)).  The previous fix replaced the
byte-loop with libc memcpy (2.5x speedup).  This change eliminates the
_StringFormat calls entirely.

Replace FOutput: TStringList with TIRBuffer — a plain growable byte buffer.
EmitLine now appends raw bytes with a direct memcpy, bypassing all string
allocation and reference counting.  AppendBuffer bulk-copies sub-buffers in
one call, replacing the line-by-line TStringList.AddStrings pattern.  GetOutput
returns FOutput.Text which does a single SetLength + memcpy.

Portability:
- FPC: uses Move() via {$IFDEF FPC} guards in BufCopy.
- Blaise: uses _ir_memcpy (external 'memcpy') since Move is not yet a Blaise
  built-in; {$linklib c} ensures the symbol is available at link time.

Result:
  stage-2 self-hosting compile time: 1m 20s -> 0.40s  (200x speedup)
  Combined with the MemCopy RTL fix:  3m 18s -> 0.40s  (490x total)

FIXPOINT_OK confirmed. All 1275 tests pass.
2026-05-09 13:20:55 +01:00
Graeme Geldenhuys 8191e58bf2 perf(rtl): replace byte-loop MemCopy/MemCompare with libc memcpy/memcmp
gprof profiling of the stage-2 self-hosted binary revealed that 99.95%
of execution time was in MemCopy, called via _StringFormat from every
EmitLine(Format(...)) call in the codegen (186,116 calls per compile).

Both blaise_str.pas and blaise_arc.pas had hand-rolled byte-copy loops:

  for I := 0 to N - 1 do D[I] := S[I];

Replace with external bindings to libc memcpy/memcmp, which use SIMD
instructions and are orders of magnitude faster for typical string sizes.

Result: stage-2 self-hosting compile time drops from ~3m 18s to ~1m 20s
— a 2.5× speedup with a 4-line change.

FIXPOINT_OK confirmed after this change.
2026-05-09 12:54:37 +01:00
Graeme Geldenhuys e558965a78 feat(codegen): mem2reg — promote scalar locals to QBE SSA temporaries
Replace the alloc+load+store pattern with direct QBE temp reuse for
scalar local variables.  QBE supports non-SSA IR natively: assigning
the same temp name multiple times is valid and QBE inserts phi nodes
internally.

Promoted types: Integer, UInt32, Boolean, Byte, Enum, Int64, Double,
Single, Set.  Excluded (address-taken or ARC-complex): tyClass,
tyString, tyPointer, tyPChar, tyMetaClass — these require a loadl from
the slot in ~30 call sites that are not yet promotion-aware.

Pre-pass per function:
- BlockHasTry: suppresses promotion entirely when the block contains
  try/finally or try/except — promoted temps live in registers and do
  not survive setjmp/longjmp used by the exception frame.
- CollectAddressTaken: marks locals whose address is taken (@X or
  passed as var-arg) as non-promotable.

Updated codegen sites:
- EmitVarAllocs: emits '%_var_X =QT copy 0' instead of alloc+store.
- EmitAssignment: writes use '%_var_X =QT copy val' instead of store*.
- EmitArcCleanup: reads use 'copy %_var_X' instead of loadl.
- EmitExpr (TIdentExpr): reads use 'copy %_var_X' instead of load*.
- EmitForStmt: loop variable init/test/step use copy instead of
  load/store.
- EmitProcCall (Inc/Dec): in-place add/sub uses copy instead of
  load/store for promoted locals.
- Return value: 'ret %_var_Result' uses copy instead of loadw/loadl.

Fixpoint: stage-2 IR = stage-3 IR (FIXPOINT_OK).
All 1275 tests pass.
2026-05-09 11:49:56 +01:00
Graeme Geldenhuys f2e78bd357 fix(semantic): normalise identifier casing to declared symbol name
Symbol table lookup is case-insensitive, but the AST node retained the
as-typed casing from source. The code generator used that casing to emit
QBE temp and global names, producing mismatched names (e.g. %_var_result
vs %_var_Result) that QBE rejected with "invalid type for second operand".

Fix: after a successful Lookup, copy Sym.Name back onto the AST node for
both TAssignment (AAssign.Name) and TIdentExpr (TIdentExpr(AExpr).Name).

Add two regression tests in cp.test.codegen:
- TestResult_LowercaseAssign_CompilesOK: 'result := x + y' compiles cleanly
- TestIdent_WrongCase_NormalisedInIR: 'n := 42' uses the declared $N global

Reported by the community: https://github.com/graemeg/blaise/discussions/15
2026-05-09 10:03:30 +01:00
Graeme Geldenhuys 9643911c2e fix(semantic): integer literal scores exact match only against integer types
An integer literal constant (untyped) should score 2 (exact) against
integer-family types (Integer, Int64, UInt32, Byte) and score 1 (widening)
against floating-point types (Double, Single).

Previously the literal scored 2 against *any* numeric type, so F(42) with
overloads F(Int64) and F(Double) was incorrectly flagged as ambiguous —
both scored 2. Now Int64 wins over Double as expected.

Update SrcAmbiguousOverload in cp.test.overload.pas to use Double vs Single
(two float overloads) which remain equally-scored from an integer literal,
preserving the ambiguity test intent.
2026-05-09 09:59:09 +01:00
Graeme Geldenhuys 7e52677781 fix: QBE has no truncl; assigning an l value to a w temp implicitly truncates. 2026-05-09 09:51:51 +01:00
Graeme Geldenhuys 6207bb9761 feat: compiler and RTL improvements from bcl.testing migration work
Compiler changes:
- uSemantic: SemanticError now includes unit/program name for easier debugging
- uSemantic: implicit-self method calls use ResolveMethodOverload so overloaded
  methods (e.g. AssertEquals variants) resolve to the correct overload
- uSemantic: integer literals score exact match against any numeric type in
  overload resolution; secondary tiebreaker on exact-match count breaks ties
- uSemantic: PosEx, GetCurrentDir, ExtractFileDir, ExcludeTrailingPathDelimiter,
  GetTempFileName builtins added
- uSymbolTable: register PosEx, GetCurrentDir, GetTempFileName,
  ExcludeTrailingPathDelimiter, ExtractFileDir, RemoveDir symbols
- uCodeGenQBE: codegen for PosEx, GetCurrentDir, ExtractFileDir,
  ExcludeTrailingPathDelimiter, GetTempFileName, ForceDirectories (stmt), RemoveDir
- uParser: fix nil-statement bug in except handler — bare "on E: T do ;" added nil
  to the handler statement list, causing a codegen crash on empty except clauses
- bcl.testing: add Ignore/EIgnoredTest support; TTestResult tracks ignored count

RTL additions:
- blaise_str.pas: _StringPosEx (3-arg Pos with start offset)
- blaise_io.c: _GetCurrentDir, _ExtractFileDir, _ExcludeTrailingPathDelimiter,
  _RemoveDir, _GetTempFileName
2026-05-09 09:51:41 +01:00
Graeme Geldenhuys a1a2f1fa71 test(parser): add chained postfix access tests
Four tests covering Obj.Method().Field, Arr[i].Field,
A.GetB().GetC().Val, and GetList()[0] patterns.
2026-05-08 08:56:30 +01:00
Graeme Geldenhuys 3476f6c9a4 feat(parser): add postfix chaining loop for Expr.Field, Expr[i], Expr^
Adds a unified postfix chaining loop at the end of ParseFactor that
handles .Field, .Method(...), [subscript], and ^ dereference after any
expression. Previously Ident.Method(...) and Expr[i] exited without
checking for further postfix access, so patterns like Obj.GetRec().X,
Tokens[0].Kind, and A.GetB().GetC().Val failed to parse.
2026-05-08 08:56:20 +01:00
Graeme Geldenhuys d8c0ef4ef6 fix: we don't have any code for migration-analyser yet. Disable tests for it. 2026-05-08 00:52:44 +01:00
Graeme Geldenhuys eacaf1e192 fix(compiler): restore poUsePipes for FPC TProcess in RunProcess
The poUsePipes option was removed during self-hosting adaptation since
the Blaise RTL TProcess always uses pipes. FPC's TProcess requires
the option explicitly, causing an access violation on Output.Read when
compiling with --output.
2026-05-08 00:33:50 +01:00
Graeme Geldenhuys da857ecc8e refactor(e2e): remove TStringStream from RunProc, replace sLineBreak with #10
Simplify RunProc() output capture: accumulate pipe bytes directly into a
string via SetString instead of routing through TStringStream.  Replace
sLineBreak with #10 in error messages to reduce SysUtils dependencies
ahead of the Blaise test-suite migration.
2026-05-07 23:55:13 +01:00
Graeme Geldenhuys 3e4d591c1d chore(doc): PasBuild rename --fpc to --compiler [already done] 2026-05-07 23:25:19 +01:00
Graeme Geldenhuys 06882bf432 feat(compiler,rtl): add OS utility builtins for test suite migration
Add GetProcessID, DirectoryExists, GetTempDir, ForceDirectories, and
Sleep as compiler builtins backed by C RTL functions. These are needed
by the e2e test infrastructure (cp.test.e2e.pas) before switching the
test suite from FPC to Blaise compilation (v0.6.0 Step 2a).
2026-05-07 23:19:52 +01:00
Graeme Geldenhuys 9300fe8a46 chore(doc): new future improvement - If Conditional Operator (ternary operator) 2026-05-07 23:19:03 +01:00
Graeme Geldenhuys db7660a95e chore(docs): Remove Multi-Line string literal from future-improvements
It already landed in the compiler.
2026-05-07 23:18:24 +01:00
Graeme Geldenhuys 11f657caef feat(lexer): implement triple single-quote text blocks
Add '''...''' multi-line string literal syntax to the lexer and
tokeniser.  Opening ''' followed by a newline starts a text block;
closing ''' on its own line sets the indentation baseline for margin
stripping.  Single quotes inside the block require no escaping.

Disambiguation: ''' followed by a newline opens a text block; followed
by any other character falls back to the classic '' escape parse.

11 new tests in cp.test.textblock.pas cover basic blocks, margin
stripping, embedded quotes, empty blocks, relative indentation
preservation, disambiguation from '''', trailing content, tabs,
and blank line preservation.  All 1264 tests pass; fixpoint verified.
2026-05-07 19:52:23 +01:00
Graeme Geldenhuys 8f0a844d07 feat(compiler,rtl): implement TObject.InheritsFrom
Add _InheritsFrom RTL helper in blaise_arc.pas that walks the typeinfo
parent chain to check class identity. Wire IsBuiltinInheritsFrom through
the semantic analyser (tyPointer, tyMetaClass, tyClass receivers) and
codegen (emits call $_InheritsFrom with correct typeinfo loading).

Update punit with AssertInheritsFrom, AssertInheritsFromClass, and class
identity checks in AssertException and RunTestHandler. Update testpunit2
so DoTest21 correctly fails on a class mismatch (EError expected, EFail
raised). Add 5 unit tests and 6 e2e tests; all 1253 tests pass, fixpoint
confirmed.
2026-05-07 09:53:47 +01:00
Graeme Geldenhuys f3f0036381 docs: remove implemented entries from future-improvements.adoc
Removes const sections, Abs(), ClassName/ClassType (all confirmed
implemented). Updates Str() workaround note now that DoubleToStr/
SingleToStr exist. Updates InheritsFrom effort note now that ClassType
vmt slots are in place.
2026-05-07 08:36:28 +01:00
Graeme Geldenhuys 2f28051956 chore: begin v0.6.0-dev cycle 2026-05-07 08:25:10 +01:00
Graeme Geldenhuys d2209afd0e release: v0.5.0
Fixpoint achieved on the full multi-file source (105865 lines of QBE IR).
Ships indexed-property getter codegen and string-field char byte-load, fixing
the self-hosting crash introduced by overloading support in v0.4.0.
2026-05-07 08:24:50 +01:00