aboutsummaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
author_ov4 <ov4@ov4.org>2026-07-26 17:21:14 +0000
committer_ov4 <ov4@ov4.org>2026-07-26 17:21:14 +0000
commit3366a2e0fae05d9e792169e74471c7282bf18681 (patch)
tree0cc4cacdac7d32ee7671d6799c52f74953c2007e /include
parent0fef2986daa2945c9e3d050c0f750d56792b30bd (diff)
feat: TYPE-system front end — tokens, designators, ParamDecl grammar
- 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 <noreply@anthropic.com>
Diffstat (limited to 'include')
-rw-r--r--include/cps/FunctionAST.h28
-rw-r--r--include/cps/FunctionGen.h2
-rw-r--r--include/cps/Lexer.h7
-rw-r--r--include/cps/Parser.h13
-rw-r--r--include/cps/TypeAST.h120
5 files changed, 160 insertions, 10 deletions
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 <cstdint>
#include <vector>
#include <string>
#include <memory>
-#include <tuple>
+#include <utility>
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<std::pair<int64_t, int64_t>> DeclaredBounds;
+};
+
class PrototypeAST {
std::string Name;
- std::vector<std::tuple<std::string, std::string, bool>> Args;
+ std::vector<ParamDecl> Params;
std::string ReturnType;
bool IsExternal;
public:
- PrototypeAST(const std::string &Name,
- std::vector<std::tuple<std::string, std::string, bool>> Args,
+ PrototypeAST(const std::string &Name,
+ std::vector<ParamDecl> 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<std::tuple<std::string, std::string, bool>> &getArgs() const { return Args; }
+ const std::vector<ParamDecl> &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<std::string, FuncSig> Signatures;
- void createArgumentAllocas(llvm::Function *F, const std::vector<std::tuple<std::string, std::string, bool>> &Args);
+ void createArgumentAllocas(llvm::Function *F, const std::vector<ParamDecl> &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 <cstdint>
#include <vector>
#include <initializer_list>
#include <memory>
#include <map>
#include <optional>
-#include <tuple>
#include <string>
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<int, int> BinopPrecedence;
@@ -42,12 +45,18 @@ class Parser {
std::unique_ptr<StmtAST> ParseForStmt();
std::unique_ptr<StmtAST> ParseDeclare();
+ std::unique_ptr<StmtAST> ParseAssignStmt();
+ bool ParseDesignatorSuffix(std::vector<DesignatorAccess> &Out);
+
+ std::unique_ptr<StmtAST> ParseTypeDecl();
+ bool ParseRecordField(std::vector<RecordFieldDecl> &Out);
std::unique_ptr<StmtAST> ParseFunction();
std::unique_ptr<StmtAST> ParseProcedure();
std::unique_ptr<StmtAST> ParseCallStmt();
std::unique_ptr<StmtAST> ParseReturnStmt();
- std::optional<std::vector<std::tuple<std::string, std::string, bool>>> ParsePrototypeArgs();
+ std::optional<std::vector<ParamDecl>> ParsePrototypeArgs(bool AllowByRef);
+ bool ParseSignedIntBound(int64_t &Out);
std::unique_ptr<StmtAST> ParseOpenFile();
std::unique_ptr<StmtAST> 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 <cstdint>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+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 <name> = (value1, value2, ...)
+class EnumTypeDeclAST : public StmtAST {
+ std::string Name;
+ std::vector<std::string> ValueNames;
+
+public:
+ EnumTypeDeclAST(const std::string &Name, std::vector<std::string> ValueNames)
+ : Name(Name), ValueNames(std::move(ValueNames)) {}
+
+ const std::string &getName() const { return Name; }
+ const std::vector<std::string> &getValueNames() const { return ValueNames; }
+};
+
+// TYPE <name> = ^<pointee 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 <name>
+// DECLARE <field> : <type>
+// ...
+// ENDTYPE
+class RecordTypeDeclAST : public StmtAST {
+ std::string Name;
+ std::vector<RecordFieldDecl> Fields;
+
+public:
+ RecordTypeDeclAST(const std::string &Name, std::vector<RecordFieldDecl> Fields)
+ : Name(Name), Fields(std::move(Fields)) {}
+
+ const std::string &getName() const { return Name; }
+ const std::vector<RecordFieldDecl> &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<std::unique_ptr<ExprAST>> Indices; // Index only
+ std::string FieldName; // Field only
+};
+
+// <base name> 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<DesignatorAccess> Accesses;
+ int Line;
+
+public:
+ DesignatorExprAST(const std::string &BaseName,
+ std::vector<DesignatorAccess> Accesses,
+ int Line)
+ : BaseName(BaseName), Accesses(std::move(Accesses)), Line(Line) {}
+
+ const std::string &getBaseName() const { return BaseName; }
+ const std::vector<DesignatorAccess> &getAccesses() const { return Accesses; }
+ int getLine() const { return Line; }
+};
+
+// ^<designator> — address-of. The operand is a designator by construction,
+// so an lvalue is guaranteed at parse time.
+class AddrOfExprAST : public ExprAST {
+ std::unique_ptr<DesignatorExprAST> Target;
+
+public:
+ explicit AddrOfExprAST(std::unique_ptr<DesignatorExprAST> Target)
+ : Target(std::move(Target)) {}
+
+ DesignatorExprAST *getTarget() const { return Target.get(); }
+};
+
+// <designator> <- <expr> for targets beyond a bare name or single indexing
+// (those degrade to AssignStmtAST / ArrayAssignStmtAST in the parser).
+class DesignatorAssignStmtAST : public StmtAST {
+ std::unique_ptr<DesignatorExprAST> Target;
+ std::unique_ptr<ExprAST> Expr;
+
+public:
+ DesignatorAssignStmtAST(std::unique_ptr<DesignatorExprAST> Target,
+ std::unique_ptr<ExprAST> Expr)
+ : Target(std::move(Target)), Expr(std::move(Expr)) {}
+
+ DesignatorExprAST *getTarget() const { return Target.get(); }
+ ExprAST *getExpr() const { return Expr.get(); }
+};
+
+} // namespace cps