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
|
|
|
CC = gcc
|
|
|
|
|
CFLAGS = -O2 -Wall -Wextra -std=c11
|
|
|
|
|
|
|
|
|
|
SRC_DIR = src/main/c
|
|
|
|
|
OBJ_DIR = target
|
|
|
|
|
LIB = $(OBJ_DIR)/blaise_rtl.a
|
|
|
|
|
|
|
|
|
|
# Where the compiler binary lives — install copies the RTL there so the
|
|
|
|
|
# driver finds it automatically next to itself.
|
2026-04-24 19:42:54 +03:00
|
|
|
COMPILER_BIN = ../compiler/target
|
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
|
|
|
|
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 16:07:04 +03:00
|
|
|
SRCS = $(SRC_DIR)/blaise_arc.c \
|
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
|
|
|
$(SRC_DIR)/blaise_exc.c \
|
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
|
|
|
$(SRC_DIR)/blaise_weak.c \
|
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
|
|
|
$(SRC_DIR)/blaise_str.c \
|
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
|
|
|
$(SRC_DIR)/blaise_io.c \
|
|
|
|
|
$(SRC_DIR)/blaise_process.c
|
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
|
|
|
OBJS = $(patsubst $(SRC_DIR)/%.c,$(OBJ_DIR)/%.o,$(SRCS))
|
|
|
|
|
|
|
|
|
|
.PHONY: all install clean
|
|
|
|
|
|
|
|
|
|
all: $(LIB)
|
|
|
|
|
|
|
|
|
|
$(LIB): $(OBJS)
|
|
|
|
|
ar rcs $@ $^
|
|
|
|
|
|
|
|
|
|
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c
|
|
|
|
|
@mkdir -p $(OBJ_DIR)
|
|
|
|
|
$(CC) $(CFLAGS) -c -o $@ $<
|
|
|
|
|
|
|
|
|
|
install: $(LIB)
|
|
|
|
|
@mkdir -p $(COMPILER_BIN)
|
|
|
|
|
cp $(LIB) $(COMPILER_BIN)/blaise_rtl.a
|
|
|
|
|
@echo "Installed blaise_rtl.a → $(COMPILER_BIN)/blaise_rtl.a"
|
|
|
|
|
|
|
|
|
|
clean:
|
|
|
|
|
rm -f $(OBJ_DIR)/*.o $(LIB)
|