From 3366a2e0fae05d9e792169e74471c7282bf18681 Mon Sep 17 00:00:00 2001 From: _ov4 Date: Sun, 26 Jul 2026 17:21:14 +0000 Subject: feat: TYPE-system front end — tokens, designators, ParamDecl grammar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Reserve TYPE/ENDTYPE/SET/DEFINE (-600 token block); SET/DEFINE parse to dedicated not-implemented errors (RANDOM precedent). - New TypeAST.h: enum/pointer/record decl nodes, designator chain (DesignatorExprAST + Index/Field/Deref accessors), AddrOfExprAST, DesignatorAssignStmtAST. Bare-name and single-index shapes degrade to their legacy nodes, so existing programs produce identical ASTs. - ParserType.cc: TYPE declarations (top-level only, enforced via a ParseBlock depth counter), DECLARE-only record bodies with local field-level error recovery. - Postfix [ ] . ^ chain in ParseIdentifierExpr; prefix ^ (address-of, designator operand only) in ParseUnary; assignment targets rebuilt on the same suffix parser. - Parameters: ParamDecl{name, type, mode, array rank/bounds} replaces the (name, type, byref) tuple. Pass mode is sticky across parameters and BYREF is rejected in FUNCTIONs, both per guide §8.3 (the old per-param reset made the guide's own SWAP example pass Y by value). ARRAY OF T / ARRAY[l:u,...] OF T parameter syntax parses; codegen rejects it until the parameter-passing phase lands. - CodeGen stubs report clear not-implemented errors for the new nodes so nothing is silently ignored. Co-Authored-By: Claude Fable 5 --- include/cps/FunctionAST.h | 28 ++++++++--- include/cps/FunctionGen.h | 2 +- include/cps/Lexer.h | 7 ++- include/cps/Parser.h | 13 ++++- include/cps/TypeAST.h | 120 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 160 insertions(+), 10 deletions(-) create mode 100644 include/cps/TypeAST.h (limited to 'include') diff --git a/include/cps/FunctionAST.h b/include/cps/FunctionAST.h index 613079c..f663867 100644 --- a/include/cps/FunctionAST.h +++ b/include/cps/FunctionAST.h @@ -1,27 +1,43 @@ #pragma once #include "cps/AST.h" +#include #include #include #include -#include +#include namespace cps { +enum class PassMode : uint8_t { ByVal, ByRef }; + +// One declared FUNCTION/PROCEDURE parameter. For array parameters TypeName +// is the ELEMENT type; DeclaredBounds holds literal per-dimension bounds for +// the bounded form (empty for `ARRAY OF T`, which is rank 1 with the bounds +// travelling alongside the argument). +struct ParamDecl { + std::string Name; + std::string TypeName; + PassMode Mode = PassMode::ByVal; + bool IsArray = false; + int Rank = 0; // 0 = scalar + std::vector> DeclaredBounds; +}; + class PrototypeAST { std::string Name; - std::vector> Args; + std::vector Params; std::string ReturnType; bool IsExternal; public: - PrototypeAST(const std::string &Name, - std::vector> Args, + PrototypeAST(const std::string &Name, + std::vector Params, const std::string &ReturnType, bool IsExternal = false) - : Name(Name), Args(std::move(Args)), ReturnType(ReturnType), IsExternal(IsExternal) {} + : Name(Name), Params(std::move(Params)), ReturnType(ReturnType), IsExternal(IsExternal) {} const std::string &getName() const { return Name; } - const std::vector> &getArgs() const { return Args; } + const std::vector &getParams() const { return Params; } const std::string &getReturnType() const { return ReturnType; } bool isExternal() const { return IsExternal; } }; diff --git a/include/cps/FunctionGen.h b/include/cps/FunctionGen.h index cce17c3..9bb6eef 100644 --- a/include/cps/FunctionGen.h +++ b/include/cps/FunctionGen.h @@ -30,7 +30,7 @@ class FunctionGen { std::map Signatures; - void createArgumentAllocas(llvm::Function *F, const std::vector> &Args); + void createArgumentAllocas(llvm::Function *F, const std::vector &Params); public: FunctionGen(llvm::LLVMContext &C, diff --git a/include/cps/Lexer.h b/include/cps/Lexer.h index 81fbd99..2af0260 100644 --- a/include/cps/Lexer.h +++ b/include/cps/Lexer.h @@ -78,7 +78,12 @@ enum Token { tok_read = -504, tok_write = -505, tok_append = -506, - tok_random = -507 + tok_random = -507, + + tok_type = -600, + tok_endtype = -601, + tok_set = -602, // reserved: SET types are recognised but not implemented + tok_define = -603 // reserved: DEFINE (set literals) likewise }; class Lexer { diff --git a/include/cps/Parser.h b/include/cps/Parser.h index 0ecb7f9..49f418c 100644 --- a/include/cps/Parser.h +++ b/include/cps/Parser.h @@ -1,12 +1,14 @@ #pragma once #include "cps/Lexer.h" #include "cps/AST.h" +#include "cps/TypeAST.h" +#include "cps/FunctionAST.h" +#include #include #include #include #include #include -#include #include namespace cps { @@ -15,6 +17,7 @@ class Parser { Lexer &Lex; int CurTok; unsigned NumErrors = 0; + unsigned BlockDepth = 0; // ParseBlock nesting; 1 = top level std::map BinopPrecedence; @@ -42,12 +45,18 @@ class Parser { std::unique_ptr ParseForStmt(); std::unique_ptr ParseDeclare(); + std::unique_ptr ParseAssignStmt(); + bool ParseDesignatorSuffix(std::vector &Out); + + std::unique_ptr ParseTypeDecl(); + bool ParseRecordField(std::vector &Out); std::unique_ptr ParseFunction(); std::unique_ptr ParseProcedure(); std::unique_ptr ParseCallStmt(); std::unique_ptr ParseReturnStmt(); - std::optional>> ParsePrototypeArgs(); + std::optional> ParsePrototypeArgs(bool AllowByRef); + bool ParseSignedIntBound(int64_t &Out); std::unique_ptr ParseOpenFile(); std::unique_ptr ParseReadFile(); diff --git a/include/cps/TypeAST.h b/include/cps/TypeAST.h new file mode 100644 index 0000000..d349859 --- /dev/null +++ b/include/cps/TypeAST.h @@ -0,0 +1,120 @@ +#pragma once +#include "cps/AST.h" +#include +#include +#include +#include +#include + +namespace cps { + +// ---- User-defined type declarations (CIE 9618 §4.1) ---- + +// One field of a record TYPE body. Plain data, not a statement node, so the +// emitStmt chain can never execute a field by accident. +struct RecordFieldDecl { + std::string Name; + std::string TypeName; +}; + +// TYPE = (value1, value2, ...) +class EnumTypeDeclAST : public StmtAST { + std::string Name; + std::vector ValueNames; + +public: + EnumTypeDeclAST(const std::string &Name, std::vector ValueNames) + : Name(Name), ValueNames(std::move(ValueNames)) {} + + const std::string &getName() const { return Name; } + const std::vector &getValueNames() const { return ValueNames; } +}; + +// TYPE = ^ +class PointerTypeDeclAST : public StmtAST { + std::string Name; + std::string PointeeTypeName; + +public: + PointerTypeDeclAST(const std::string &Name, const std::string &PointeeTypeName) + : Name(Name), PointeeTypeName(PointeeTypeName) {} + + const std::string &getName() const { return Name; } + const std::string &getPointeeTypeName() const { return PointeeTypeName; } +}; + +// TYPE +// DECLARE : +// ... +// ENDTYPE +class RecordTypeDeclAST : public StmtAST { + std::string Name; + std::vector Fields; + +public: + RecordTypeDeclAST(const std::string &Name, std::vector Fields) + : Name(Name), Fields(std::move(Fields)) {} + + const std::string &getName() const { return Name; } + const std::vector &getFields() const { return Fields; } +}; + +// ---- Designators: chained accesses usable as lvalue or rvalue (§4.2) ---- + +enum class AccessKind : uint8_t { Index, Field, Deref }; + +// One postfix accessor: [indices], .Field, or ^ (dereference). +struct DesignatorAccess { + AccessKind Kind; + std::vector> Indices; // Index only + std::string FieldName; // Field only +}; + +// followed by one or more accessors, e.g. Form[i].YearGroup, p^. +// Bare names and single-index accesses keep their legacy nodes (parser +// degrade rule); a designator node always carries at least one accessor +// beyond those shapes, except as the operand of AddrOfExprAST. +class DesignatorExprAST : public ExprAST { + std::string BaseName; + std::vector Accesses; + int Line; + +public: + DesignatorExprAST(const std::string &BaseName, + std::vector Accesses, + int Line) + : BaseName(BaseName), Accesses(std::move(Accesses)), Line(Line) {} + + const std::string &getBaseName() const { return BaseName; } + const std::vector &getAccesses() const { return Accesses; } + int getLine() const { return Line; } +}; + +// ^ — address-of. The operand is a designator by construction, +// so an lvalue is guaranteed at parse time. +class AddrOfExprAST : public ExprAST { + std::unique_ptr Target; + +public: + explicit AddrOfExprAST(std::unique_ptr Target) + : Target(std::move(Target)) {} + + DesignatorExprAST *getTarget() const { return Target.get(); } +}; + +// <- for targets beyond a bare name or single indexing +// (those degrade to AssignStmtAST / ArrayAssignStmtAST in the parser). +class DesignatorAssignStmtAST : public StmtAST { + std::unique_ptr Target; + std::unique_ptr Expr; + +public: + DesignatorAssignStmtAST(std::unique_ptr Target, + std::unique_ptr Expr) + : Target(std::move(Target)), Expr(std::move(Expr)) {} + + DesignatorExprAST *getTarget() const { return Target.get(); } + ExprAST *getExpr() const { return Expr.get(); } +}; + +} // namespace cps -- cgit v1.3.1-4-g156e