From f5a2541ed3faf9a942b867e2d54577352153afce Mon Sep 17 00:00:00 2001 From: _ov4 Date: Tue, 13 Oct 2026 13:40:57 +0000 Subject: Refactor record fields to preserve visibility and share parsing Co-Authored-By: Claude Fable 5.1 --- include/cps/Parser.h | 4 +++- include/cps/TypeAST.h | 1 + include/cps/TypeSystem.h | 5 +++- lib/CodeGen/Designator.cc | 5 +--- lib/CodeGen/TypeSystem.cc | 8 +++++-- lib/Parser/ParserType.cc | 61 +++++++++++++++++++++++++---------------------- 6 files changed, 48 insertions(+), 36 deletions(-) diff --git a/include/cps/Parser.h b/include/cps/Parser.h index 49f418c..7344015 100644 --- a/include/cps/Parser.h +++ b/include/cps/Parser.h @@ -49,7 +49,9 @@ class Parser { bool ParseDesignatorSuffix(std::vector &Out); std::unique_ptr ParseTypeDecl(); - bool ParseRecordField(std::vector &Out); + std::optional> ParseRecordBody( + const std::string &Name, int Line, int EndToken); + bool ParseRecordField(std::vector &Out, bool IsPublic); std::unique_ptr ParseFunction(); std::unique_ptr ParseProcedure(); diff --git a/include/cps/TypeAST.h b/include/cps/TypeAST.h index d349859..601b49b 100644 --- a/include/cps/TypeAST.h +++ b/include/cps/TypeAST.h @@ -15,6 +15,7 @@ namespace cps { struct RecordFieldDecl { std::string Name; std::string TypeName; + bool IsPublic = true; // TYPE fields are always public. }; // TYPE = (value1, value2, ...) diff --git a/include/cps/TypeSystem.h b/include/cps/TypeSystem.h index 06ffe96..2ef1cd6 100644 --- a/include/cps/TypeSystem.h +++ b/include/cps/TypeSystem.h @@ -11,6 +11,8 @@ namespace cps { +struct RecordFieldDecl; + // Enum ordinals are 0-based: DECLARE zero-init and array memset(0) then // produce the first enumerator, a valid value. inline constexpr int64_t kEnumOrdinalBase = 0; @@ -35,6 +37,7 @@ enum class TypeKind { struct RecordFieldInfo { std::string TypeName; unsigned Index = 0; // struct field index + bool IsPublic = true; }; struct RecordPayload { @@ -117,7 +120,7 @@ public: const std::vector &Values, std::string &Err); bool defineRecord(const std::string &Name, - const std::vector> &Fields, + const std::vector &Fields, std::string &Err); bool definePointer(const std::string &Name, const std::string &PointeeName, diff --git a/lib/CodeGen/Designator.cc b/lib/CodeGen/Designator.cc index ad0cc49..640ca0b 100644 --- a/lib/CodeGen/Designator.cc +++ b/lib/CodeGen/Designator.cc @@ -58,10 +58,7 @@ void CodeGen::runTypePrePass(const std::vector> &Statem } else if (auto *P = dynamic_cast(S.get())) { Ok = Types->definePointer(P->getName(), P->getPointeeTypeName(), Err); } else if (auto *R = dynamic_cast(S.get())) { - std::vector> Fields; - for (const RecordFieldDecl &F : R->getFields()) - Fields.emplace_back(F.Name, F.TypeName); - Ok = Types->defineRecord(R->getName(), Fields, Err); + Ok = Types->defineRecord(R->getName(), R->getFields(), Err); } if (!Ok) reportError("%s", Err.c_str()); } diff --git a/lib/CodeGen/TypeSystem.cc b/lib/CodeGen/TypeSystem.cc index c170be8..f693ba1 100644 --- a/lib/CodeGen/TypeSystem.cc +++ b/lib/CodeGen/TypeSystem.cc @@ -1,4 +1,5 @@ #include "cps/TypeSystem.h" +#include "cps/TypeAST.h" #include "llvm/IR/Constants.h" #include @@ -185,7 +186,7 @@ bool TypeSystem::defineEnum(const std::string &Name, } bool TypeSystem::defineRecord(const std::string &Name, - const std::vector> &Fields, + const std::vector &Fields, std::string &Err) { auto It = Types.find(Name); if (It == Types.end() || It->second.Kind != TypeKind::Record) { @@ -195,7 +196,9 @@ bool TypeSystem::defineRecord(const std::string &Name, std::vector FieldTypes; RecordPayload Payload; - for (const auto &[FieldName, FieldTypeName] : Fields) { + for (const RecordFieldDecl &Field : Fields) { + const std::string &FieldName = Field.Name; + const std::string &FieldTypeName = Field.TypeName; if (Payload.findField(FieldName)) { Err = "Duplicate field '" + FieldName + "' in record " + Name; return false; @@ -222,6 +225,7 @@ bool TypeSystem::defineRecord(const std::string &Name, RecordFieldInfo FI; FI.TypeName = FieldInfo->Name; FI.Index = static_cast(FieldTypes.size()); + FI.IsPublic = Field.IsPublic; Payload.Fields.emplace_back(FieldName, FI); FieldTypes.push_back(FieldInfo->LLVMType); } diff --git a/lib/Parser/ParserType.cc b/lib/Parser/ParserType.cc index 8be7e46..ae671d5 100644 --- a/lib/Parser/ParserType.cc +++ b/lib/Parser/ParserType.cc @@ -66,30 +66,9 @@ std::unique_ptr Parser::ParseTypeDecl() { return nullptr; } } else { - // Record body: DECLARE-only lines, so nothing here can leak into the - // statement chain. One bad field yields one diagnostic; parsing then - // resumes at the next field or ENDTYPE. - std::vector Fields; - bool HadFieldError = false; - while (CurTok == tok_declare) { - if (!ParseRecordField(Fields)) { - HadFieldError = true; - while (CurTok != tok_declare && CurTok != tok_endtype && CurTok != tok_eof) - getNextToken(); - } - } - if (CurTok != tok_endtype) { - fprintf(stderr, "error: Expected DECLARE or ENDTYPE in TYPE %s at line %d\n", - Name.c_str(), Lex.getLine()); - return nullptr; // token left for the outer recovery - } - getNextToken(); // eat ENDTYPE - if (Fields.empty()) { - fprintf(stderr, "error: Record TYPE %s has no fields at line %d\n", Name.c_str(), Line); - return nullptr; - } - if (HadFieldError) return nullptr; - Decl = std::make_unique(Name, std::move(Fields)); + auto Fields = ParseRecordBody(Name, Line, tok_endtype); + if (!Fields) return nullptr; + Decl = std::make_unique(Name, std::move(*Fields)); } if (!AtTopLevel) { @@ -99,9 +78,35 @@ std::unique_ptr Parser::ParseTypeDecl() { return Decl; } -// DECLARE : — one record field. Array fields are deferred. -bool Parser::ParseRecordField(std::vector &Out) { - getNextToken(); // eat DECLARE +// Shared record body and field parsing. Fields are data, never statements. +std::optional> Parser::ParseRecordBody( + const std::string &Name, int Line, int EndToken) { + std::vector Fields; + bool HadFieldError = false; + while (CurTok == tok_declare) { + if (!ParseRecordField(Fields, true)) { + HadFieldError = true; + while (CurTok != tok_declare && CurTok != EndToken && CurTok != tok_eof) + getNextToken(); + } + } + if (CurTok != EndToken) { + fprintf(stderr, "error: Expected DECLARE or ENDTYPE in TYPE %s at line %d\n", + Name.c_str(), Lex.getLine()); + return std::nullopt; + } + getNextToken(); // eat body terminator + if (Fields.empty()) { + fprintf(stderr, "error: Record TYPE %s has no fields at line %d\n", Name.c_str(), Line); + return std::nullopt; + } + if (HadFieldError) return std::nullopt; + return Fields; +} + +// : . Array fields are deferred. +bool Parser::ParseRecordField(std::vector &Out, bool IsPublic) { + getNextToken(); // eat field introducer if (CurTok != tok_identifier) { fprintf(stderr, "error: Expected field name after DECLARE at line %d\n", Lex.getLine()); return false; @@ -125,6 +130,6 @@ bool Parser::ParseRecordField(std::vector &Out) { std::string TypeName = ParseTypeName(false); if (TypeName.empty()) return false; - Out.push_back({FieldName, TypeName}); + Out.push_back({FieldName, TypeName, IsPublic}); return true; } -- cgit v1.3.1-4-g156e