aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author_ov4 <ov4@ov4.org>2026-10-22 04:41:14 +0000
committer_ov4 <ov4@ov4.org>2026-10-22 04:41:14 +0000
commit620a1543b8ce8479592f0bf36a64640d3618e25d (patch)
treef2d61ad0cf4a6e715636017ad2c8cd509c56249f
parent31e443264ac621b812ec5a1a5b0f4b67e4cc25e3 (diff)
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 <noreply@anthropic.com>
-rw-r--r--AGENTS/ARCH/LAYOUT.md2
-rw-r--r--CMakeLists.txt1
-rw-r--r--lib/Parser/ParserTemplate.cc134
-rw-r--r--lib/Parser/ParserType.cc130
4 files changed, 136 insertions, 131 deletions
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 <T, Rest...> [OVERLOAD] FUNCTION/PROCEDURE or CLASS.
+// Invalid nesting still consumes the entire declaration before diagnosing it.
+std::unique_ptr<StmtAST> 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<TemplateParam> 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<StmtAST> 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<TemplateDeclAST>(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<TemplateDeclAST>(Params,
+ std::unique_ptr<ClassTypeDeclAST>(static_cast<ClassTypeDeclAST *>(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<TemplateDeclAST *>(Decl.get()); T->getClass()) TypeNames.insert(T->getName());
+ Decl->setLine(Line);
+ return Decl;
+}
+
+std::optional<std::vector<std::string>> Parser::ParseTemplateArgs() {
+ getNextToken(); // '<'
+ std::vector<std::string> 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 <T, Rest...> [OVERLOAD] FUNCTION/PROCEDURE or CLASS.
-// Invalid nesting still consumes the entire declaration before diagnosing it.
-std::unique_ptr<StmtAST> 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<TemplateParam> 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<StmtAST> 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<TemplateDeclAST>(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<TemplateDeclAST>(Params,
- std::unique_ptr<ClassTypeDeclAST>(static_cast<ClassTypeDeclAST *>(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<TemplateDeclAST *>(Decl.get()); T->getClass()) TypeNames.insert(T->getName());
- Decl->setLine(Line);
- return Decl;
-}
-
-std::optional<std::vector<std::string>> Parser::ParseTemplateArgs() {
- getNextToken(); // '<'
- std::vector<std::string> 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 <id> = (value1, value2, ...) enumerated type
// TYPE <id> = ^<type> pointer type
// TYPE <id> record type