aboutsummaryrefslogtreecommitdiff
path: root/lib/Parser/ParserFunction.cc
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 /lib/Parser/ParserFunction.cc
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 'lib/Parser/ParserFunction.cc')
-rw-r--r--lib/Parser/ParserFunction.cc101
1 files changed, 84 insertions, 17 deletions
diff --git a/lib/Parser/ParserFunction.cc b/lib/Parser/ParserFunction.cc
index 7663f38..a46cd0f 100644
--- a/lib/Parser/ParserFunction.cc
+++ b/lib/Parser/ParserFunction.cc
@@ -1,45 +1,112 @@
#include "cps/Parser.h"
#include "cps/FunctionAST.h"
#include <cstdio>
-#include <tuple>
using namespace cps;
-std::optional<std::vector<std::tuple<std::string, std::string, bool>>> Parser::ParsePrototypeArgs() {
- std::vector<std::tuple<std::string, std::string, bool>> Args;
- if (CurTok != '(') return Args;
+// Array-parameter bounds live in the signature, not the AST, so they must be
+// integer literals (an optional leading '-' is allowed).
+bool Parser::ParseSignedIntBound(int64_t &Out) {
+ bool Negative = false;
+ if (CurTok == '-') {
+ Negative = true;
+ getNextToken();
+ }
+ if (CurTok != tok_number_int) {
+ fprintf(stderr, "Error: Array parameter bounds must be integer literals at line %d\n",
+ Lex.getLine());
+ return false;
+ }
+ Out = Negative ? -Lex.NumVal : Lex.NumVal;
+ getNextToken();
+ return true;
+}
+
+// ( [BYREF|BYVAL] <name> : <type>
+// | <name> : ARRAY OF <type> -- rank 1, bounds from the argument
+// | <name> : ARRAY[<int>:<int>,...] OF <type> , ... )
+// The pass mode is sticky across parameters (guide §8.3: "the BYVAL or BYREF
+// keyword need not be repeated"); unannotated parameters default to BYVAL
+// (§8.1). BYREF is procedure-only (§8.3), gated by AllowByRef.
+std::optional<std::vector<ParamDecl>> Parser::ParsePrototypeArgs(bool AllowByRef) {
+ std::vector<ParamDecl> Params;
+ if (CurTok != '(') return Params;
getNextToken();
+ PassMode Current = PassMode::ByVal;
while (CurTok != ')') {
- bool IsRef = false;
if (CurTok == tok_byref) {
- IsRef = true;
+ if (!AllowByRef) {
+ fprintf(stderr, "Error: BYREF parameters are not allowed in a FUNCTION (procedures only) at line %d\n",
+ Lex.getLine());
+ return std::nullopt;
+ }
+ Current = PassMode::ByRef;
getNextToken();
} else if (CurTok == tok_byval) {
- IsRef = false;
+ Current = PassMode::ByVal;
getNextToken();
}
if (CurTok != tok_identifier) {
- fprintf(stderr, "Error: Expected argument name\n");
+ fprintf(stderr, "Error: Expected argument name at line %d\n", Lex.getLine());
return std::nullopt;
}
- std::string Name = Lex.IdentifierStr;
+ ParamDecl P;
+ P.Name = Lex.IdentifierStr;
+ P.Mode = Current;
getNextToken();
if (CurTok != tok_colon) {
- fprintf(stderr, "Error: Expected ':' after argument name\n");
+ fprintf(stderr, "Error: Expected ':' after argument name at line %d\n", Lex.getLine());
return std::nullopt;
}
getNextToken();
- std::string Type = ParseTypeName(false);
- if (Type.empty()) {
- fprintf(stderr, "Error: Expected argument type for '%s'\n", Name.c_str());
+ if (CurTok == tok_array) {
+ getNextToken();
+ P.IsArray = true;
+ if (CurTok == '[') {
+ getNextToken();
+ while (true) {
+ int64_t Lo = 0, Hi = 0;
+ if (!ParseSignedIntBound(Lo)) return std::nullopt;
+ if (CurTok != tok_colon) {
+ fprintf(stderr, "Error: Expected ':' between array parameter bounds at line %d\n",
+ Lex.getLine());
+ return std::nullopt;
+ }
+ getNextToken();
+ if (!ParseSignedIntBound(Hi)) return std::nullopt;
+ P.DeclaredBounds.emplace_back(Lo, Hi);
+ if (CurTok == ']') break;
+ if (CurTok != ',') {
+ fprintf(stderr, "Error: Expected ',' or ']' in array parameter bounds at line %d\n",
+ Lex.getLine());
+ return std::nullopt;
+ }
+ getNextToken();
+ }
+ getNextToken(); // eat ']'
+ P.Rank = static_cast<int>(P.DeclaredBounds.size());
+ } else {
+ P.Rank = 1; // ARRAY OF T: bounds travel with the argument
+ }
+ if (CurTok != tok_of) {
+ fprintf(stderr, "Error: Expected OF after ARRAY in parameter '%s' at line %d\n",
+ P.Name.c_str(), Lex.getLine());
+ return std::nullopt;
+ }
+ getNextToken();
+ }
+
+ P.TypeName = ParseTypeName(false);
+ if (P.TypeName.empty()) {
+ fprintf(stderr, "Error: Expected argument type for '%s'\n", P.Name.c_str());
return std::nullopt;
}
- Args.emplace_back(Name, Type, IsRef);
+ Params.push_back(std::move(P));
if (CurTok == ')') break;
if (CurTok != ',') {
@@ -49,7 +116,7 @@ std::optional<std::vector<std::tuple<std::string, std::string, bool>>> Parser::P
getNextToken();
}
getNextToken();
- return Args;
+ return Params;
}
std::unique_ptr<StmtAST> Parser::ParseFunction() {
@@ -62,7 +129,7 @@ std::unique_ptr<StmtAST> Parser::ParseFunction() {
std::string Name = Lex.IdentifierStr;
getNextToken();
- auto Args = ParsePrototypeArgs();
+ auto Args = ParsePrototypeArgs(/*AllowByRef=*/false);
if (!Args) return nullptr;
std::string RetType = "INTEGER";
@@ -98,7 +165,7 @@ std::unique_ptr<StmtAST> Parser::ParseProcedure() {
std::string Name = Lex.IdentifierStr;
getNextToken();
- auto Args = ParsePrototypeArgs();
+ auto Args = ParsePrototypeArgs(/*AllowByRef=*/true);
if (!Args) return nullptr;
auto Proto = std::make_unique<PrototypeAST>(Name, std::move(*Args), "VOID");