Commit graph

10 commits

Author SHA1 Message Date
Graeme Geldenhuys 0fbeea1f9e Add function methods with Result variable and call expressions
Lexer:
- Added tkFunction keyword token.

AST:
- TMethodDecl gains ReturnTypeName (string) and ResolvedReturnType
  (TTypeDesc, nil = procedure).
- TMethodCallExpr: new expression node for obj.Method(args) in rvalue
  context; carries ObjectName, Name, Args, ResolvedClassType,
  ResolvedMethod — set by uSemantic.

Parser:
- ParseMethodDecl(IsFunction): dispatches on tkFunction vs tkProcedure;
  for functions, consumes ': ReturnType' after the parameter list.
- ParseClassDef: loops on tkProcedure or tkFunction tokens.
- ParseFactor: IDENT.IDENT followed by '(' now produces TMethodCallExpr
  with an inline arg list; without '(' stays TFieldAccessExpr.

Semantic:
- AnalyseTypeDecls: resolves ReturnTypeName → ResolvedReturnType for
  each function method alongside param type resolution.
- AnalyseMethodDecl: for function methods, defines 'Result' as a
  skVariable of the return type in the method scope, making it
  assignable inside the body.
- AnalyseMethodCallExpr: validates object type, looks up method,
  verifies the method has a return type (not a procedure), checks arg
  count and types; sets ResolvedClassType and ResolvedMethod.
- AnalyseExpr: handles TMethodCallExpr before TFieldAccessExpr.

Code generator:
- EmitMethodDef: emits 'function <qtype> $T_M(...)' for function
  methods; allocates and zero-inits %_var_Result; after EmitBlock,
  loads Result and emits 'ret <temp>'.
- EmitExpr: handles TMethodCallExpr — loads Self, builds arg list,
  emits 'call $T_M(...)' into a typed temp, returns the temp.

Tests: 226 unit tests (22 new function-method tests), 0 failures.
2026-04-20 19:48:49 +01:00
Graeme Geldenhuys 313619511c Add class instance methods (procedure, inline body, Self, method calls)
Lexer:
- Added tkProcedure keyword token.

AST:
- TMethodParam: stores parameter name, type string, and resolved type.
- TMethodDecl: method declaration with name, param list, and inline body
  (TBlock). Avoids TObject.MethodName collision by naming the field Name.
- TClassTypeDef.Methods: owned TObjectList of TMethodDecl.
- TMethodCallStmt: statement node for obj.Method(args) calls; ObjectName
  and Name fields; ResolvedClassType and ResolvedMethod set by semantic.
- TBlock forward-declared before TMethodDecl to break circular dependency.

Parser:
- ParseMethodDecl: parses 'procedure Name [(ParamList)] ; Block ;' inside
  a class body.
- ParseParamList: handles 'Name : Type [; Name : Type ...]' param groups.
- ParseClassDef: field parsing followed by method parsing before 'end'.
- ParseStmt: after 'IDENT.IDENT', dispatches to TFieldAssignment (':=')
  or TMethodCallStmt (everything else, optionally with arg list).
- ParseMethodCallArgList: argument list for method call statements.

Semantic:
- FMethodIndex: TStringList mapping 'TypeName.Name' -> TMethodDecl for
  O(log n) dispatch lookup.
- AnalyseTypeDecls: indexes methods and resolves param types after fields.
- AnalyseMethodBodies: analyses each method body after type registration.
- AnalyseMethodDecl: pushes a scope with Self (class type) and explicit
  params, then calls AnalyseBlock for the method body.
- AnalyseMethodCall: resolves object type, looks up method, checks arg
  count and types; sets ResolvedClassType and ResolvedMethod on the node.

Code generator:
- EmitMethodDefs: emits a QBE function for every method in every class
  type before the main function.
- EmitMethodDef: emits 'function $TypeName_Name(l %_par_Self, ...) {'.
- EmitParamAllocs: allocates stack slots for Self and explicit params.
- EmitMethodCall: loads Self from the caller's var slot, then emits
  'call $TypeName_Name(l selfTemp, ...)'.

