From 3cdb5d202faf04993e5aad955078653251cb6073 Mon Sep 17 00:00:00 2001 From: _ov4 Date: Wed, 21 Oct 2026 06:18:34 +0000 Subject: docs: document TEMPLATE Co-Authored-By: Claude Opus 5.5 --- AGENTS/ARCH.md | 4 +- AGENTS/CONVENTIONS.md | 4 ++ AGENTS/EXTENDING.md | 2 + AGENTS/PITFALLS.md | 1 + AGENTS/SUBSYSTEMS.md | 12 ++++ README.md | 1 + doc/language/cpsc-language-overview.7.md | 1 + doc/language/keyword/cpsc-keyword-class.7.md | 2 +- doc/language/keyword/cpsc-keyword-function.7.md | 2 +- doc/language/keyword/cpsc-keyword-overload.7.md | 2 +- doc/language/keyword/cpsc-keyword-template.7.md | 77 +++++++++++++++++++++++++ 11 files changed, 104 insertions(+), 4 deletions(-) create mode 100644 doc/language/keyword/cpsc-keyword-template.7.md diff --git a/AGENTS/ARCH.md b/AGENTS/ARCH.md index 75202cd..e6361a9 100644 --- a/AGENTS/ARCH.md +++ b/AGENTS/ARCH.md @@ -314,9 +314,11 @@ do not modify the real release for local verification. | `lib/Parser/ParserFunction.cc` | FUNCTION/PROCEDURE/CALL/RETURN + parameter-list parsing | | `lib/Parser/ParserFile.cc` | OPENFILE/READFILE/WRITEFILE/CLOSEFILE parsing | | `lib/Parser/ParserType.cc` | Type names, variable/array/constant declarations, TYPE/ENDTYPE and CLASS/ENDCLASS declarations, shared field and method parsing, SET/DEFINE reserved stubs | +| `include/cps/TemplateAST.h`, `lib/Parser/ParserType.cc` | TEMPLATE declarations, type parameter packs and canonical template ids; call and pack parsing also uses ParserExpression.cc/ParserFunction.cc | | `lib/CodeGen/CodeGen.cc` | Initialization, symbols, call arguments and main() compilation | | `lib/CodeGen/ExpressionGen.cc`, `StatementGen.cc`, `ControlFlowGen.cc` | Expression/statement dispatch and control-flow emission | | `lib/CodeGen/OperatorGen.cc`, `include/cps/Operator.h` | Exact global/member operator selection, calls and temporary receivers; shared operator spellings | +| `lib/CodeGen/TemplateGen.cc`, `TemplateClone.h`, `TemplateClone.cc` | Template registry validation, cached function/class instantiation, structural type substitution, pack expansion and deferred bodies; call deduction/selection lives in CodeGen.cc | | `lib/CodeGen/ConstructorGen.cc` | Construction, copying, assignment, destruction and deferred object cleanup | | `lib/CodeGen/GlobalGen.cc` | GLOBAL declarations and scalar/owning STRING initializers | | `lib/CodeGen/ConstantGen.cc` | Literal constants, declaration conflicts and lexical binding restoration | @@ -337,7 +339,7 @@ do not modify the real release for local verification. | `cmake/Manual.cmake`, `cmake/GenerateManual.cmake.in` | Optional manual generation with local-first Pandoc discovery | | `install.sh`, `install.ps1`, `cmake/Install.cmake.in`, `cmake/Release.cmake` | Artifact installation and versioned binary release packaging | -AST nodes live in `include/cps/AST.h` (+ `FunctionAST.h`, `FileAST.h`, `TypeAST.h`); +AST nodes live in `include/cps/AST.h` (+ `FunctionAST.h`, `FileAST.h`, `TypeAST.h`, `TemplateAST.h`); they are pure data with virtual destructors only — no codegen/visitor methods. ## Detail docs diff --git a/AGENTS/CONVENTIONS.md b/AGENTS/CONVENTIONS.md index 5863548..ed9acb1 100644 --- a/AGENTS/CONVENTIONS.md +++ b/AGENTS/CONVENTIONS.md @@ -137,6 +137,10 @@ declaration spelling for LLVM lookup, independently of call-site casing. Methods `Class.Method` names and a hidden leading receiver address; their FuncSig parameters contain only explicit source parameters. Method prototypes are pre-registered too. +**Templates substitute ASTs before ordinary emission.** Keep explicit `TemplateArgs` separate from call callee names; canonical type ids have no spaces (`Box>`). `TemplateClone.cc` owns deep cloning and structural substitution; every new body AST node needs coverage there. A type parameter binds one canonical type and a pack binds a list; expanded parameter names use unlexable `#` suffixes. FunctionGen stores overload signatures in `IdentifierMap>` so instantiating another template cannot invalidate a held `FuncSig *` during marshalling. CodeGen owns cloned ASTs for the entire compile and caches each instance by declaration plus canonical arguments. + +**Template selection and context are shared by typing and emission.** When multiple definitions share a function/procedure name, every ordinary and template definition must specify OVERLOAD. `resolveCall` computes static argument types/ranks once, then tries exact ordinary, non-variadic template and variadic template tiers; same-tier ties are errors. Candidate checking must stay silent and must not instantiate speculative class types. Explicit arguments pre-bind types without allowing conversions. Instances keep the template owner for diagnostics/library behavior and the first requester in `VisibilityUnit` for lookup (`value_or(CurrentUnit)`). Deferred bodies use the symbol snapshot at the template declaration to preserve positional own constants; save and restore all caller context. Class resolution hooks must honor visibility and reserve/cache the record before resolving fields. + **Operator overloads share static selection.** OPERATOR is a reserved keyword; `OVERLOAD FUNCTION OPERATOR+(...)` records the token on PrototypeAST. Canonical names contain a space (`OPERATOR +`, `Class.OPERATOR AND`) to avoid collisions with diff --git a/AGENTS/EXTENDING.md b/AGENTS/EXTENDING.md index bb297d4..668941b 100644 --- a/AGENTS/EXTENDING.md +++ b/AGENTS/EXTENDING.md @@ -17,6 +17,8 @@ fall-through branch in control-flow codegen with `if (!Builder->GetInsertBlock()->hasTerminator())` — a RETURN in the body may have already terminated the block. +**New AST nodes must be cloned in `lib/CodeGen/TemplateClone.cc`.** Its dynamic_cast chains deep-clone every expression and statement permitted in a template body, preserving source lines. Add structural type substitution for each new type-bearing field and expand packs only through the argument-list helper. Unsupported nodes must diagnose rather than disappear; nested function definitions remain forbidden in template bodies. + **New builtin function:** add a `{Name, Arity, ReturnTypeName}` row to the shared `Builtins` table in the internal `lib/CodeGen/Builtins.h` (single source of truth — arity checking in `emitCall`, the return type in `getExprTypeInfo`, and the reject-user-redefinition check diff --git a/AGENTS/PITFALLS.md b/AGENTS/PITFALLS.md index 8d2d890..7bc78fb 100644 --- a/AGENTS/PITFALLS.md +++ b/AGENTS/PITFALLS.md @@ -91,6 +91,7 @@ test programs. inputs. MAIN and builtins cannot be overloaded. NEW supports overloads; DECLARE, fields and arrays needing default construction require a matching no-argument NEW. Private access is checked after selection, never used to select a public fallback. +- **TEMPLATE extension.** Top-level FUNCTION/PROCEDURE/CLASS only; all template parameters are types, with at most one final pack. Exact types are required even with explicit arguments (`Max(1, 2.5)` fails). Deduction recognizes T and array element T, never the T inside Box. When multiple ordinary/template functions share a name, every definition must specify OVERLOAD; exact ordinary definitions win, then non-variadic templates, then variadic templates, with same-tier ties ambiguous. Explicit arguments select templates only. Packs are final non-array parameters and expand only in call/constructor argument lists; pack fields are rejected, including in variadic classes. No non-type parameters, SIZEOF..., member/operator templates, specialization, bare injected class name or pointer-alias templates. Pointer-linked generic containers remain unsupported; use fixed fields or other supported value layouts. Class instances follow ordinary constructor rules, so a parameterized NEW alone does not permit `DECLARE B : Box` without arguments. Template instances share the first requester's visibility for their cached body; positional constants come from the template declaration. Instantiation depth is limited to 64. Empty source lists `<>` are invalid and lex as inequality; `>>` remains two tokens, but adjacent `<-` and `>=` merge. Expression lookahead treats `A < B, C > (D)` as a template call; parenthesize comparisons to avoid that ambiguity. - **OPERATOR extension.** Unary/binary operators require OVERLOAD FUNCTION, with supported arity and at least one record/class/enum operand. Pointer aliases alone do not qualify. Exact matching considers visible globals and left-class members diff --git a/AGENTS/SUBSYSTEMS.md b/AGENTS/SUBSYSTEMS.md index 23a5270..3112142 100644 --- a/AGENTS/SUBSYSTEMS.md +++ b/AGENTS/SUBSYSTEMS.md @@ -196,3 +196,15 @@ Builder (RuntimeCheck emits through the same one), saving/restoring the insert b **Class copies and temporaries.** Callers construct BYVAL class lvalue arguments with a same-class BYREF OVERLOAD NEW when defined, otherwise they copy bits; the callee's RegisterCopy hook takes ownership. emitOwnedValue constructs lvalue copies and keeps pending arguments Alive until marshalling succeeds. RETURN copies lvalues and transfers rvalues. Records and BYVAL arrays recursively clone STRING-bearing fields/elements. Other owning class fields retain the existing shallow-copy limits. StackObject::Temporary uses statement cleanup and deferred temporary-only cleanup before local handlers, while ordinary objects remain function-scoped. Function-result method receivers are materialized by the same path as operator receivers. Assignment first selects an exact member OPERATOR<-; without one, STRING-bearing aggregate assignment clones string fields from lvalues or transfers them from rvalues, releasing the target's old string fields without invoking the enclosing class destructor; other classes retain their existing bitwise assignment behavior. **Standard library** (`pseudocode-lib/`): the default build compiles each .cc with the host compiler into build/pseudocode-lib/*.o, copies .cps units beside them, and embeds all these outputs in cpsc. StandardLibrary extracts the resources through linking/execution; the driver implicitly imports std.cps unless -no-std is set. std.cps imports string.cps. STRING is a #LIB class with a public internal void* handle owning std::string. The _string_impl_* C ABI reports __cps_user_line and throws 0 on std::exception. Functions/operators use ordinary resolution; StringValueGen bridges literals, C_STR, C conversions and file operands. C_STRING literals have an adjacent uppercase C suffix. INPUT delegates to READ_INPUT, OUTPUT uses C stdout buffers, and READFILE invokes assignment from C_STRING then frees its temporary buffer. Source/object resources are embedded in installed/released cpsc; saved IR requires the object when manually linked. + +## Templates + +`TemplateDeclAST` owns a function/procedure or class definition plus type parameters; `PackExpansionExprAST` represents a value pack used in an argument list. The top-level name scan registers templates without treating them as executable code. After ordinary prototype registration, `validateTemplates` checks OVERLOAD consistency across all units, including invisible definitions. CONSTANT/GLOBAL name checks use template-aware callable lookup. + +`CodeGen::resolveCall` retains ordinary fast paths when no templates exist. Otherwise it computes static argument types and whole-array ranks once, selecting exact ordinary definitions before non-variadic templates and then variadic templates. Explicit type arguments pre-bind positional parameters and constrain a pack prefix; deduction from `T` or array element `T` fills remaining bindings. Composite types such as `Box` are checked after substitution but do not deduce T. Every position must match exactly and every non-pack type must be bound. Candidate failures stay silent; only the final no-match or ambiguity is diagnosed. `hasCallable` keeps CALL statement re-resolution aware of templates. + +`TemplateClone.{h,cc}` deep-clones body ASTs with structural type substitution, preserving source lines. Bindings map each type parameter to a list (one type for ordinary parameters). A final value pack expands into `Name#1` through `Name#N`; argument-list cloning expands references only in calls, member calls, NEW and DECLARE constructors. Class cloning substitutes fields, method types and owning class names; pack fields and nested function definitions are rejected. Add clone coverage whenever introducing a body AST node. + +`TemplateGen.cc` caches instances by source declaration and canonical type arguments, owns cloned ASTs for the compile lifetime and immediately registers selected function prototypes. Names are `Max`, with the parameter signature appended for OVERLOAD. FunctionGen's signature deques keep pointers stable while recursive instantiation appends signatures. `TypeSystem::resolve` has a miss hook for visible class templates, including diagnostics for bare template names. It resolves arguments, reserves and caches an opaque record, defines its fields, records the template owner in TypeOwners and uses the factored `registerClassMethods`/`emitClassPrototypes` paths. Prototypes requested during the type pre-pass are deferred until it completes. Failed type names are remembered to avoid repeating the instantiation error; speculative candidate checks do not record failures. `findMethod` forces type resolution for template ids before NEW/DELETE lookup. + +After all units' statement loops, `drainTemplateBodies` emits queued functions and class methods through `emitStmt`, repeating for newly requested instances until the queue is empty. Instantiation depth is limited to 64. `selectUnit` restores the template owner's file, library mode and call hook; `VisibilityUnit` uses the first requester's transitive visibility, including requester-defined operators needed by a library template. Each body receives the symbol snapshot from the template declaration, preserving positional own constants and imported constants. SaveAndRestore guards and unit restoration retain the caller's state. diff --git a/README.md b/README.md index 1d83814..e419a95 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ Details can be found here: [Pseudocode Guide for Teachers](https://www.cambridge - **Text files**: Support `OPENFILE`, `READFILE`, `WRITEFILE`, `CLOSEFILE` and `EOF`. - **User-defined types**: Support enumerated types, pointers and nested records. - **Classes**: Support public/private fields and methods, constructors, destructors, copying and operator overloads. +- **Templates**: Instantiate function, procedure and class [templates](doc/language/keyword/cpsc-keyword-template.7.md) with type deduction, explicit type arguments and variadic packs. - **Selection and constants**: Support `CASE OF` branches and named literal `CONSTANT` values. ## TODO diff --git a/doc/language/cpsc-language-overview.7.md b/doc/language/cpsc-language-overview.7.md index 1256e43..2106b90 100644 --- a/doc/language/cpsc-language-overview.7.md +++ b/doc/language/cpsc-language-overview.7.md @@ -44,6 +44,7 @@ Multi-file builds use the compiler extensions INCLUDE and GLOBAL. Pass every sou - [cpsc-keyword-procedure(7)](keyword/cpsc-keyword-procedure.7.md): define a procedure and parameter modes. - [cpsc-keyword-operator(7)](keyword/cpsc-keyword-operator.7.md): overload unary and binary operators. - [cpsc-keyword-overload(7)](keyword/cpsc-keyword-overload.7.md): explicitly overload functions, procedures and constructors. +- [cpsc-keyword-template(7)](keyword/cpsc-keyword-template.7.md): define function, procedure and class templates with type packs. - [cpsc-keyword-call(7)](keyword/cpsc-keyword-call.7.md): invoke a procedure. - [cpsc-keyword-openfile(7)](keyword/cpsc-keyword-openfile.7.md): open a text file. - [cpsc-keyword-readfile(7)](keyword/cpsc-keyword-readfile.7.md): read a line from a text file. diff --git a/doc/language/keyword/cpsc-keyword-class.7.md b/doc/language/keyword/cpsc-keyword-class.7.md index 139f97e..bcb299b 100644 --- a/doc/language/keyword/cpsc-keyword-class.7.md +++ b/doc/language/keyword/cpsc-keyword-class.7.md @@ -69,4 +69,4 @@ OUTPUT Item.Get() # SEE ALSO -[cpsc-keyword-overload(7)](cpsc-keyword-overload.7.md), [cpsc-language-overview(7)](../cpsc-language-overview.7.md), [cpsc-keyword-delete(7)](cpsc-keyword-delete.7.md), [cpsc-keyword-type(7)](cpsc-keyword-type.7.md), [cpsc-keyword-function(7)](cpsc-keyword-function.7.md), [cpsc-keyword-procedure(7)](cpsc-keyword-procedure.7.md), [cpsc-keyword-call(7)](cpsc-keyword-call.7.md). +[cpsc-keyword-template(7)](cpsc-keyword-template.7.md), [cpsc-keyword-overload(7)](cpsc-keyword-overload.7.md), [cpsc-language-overview(7)](../cpsc-language-overview.7.md), [cpsc-keyword-delete(7)](cpsc-keyword-delete.7.md), [cpsc-keyword-type(7)](cpsc-keyword-type.7.md), [cpsc-keyword-function(7)](cpsc-keyword-function.7.md), [cpsc-keyword-procedure(7)](cpsc-keyword-procedure.7.md), [cpsc-keyword-call(7)](cpsc-keyword-call.7.md). diff --git a/doc/language/keyword/cpsc-keyword-function.7.md b/doc/language/keyword/cpsc-keyword-function.7.md index 9f3839d..a7de9a4 100644 --- a/doc/language/keyword/cpsc-keyword-function.7.md +++ b/doc/language/keyword/cpsc-keyword-function.7.md @@ -38,4 +38,4 @@ OUTPUT Double(4) # SEE ALSO -[cpsc-keyword-overload(7)](cpsc-keyword-overload.7.md), [cpsc-language-overview(7)](../cpsc-language-overview.7.md), [cpsc-keyword-class(7)](cpsc-keyword-class.7.md), [cpsc-keyword-procedure(7)](cpsc-keyword-procedure.7.md), [cpsc-keyword-call(7)](cpsc-keyword-call.7.md). +[cpsc-keyword-template(7)](cpsc-keyword-template.7.md), [cpsc-keyword-overload(7)](cpsc-keyword-overload.7.md), [cpsc-language-overview(7)](../cpsc-language-overview.7.md), [cpsc-keyword-class(7)](cpsc-keyword-class.7.md), [cpsc-keyword-procedure(7)](cpsc-keyword-procedure.7.md), [cpsc-keyword-call(7)](cpsc-keyword-call.7.md). diff --git a/doc/language/keyword/cpsc-keyword-overload.7.md b/doc/language/keyword/cpsc-keyword-overload.7.md index 21ba7b7..4012172 100644 --- a/doc/language/keyword/cpsc-keyword-overload.7.md +++ b/doc/language/keyword/cpsc-keyword-overload.7.md @@ -49,4 +49,4 @@ OUTPUT Area(2.0) # SEE ALSO -[cpsc-keyword-function(7)](cpsc-keyword-function.7.md), [cpsc-keyword-procedure(7)](cpsc-keyword-procedure.7.md), [cpsc-keyword-class(7)](cpsc-keyword-class.7.md), [cpsc-keyword-call(7)](cpsc-keyword-call.7.md). +[cpsc-keyword-template(7)](cpsc-keyword-template.7.md), [cpsc-keyword-function(7)](cpsc-keyword-function.7.md), [cpsc-keyword-procedure(7)](cpsc-keyword-procedure.7.md), [cpsc-keyword-class(7)](cpsc-keyword-class.7.md), [cpsc-keyword-call(7)](cpsc-keyword-call.7.md). diff --git a/doc/language/keyword/cpsc-keyword-template.7.md b/doc/language/keyword/cpsc-keyword-template.7.md new file mode 100644 index 0000000..95dee8c --- /dev/null +++ b/doc/language/keyword/cpsc-keyword-template.7.md @@ -0,0 +1,77 @@ +% CPSC-KEYWORD-TEMPLATE(7) + +# NAME + +cpsc-keyword-template - define function, procedure and class templates + +# SYNOPSIS + +```text +TEMPLATE +[OVERLOAD] FUNCTION name(parameters) RETURNS type + statements +ENDFUNCTION +TEMPLATE +[OVERLOAD] PROCEDURE name(parameters) + statements +ENDPROCEDURE +TEMPLATE +CLASS name + fields and methods +ENDCLASS +``` + +# DESCRIPTION + +TEMPLATE is a compiler extension, absent from the CIE guide. Place it before a top-level FUNCTION, PROCEDURE or CLASS, before OVERLOAD when present. Every template parameter names a type; no typename keyword is needed. Names must be distinct. A single optional type pack, written `Rest...`, must come last. Each concrete use creates and caches an ordinary function or class instance by substituting types into the template body. + +Function calls deduce types from parameters typed `T` or `ARRAY OF T`; bounded array parameters also require matching rank. Deduction does not look inside `Box`. Explicit arguments, as in `Max(1.5, 2.0)`, bind types positionally before deduction fills any remaining parameters. All argument types must match exactly, including explicitly bound types: `Max(1, 2.5)` is an error. Explicit arguments are forbidden on ordinary functions and builtins. A type used only in RETURNS requires an explicit argument. + +A final parameter `Others : Rest...` expands into one parameter per pack type, possibly none. It cannot be an ARRAY parameter. Write `Others...` only in argument lists of function or method calls, NEW or DECLARE constructors. A bare pack value is invalid. In type argument lists, `Rest...` forwards the current type pack. Explicit pack arguments constrain a prefix; deduction can supply the remaining types. + +When multiple function or procedure definitions share a name, every definition, including ordinary definitions, must specify OVERLOAD. Selection prefers an exact ordinary definition, then a non-variadic template, then a variadic template. Two matches at the same level are ambiguous; no further template ordering is performed. Explicit template arguments select only templates. Normal BYREF, array-bound and access checks apply after selection; see [cpsc-keyword-overload(7)](cpsc-keyword-overload.7.md). + +Use a class instance such as `Box` wherever a type name is accepted, including nested instances such as `Box>`. Supply all class type arguments explicitly; variadic CLASS templates are allowed, and their methods may use the class's pack parameters. Pack fields such as `Values : Rest...` are rejected. Constructors, ownership and destruction follow ordinary CLASS rules. In particular, a declaration without constructor arguments needs a matching parameterless NEW when constructors exist; see [cpsc-keyword-class(7)](cpsc-keyword-class.7.md). + +Non-type parameters, SIZEOF..., member templates, operator templates, specialization and pointer-alias templates are unsupported. Within a class template, write the full instance type rather than the bare class name. Pointer-linked generic containers remain unsupported because pointer-alias templates are unavailable. + +Source template lists cannot be empty: `<>` is the inequality token. Nested closing `>>` works as two tokens. Separate `<` from a following `-` and `>` from a following `=` to avoid the `<-` and `>=` tokens. Expression lookahead treats `A < B, C > (D)` as a template call; parenthesize comparisons to avoid this ambiguity. + +# EXAMPLE + +```text +TEMPLATE +FUNCTION Max(A : T, B : T) RETURNS T + IF A > B THEN + RETURN A + ENDIF + RETURN B +ENDFUNCTION + +OVERLOAD PROCEDURE PrintAll() +ENDPROCEDURE +TEMPLATE +OVERLOAD PROCEDURE PrintAll(First : T, Others : Rest...) + OUTPUT First + CALL PrintAll(Others...) +ENDPROCEDURE + +TEMPLATE +CLASS Box + Value : T + OVERLOAD PROCEDURE NEW() + ENDPROCEDURE + OVERLOAD PROCEDURE NEW(V : T) + Value <- V + ENDPROCEDURE +ENDCLASS + +DECLARE B : Box +B.Value <- Max(3, 7) +OUTPUT Max(1.5, 2.0) +CALL PrintAll(B.Value, 2.5, 'c', "str") +``` + +# SEE ALSO + +[cpsc-keyword-overload(7)](cpsc-keyword-overload.7.md), [cpsc-keyword-class(7)](cpsc-keyword-class.7.md), [cpsc-keyword-function(7)](cpsc-keyword-function.7.md), [cpsc-keyword-procedure(7)](cpsc-keyword-procedure.7.md), [cpsc-keyword-declare(7)](cpsc-keyword-declare.7.md), [cpsc-language-overview(7)](../cpsc-language-overview.7.md). -- cgit v1.3.1-4-g156e