From 620a1543b8ce8479592f0bf36a64640d3618e25d Mon Sep 17 00:00:00 2001 From: _ov4 Date: Thu, 22 Oct 2026 04:41:14 +0000 Subject: refactor(parser): move TEMPLATE declaration and argument parsing into ParserTemplate.cc Pure move of isTemplatePack, ParseTemplateDecl and ParseTemplateArgs out of ParserType.cc; no behavior change. Co-Authored-By: Claude Sonnet 5.5 --- AGENTS/ARCH/LAYOUT.md | 2 +- CMakeLists.txt | 1 + lib/Parser/ParserTemplate.cc | 134 +++++++++++++++++++++++++++++++++++++++++++ lib/Parser/ParserType.cc | 130 ----------------------------------------- 4 files changed, 136 insertions(+), 131 deletions(-) create mode 100644 lib/Parser/ParserTemplate.cc diff --git a/AGENTS/ARCH/LAYOUT.md b/AGENTS/ARCH/LAYOUT.md index 2586cf1..a88c60b 100644 --- a/AGENTS/ARCH/LAYOUT.md +++ b/AGENTS/ARCH/LAYOUT.md @@ -9,7 +9,7 @@ | `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 | +| `include/cps/TemplateAST.h`, `lib/Parser/ParserTemplate.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 and main() compilation | | `lib/CodeGen/CallGen.cc` | Call-argument marshalling (`marshalCallArgs`, variadic arguments) and call resolution (`resolveCall`) | | `lib/CodeGen/ExpressionGen.cc`, `StatementGen.cc`, `ControlFlowGen.cc` | Expression/statement dispatch and control-flow emission | diff --git a/CMakeLists.txt b/CMakeLists.txt index 993c56b..e1cf41b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,6 +50,7 @@ add_library(CPSParser STATIC lib/Parser/ParserFunction.cc lib/Parser/ParserFile.cc lib/Parser/ParserType.cc + lib/Parser/ParserTemplate.cc ) add_library(CPSCodeGen STATIC diff --git a/lib/Parser/ParserTemplate.cc b/lib/Parser/ParserTemplate.cc new file mode 100644 index 0000000..4e6f9d7 --- /dev/null +++ b/lib/Parser/ParserTemplate.cc @@ -0,0 +1,134 @@ +#include "cps/Diagnostics.h" +#include "cps/Parser.h" +#include "cps/TypeAST.h" + +using namespace cps; +bool Parser::isTemplatePack(const std::string &Name) const { + for (const auto &P : CurrentTemplate) + if (P.IsPack && sameIdentifier(P.Name, Name)) return true; + return false; +} + +// TEMPLATE [OVERLOAD] FUNCTION/PROCEDURE or CLASS. +// Invalid nesting still consumes the entire declaration before diagnosing it. +std::unique_ptr Parser::ParseTemplateDecl() { + int Line = Lex.getLine(); + bool AtTopLevel = BlockDepth == 1 && CurrentTemplate.empty(); + getNextToken(); + if (CurTok != '<') { + sourceError(Line, "Expected '<' after TEMPLATE"); + return nullptr; + } + getNextToken(); + std::vector Params; + IdentifierSet Names; + bool Invalid = false; + bool SeenPack = false; + while (true) { + if (CurTok != tok_identifier) { + sourceError(Lex.getLine(), "Expected template parameter name"); + return nullptr; + } + TemplateParam P{Lex.IdentifierStr, false, Lex.getLine()}; + if (!Names.insert(P.Name).second) { + sourceError(P.Line, "Duplicate template parameter '%s'", P.Name.c_str()); + Invalid = true; + } + getNextToken(); + if (CurTok == tok_ellipsis) { + P.IsPack = true; + if (SeenPack) { + sourceError(P.Line, "A template may have only one parameter pack"); + Invalid = true; + } + SeenPack = true; + getNextToken(); + } + Params.push_back(std::move(P)); + if (CurTok == '>') break; + if (CurTok != ',') { + sourceError(Lex.getLine(), "Expected ',' or '>' in TEMPLATE"); + return nullptr; + } + if (Params.back().IsPack) { + sourceError(Params.back().Line, "A template parameter pack must be last"); + Invalid = true; + } + getNextToken(); + } + getNextToken(); + + auto SavedTemplate = std::move(CurrentTemplate); + auto SavedTypes = TypeNames; + CurrentTemplate = Params; + for (const auto &P : Params) TypeNames.insert(P.Name); + bool IsOverload = CurTok == tok_overload; + if (IsOverload) getNextToken(); + std::unique_ptr Decl; + if (CurTok == tok_function || CurTok == tok_procedure) { + bool Operator = Lex.peekToken() == tok_operator; + if (Operator) { + sourceError(Line, "Operator templates are not supported"); + Invalid = true; + int End = CurTok == tok_function ? tok_endfunction : tok_endprocedure; + do { getNextToken(); } while (CurTok != End && CurTok != tok_eof); + if (CurTok == End) getNextToken(); + } else { + auto Function = CurTok == tok_function ? ParseFunction({}, true, IsOverload) + : ParseProcedure({}, true, IsOverload); + if (Function) Decl = std::make_unique(Params, std::move(Function)); + } + } else if (CurTok == tok_class && !IsOverload) { + int ClassLine = Lex.getLine(); + auto Class = ParseTypeDecl(); + if (Class) { + Class->setLine(ClassLine); + Decl = std::make_unique(Params, + std::unique_ptr(static_cast(Class.release()))); + } + } else if (CurTok == tok_type) { + sourceError(Line, "TEMPLATE cannot prefix TYPE; use CLASS"); + ParseTypeDecl(); + Invalid = true; + } else { + sourceError(Line, "TEMPLATE must be followed by FUNCTION, PROCEDURE or CLASS"); + Invalid = true; + } + CurrentTemplate = std::move(SavedTemplate); + TypeNames = std::move(SavedTypes); + if (!AtTopLevel) { + sourceError(Line, "TEMPLATE declarations are only allowed at top level"); + Invalid = true; + } + if (Invalid || !Decl) return nullptr; + if (auto *T = dynamic_cast(Decl.get()); T->getClass()) TypeNames.insert(T->getName()); + Decl->setLine(Line); + return Decl; +} + +std::optional> Parser::ParseTemplateArgs() { + getNextToken(); // '<' + std::vector Args; + while (true) { + int Line = Lex.getLine(); + std::string Type = ParseTypeName(false); + if (Type.empty()) return std::nullopt; + if (CurTok == tok_ellipsis) { + if (!isTemplatePack(Type)) { + sourceError(Line, "Only the current template parameter pack may use '...' in a type argument"); + return std::nullopt; + } + Type += "..."; + getNextToken(); + } + Args.push_back(std::move(Type)); + if (CurTok == '>') break; + if (CurTok != ',') { + sourceError(Lex.getLine(), "Expected ',' or '>' in template arguments"); + return std::nullopt; + } + getNextToken(); + } + getNextToken(); + return Args; +} diff --git a/lib/Parser/ParserType.cc b/lib/Parser/ParserType.cc index ae4621c..976d165 100644 --- a/lib/Parser/ParserType.cc +++ b/lib/Parser/ParserType.cc @@ -4,136 +4,6 @@ using namespace cps; -bool Parser::isTemplatePack(const std::string &Name) const { - for (const auto &P : CurrentTemplate) - if (P.IsPack && sameIdentifier(P.Name, Name)) return true; - return false; -} - -// TEMPLATE [OVERLOAD] FUNCTION/PROCEDURE or CLASS. -// Invalid nesting still consumes the entire declaration before diagnosing it. -std::unique_ptr Parser::ParseTemplateDecl() { - int Line = Lex.getLine(); - bool AtTopLevel = BlockDepth == 1 && CurrentTemplate.empty(); - getNextToken(); - if (CurTok != '<') { - sourceError(Line, "Expected '<' after TEMPLATE"); - return nullptr; - } - getNextToken(); - std::vector Params; - IdentifierSet Names; - bool Invalid = false; - bool SeenPack = false; - while (true) { - if (CurTok != tok_identifier) { - sourceError(Lex.getLine(), "Expected template parameter name"); - return nullptr; - } - TemplateParam P{Lex.IdentifierStr, false, Lex.getLine()}; - if (!Names.insert(P.Name).second) { - sourceError(P.Line, "Duplicate template parameter '%s'", P.Name.c_str()); - Invalid = true; - } - getNextToken(); - if (CurTok == tok_ellipsis) { - P.IsPack = true; - if (SeenPack) { - sourceError(P.Line, "A template may have only one parameter pack"); - Invalid = true; - } - SeenPack = true; - getNextToken(); - } - Params.push_back(std::move(P)); - if (CurTok == '>') break; - if (CurTok != ',') { - sourceError(Lex.getLine(), "Expected ',' or '>' in TEMPLATE"); - return nullptr; - } - if (Params.back().IsPack) { - sourceError(Params.back().Line, "A template parameter pack must be last"); - Invalid = true; - } - getNextToken(); - } - getNextToken(); - - auto SavedTemplate = std::move(CurrentTemplate); - auto SavedTypes = TypeNames; - CurrentTemplate = Params; - for (const auto &P : Params) TypeNames.insert(P.Name); - bool IsOverload = CurTok == tok_overload; - if (IsOverload) getNextToken(); - std::unique_ptr Decl; - if (CurTok == tok_function || CurTok == tok_procedure) { - bool Operator = Lex.peekToken() == tok_operator; - if (Operator) { - sourceError(Line, "Operator templates are not supported"); - Invalid = true; - int End = CurTok == tok_function ? tok_endfunction : tok_endprocedure; - do { getNextToken(); } while (CurTok != End && CurTok != tok_eof); - if (CurTok == End) getNextToken(); - } else { - auto Function = CurTok == tok_function ? ParseFunction({}, true, IsOverload) - : ParseProcedure({}, true, IsOverload); - if (Function) Decl = std::make_unique(Params, std::move(Function)); - } - } else if (CurTok == tok_class && !IsOverload) { - int ClassLine = Lex.getLine(); - auto Class = ParseTypeDecl(); - if (Class) { - Class->setLine(ClassLine); - Decl = std::make_unique(Params, - std::unique_ptr(static_cast(Class.release()))); - } - } else if (CurTok == tok_type) { - sourceError(Line, "TEMPLATE cannot prefix TYPE; use CLASS"); - ParseTypeDecl(); - Invalid = true; - } else { - sourceError(Line, "TEMPLATE must be followed by FUNCTION, PROCEDURE or CLASS"); - Invalid = true; - } - CurrentTemplate = std::move(SavedTemplate); - TypeNames = std::move(SavedTypes); - if (!AtTopLevel) { - sourceError(Line, "TEMPLATE declarations are only allowed at top level"); - Invalid = true; - } - if (Invalid || !Decl) return nullptr; - if (auto *T = dynamic_cast(Decl.get()); T->getClass()) TypeNames.insert(T->getName()); - Decl->setLine(Line); - return Decl; -} - -std::optional> Parser::ParseTemplateArgs() { - getNextToken(); // '<' - std::vector Args; - while (true) { - int Line = Lex.getLine(); - std::string Type = ParseTypeName(false); - if (Type.empty()) return std::nullopt; - if (CurTok == tok_ellipsis) { - if (!isTemplatePack(Type)) { - sourceError(Line, "Only the current template parameter pack may use '...' in a type argument"); - return std::nullopt; - } - Type += "..."; - getNextToken(); - } - Args.push_back(std::move(Type)); - if (CurTok == '>') break; - if (CurTok != ',') { - sourceError(Lex.getLine(), "Expected ',' or '>' in template arguments"); - return std::nullopt; - } - getNextToken(); - } - getNextToken(); - return Args; -} - // TYPE = (value1, value2, ...) enumerated type // TYPE = ^ pointer type // TYPE record type -- cgit v1.3.1-4-g156e