Tests: 204 unit tests (24 new method tests), 0 failures.
Grammar: docs/grammar.ebnf updated with MethodDecl, ParamList, MethodCall.
2026-04-20 19:42:09 +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
Graeme Geldenhuys 8122b0a299 Wire semantic analysis pass into the compiler pipeline
The semantic analyser now runs between parsing and code generation.
Type mismatches, undeclared identifiers, and duplicate declarations are
reported to stderr with source position and exit code 1 before any IR
is emitted.
2026-04-20 18:01:03 +01:00
Graeme Geldenhuys 4fbfdfdbba Add semantic analyser with type inference and checking
TSemanticAnalyser walks the AST, resolves all identifiers via the symbol
table, annotates every expression node with ResolvedType, and type-checks
assignments and procedure calls. Raises ESemanticError with source position
on undeclared identifiers, type mismatches, duplicate declarations, and
unknown types. TProgram now owns the TSymbolTable after analysis so
ResolvedType pointers remain valid for the lifetime of the AST.

Also adds the 'div' keyword for integer division throughout the pipeline
(lexer, parser, semantic). 26 new FPCUnit tests, 126 total, all passing.
2026-04-20 17:58:27 +01:00
Graeme Geldenhuys a561525ebd Add symbol table with scope nesting and built-in types
TSymbolTable manages a scope stack with case-insensitive lookup. Each
TScope holds a sorted TStringList (names → TSymbol) and chains to its
parent for identifier resolution. Built-in primitives (Integer, Int64,
UInt32, Byte, Boolean, string) and I/O procedures (Write, WriteLn) are
pre-registered in the global scope. 29 FPCUnit tests, all passing.
2026-04-20 17:52:09 +01:00
Graeme Geldenhuys 95e1ea58af Add FPC-compatible CLI mode to Blaise compiler
Blaise can now act as a drop-in FPC replacement for PasBuild when
invoked via `pasbuild compile --fpc /path/to/blaise`.

- Detects FPC-style invocation (single-dash flags vs. native double-dash)
- Handles -iV / -iTP / -iTO probe queries PasBuild uses before compiling
- Maps -FE<dir>, -o<name> to output path; silently ignores -Fu, -FU,
  -Mobjfpc, -O<n>, -g, -gl, -CX and unknown single-dash flags
- Native --source / --output / --emit-ir mode is fully preserved
2026-04-20 17:37:14 +01:00
Graeme Geldenhuys 25de8be212 Fix compilation errors; vendor QBE 1.2; end-to-end Hello World works
Fix TProgram.Uses reserved-word clash (renamed to UsedUnits), nested
brace-comment in uParser.pas, and missing uAST in Blaise.pas uses clause.

Vendor QBE 1.2 source in vendor/qbe/ (built locally with make; binary
excluded via .gitignore). End-to-end pipeline confirmed: Blaise compiles
Hello World and Calc (6*7=42) to native x86_64 Linux binaries.

71/71 FPCUnit tests passing.
2026-04-20 16:38:57 +01:00
Graeme Geldenhuys 3fce5f86de Phase 1 bootstrap: Blaise compiler skeleton
Renames the project from 'Clean Pascal' to Blaise (after Blaise Pascal).
Adds the Phase 1 compiler pipeline with TDD test suite:

- uPasTokeniser: general Pascal tokeniser (ported from fpGUI IDE)
- uLexer: compiler-specific adapter — filters whitespace/comments,
  maps to TTokenKind, unescapes string literals
- uAST: typed AST node hierarchy (TProgram, TBlock, TBinaryExpr, etc.)
- uParser: recursive-descent parser for the Phase 1 BNF grammar
- uCodeGenQBE: QBE IR emitter — WriteLn/Write built-ins, integer
  variables, arithmetic, string literals
- Blaise.pas: compiler driver — parses flags, shells to qbe+cc
- FPCUnit test suites for lexer, parser, and code generator
2026-04-20 15:35:50 +01:00
Graeme Geldenhuys fd79a7db98 Initial project scaffold
PasBuild multi-module layout with three modules: compiler (application),
rtl (library), and tools/migration-analyser (application). Includes root
aggregator project.xml, debug/release build profiles, BSD 3-Clause licence,
README, .gitignore, and design document. QBE vendored as the Phase 1
backend. Bootstrap requires FPC 3.2.2.
2026-04-20 14:22:10 +01:00