Commit graph

11 commits

Author SHA1 Message Date
Graeme Geldenhuys 5894411a35 chore(license): relicense from BSD-3-Clause to Apache 2.0 + Runtime Library Exception
Adopts the Swift/LLVM model: Apache License 2.0 with the Runtime Library
Exception text used verbatim by the Swift project. SPDX identifier:
Apache-2.0 WITH Swift-exception.

Apache 2.0 brings an explicit patent grant and patent-retaliation clause
that BSD-3 lacks. The Runtime Library Exception ensures binaries
produced by the Blaise compiler do not inherit attribution obligations
from the linked-in RTL.

* LICENSE — full Apache 2.0 + RLE text (replaces the deleted LICENCE).
* NOTICE — project header plus QBE attribution (MIT, vendored).
* docs/language-rationale.adoc — new Project Governance section
  capturing the decision, alternatives considered, and rationale.
* SPDX headers updated across all .pas, .pp, .inc, .c source files,
  build scripts, PasBuild plugins, and the Makefile.
* project.xml license field updated.
* tools/migrate_full.py HEADER template updated so generated
  self-hosting source carries the new licence.
2026-05-03 19:45:29 +01:00
Graeme Geldenhuys 345489fec1 feat(rtl): eliminate libc printf/fprintf and most pure-C helpers — Step 9 (80%)
Write/WriteLn now call _SysWriteStr/_SysWriteInt/_SysWriteInt64/
_SysWriteNewline (blaise_sys.pas + blaise_sys_posix.c) instead of
printf/fprintf.  _SysWriteNewline uses POSIX write(2) directly;
no format strings, no libc I/O buffering.

Pure Pascal implementations replace these libc calls in the RTL:
  - toupper / tolower  (inline ASCII arithmetic in blaise_str.pas)
  - strlen             (NUL-scan loop in blaise_str.pas)
  - memcpy             (byte-copy loop in both blaise_str.pas and blaise_arc.pas)
  - memcmp             (byte-compare loop in blaise_arc.pas)

Remaining libc dependency: malloc/free (needs Pascal allocator — Step 9d).

All 1035 compiler tests pass.
2026-05-01 17:22:07 +01:00
Graeme Geldenhuys 584f0378e2 feat(rtl): migrate blaise_arc.c → blaise_arc.pas — Step 9c
Port all non-function-pointer ARC functions from C to Pascal:
  _StringAddRef, _StringRelease, _StringEquals, _StringConcat,
  TObject_Destroy, _ClassAddRef, _ClassFree

Remaining in C (blaise_arc_class.c — renamed from blaise_arc.c):
  _ClassAlloc and _ClassRelease — store and call a function pointer
  (the $_FieldCleanup_TypeName hook), which Blaise cannot express yet.

Compiler fixes required to support this port:
- uCodeGenQBE: EmitFuncDef now exits early for IsExternal declarations;
  previously it crashed (access violation) when iterating a nil body
- uSymbolTable: remove _ClassAddRef and _ClassRelease from RegisterBuiltins;
  they are internal RTL symbols emitted directly by the codegen, not
  user-callable builtins, and pre-registering them caused "Duplicate
  identifier" errors when the unit interface re-declared them

RTL Makefile: blaise_arc_class.c replaces blaise_arc.c in C_SRCS;
blaise_arc.pas added to PAS_OBJS with its own build driver.

All 1035 compiler tests pass.
2026-05-01 16:26:17 +01:00
Graeme Geldenhuys d60958dc25 feat(rtl): replace blaise_str.c with Pascal-compiled blaise_str.pas in RTL build
Remove blaise_str.c from the RTL archive and replace it with object code
compiled from blaise_str.pas via the Blaise compiler itself. The variadic
_StringFormat cannot be expressed in Pascal so it moves to blaise_str_fmt.c.

Makefile changes:
- blaise_str.c removed from C_SRCS; blaise_str_fmt.c added in its place
- New Pascal build rules: .pas → .ssa (unit IR via build driver + sed strip)
  → .s (QBE) → .o (gcc -c) → included in blaise_rtl.a
- blaise_str_build_driver.pas: minimal program driver used by the Makefile
  to compile the unit; the program section is stripped before assembly

All 1035 compiler tests pass with the new RTL.
2026-05-01 14:42:29 +01:00
Graeme Geldenhuys e568ce21f2 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 18:31:55 +01:00
Graeme Geldenhuys 4797ce71a9 fix: drain child process output to avoid pipe deadlock
RunProcess used poWaitOnExit combined with poUsePipes, which
deadlocks when the child fills the stderr pipe buffer before we
read — the child blocks waiting for the reader, we block waiting for
exit.  Drain the pipe in a loop while the child runs, then wait on
exit separately.  Also fold stderr into stdout so qbe/cc diagnostics
round-trip through a single handle.

rtl/Makefile: install blaise_rtl.a into compiler/target (not
compiler/target/bin) so it lives next to the blaise binary produced
by pasbuild.
2026-04-24 17:42:54 +01:00
Graeme Geldenhuys 637369aa10 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 13:00:34 +01:00
Graeme Geldenhuys 3258679445 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 11:50:06 +01:00
Graeme Geldenhuys 6569c1f82b 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 10:15:00 +01:00
Graeme Geldenhuys c1d53e7be6 Implement setjmp/longjmp-based exception dispatch in QBE IR
- Add blaise_exc.c to RTL: _PushExcFrame, _PopExcFrame, _Raise,
  _CurrentException, _Reraise using thread-local setjmp/longjmp frames
- Update rtl/Makefile to compile and archive blaise_exc.c
- Rewrite EmitTryFinallyStmt: alloc16 frame on stack, push+setjmp at try
  entry, finally body emitted on both normal and exception paths, _Reraise
  propagates to enclosing handler on exception path
- Rewrite EmitTryExceptStmt: same frame setup, except handler entered via
  jnz on setjmp return, _PopExcFrame before handler body
- Add 6 codegen tests for setjmp-based dispatch patterns (460 tests total)
- Update design doc: mark try/except/finally as Done, update next steps
2026-04-21 14:07:04 +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