aboutsummaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
author_ov4 <ov4@ov4.org>2026-07-26 15:45:40 +0000
committer_ov4 <ov4@ov4.org>2026-07-26 15:45:40 +0000
commit344206d57e834b2394cbe2ec0df50b04a8b3c171 (patch)
tree0eb282bc9878bf990496a5d840ca8492b3cfc508 /include
parentd1c47a328785ece9a8ffd9bbca3138818b8203b3 (diff)
feat: implement CIE 9.1 text file handling (OPENFILE/READFILE/WRITEFILE/CLOSEFILE/EOF)
Front end: OPENFILE/READFILE/WRITEFILE/CLOSEFILE plus reserved mode keywords READ/WRITE/APPEND (RANDOM reserved with a dedicated not-implemented error); statement nodes in new FileAST.h, parsing in new ParserFile.cc; EOF joins the Builtins table as the 12th builtin and CallExprAST now carries a line number for its runtime diagnostics. Runtime: new FileHandler (StringHandler-shaped leaf) lazily synthesizes internal __cps_file_* helper functions and a 16-slot open-file table keyed by filename content (strcmp — STRING '=' is pointer identity). EOF is an fgetc/ungetc peek; READFILE grows a malloc/realloc line buffer and strips a trailing CR; fopen uses text modes so mingw-w64 line translation works. File-free programs get byte-identical IR. All misuse (not open, wrong mode, reopen, fopen failure, read past EOF, table full) dies with the existing "[Fatal] line N:" pattern via a new RuntimeCheck::emitErrorAndExit overload taking runtime printf args. Also fixes a pre-existing ArithmeticHandler hole surfaced by adversarial review: mixed-width integer operands (i1 BOOLEAN / i8 CHAR vs i64) now widen via ZExt instead of emitting invalid IR, and remaining type mismatches get a clean diagnostic; -EOF(f) and -IS_NUM(x) compile. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Diffstat (limited to 'include')
-rw-r--r--include/cps/CodeGen.h3
-rw-r--r--include/cps/FileAST.h63
-rw-r--r--include/cps/FileHandler.h75
-rw-r--r--include/cps/FileMode.h11
-rw-r--r--include/cps/FunctionAST.h6
-rw-r--r--include/cps/Lexer.h11
-rw-r--r--include/cps/Parser.h5
-rw-r--r--include/cps/RuntimeCheck.h7
8 files changed, 178 insertions, 3 deletions
diff --git a/include/cps/CodeGen.h b/include/cps/CodeGen.h
index 140d1f5..43d3a32 100644
--- a/include/cps/CodeGen.h
+++ b/include/cps/CodeGen.h
@@ -18,6 +18,7 @@
namespace cps {
class ArrayHandler;
+class FileHandler;
class CodeGen {
std::unique_ptr<llvm::LLVMContext> TheContext;
@@ -33,6 +34,7 @@ class CodeGen {
std::unique_ptr<ArithmeticHandler> ArithHandler;
std::unique_ptr<StringHandler> StrHandler;
std::unique_ptr<StringConversionHandler> StrConvHandler;
+ std::unique_ptr<FileHandler> Files;
llvm::FunctionCallee PrintfFunc;
llvm::FunctionCallee ScanfFunc;
@@ -59,6 +61,7 @@ class CodeGen {
void registerSymbol(const std::string &Name, llvm::Value *Storage, const std::string &TypeName, bool IsArray = false);
const SymbolInfo *getSymbolInfo(const std::string &Name) const;
+ bool isWholeArrayVar(ExprAST *Expr) const;
const TypeInfo *getExprTypeInfo(ExprAST *Expr) const;
llvm::Value *coerceValueToType(llvm::Value *Val, const TypeInfo *TargetInfo);
bool marshalCallArgs(const std::string &Callee,
diff --git a/include/cps/FileAST.h b/include/cps/FileAST.h
new file mode 100644
index 0000000..04bd06a
--- /dev/null
+++ b/include/cps/FileAST.h
@@ -0,0 +1,63 @@
+#pragma once
+#include "cps/AST.h"
+#include "cps/FileMode.h"
+#include <memory>
+#include <string>
+
+namespace cps {
+
+class OpenFileStmtAST : public StmtAST {
+ std::unique_ptr<ExprAST> FileName;
+ FileMode Mode;
+ int Line;
+
+public:
+ OpenFileStmtAST(std::unique_ptr<ExprAST> FileName, FileMode Mode, int Line)
+ : FileName(std::move(FileName)), Mode(Mode), Line(Line) {}
+
+ ExprAST *getFileName() const { return FileName.get(); }
+ FileMode getMode() const { return Mode; }
+ int getLine() const { return Line; }
+};
+
+class ReadFileStmtAST : public StmtAST {
+ std::unique_ptr<ExprAST> FileName;
+ std::string VarName;
+ int Line;
+
+public:
+ ReadFileStmtAST(std::unique_ptr<ExprAST> FileName, const std::string &VarName, int Line)
+ : FileName(std::move(FileName)), VarName(VarName), Line(Line) {}
+
+ ExprAST *getFileName() const { return FileName.get(); }
+ const std::string &getVarName() const { return VarName; }
+ int getLine() const { return Line; }
+};
+
+class WriteFileStmtAST : public StmtAST {
+ std::unique_ptr<ExprAST> FileName;
+ std::unique_ptr<ExprAST> Data;
+ int Line;
+
+public:
+ WriteFileStmtAST(std::unique_ptr<ExprAST> FileName, std::unique_ptr<ExprAST> Data, int Line)
+ : FileName(std::move(FileName)), Data(std::move(Data)), Line(Line) {}
+
+ ExprAST *getFileName() const { return FileName.get(); }
+ ExprAST *getData() const { return Data.get(); }
+ int getLine() const { return Line; }
+};
+
+class CloseFileStmtAST : public StmtAST {
+ std::unique_ptr<ExprAST> FileName;
+ int Line;
+
+public:
+ CloseFileStmtAST(std::unique_ptr<ExprAST> FileName, int Line)
+ : FileName(std::move(FileName)), Line(Line) {}
+
+ ExprAST *getFileName() const { return FileName.get(); }
+ int getLine() const { return Line; }
+};
+
+} // namespace cps
diff --git a/include/cps/FileHandler.h b/include/cps/FileHandler.h
new file mode 100644
index 0000000..aaad6a0
--- /dev/null
+++ b/include/cps/FileHandler.h
@@ -0,0 +1,75 @@
+#pragma once
+#include "cps/FileMode.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+
+namespace cps {
+
+class RuntimeCheck;
+
+// Emits the text-file runtime: a fixed-size open-file table plus internal
+// __cps_file_* helper functions, synthesized once on first use so programs
+// without file statements keep their IR unchanged. The table is keyed by
+// filename *content* (strcmp) — pseudocode STRING equality is pointer
+// identity, so the same file may be named by many different buffers.
+// Leaf module: methods only take and return llvm::Value*s; expression
+// evaluation, type coercion and the symbol table stay in CodeGen.
+class FileHandler {
+ llvm::LLVMContext &Context;
+ llvm::IRBuilder<> &Builder;
+ llvm::Module &Module;
+ RuntimeCheck &Checker;
+
+ static constexpr uint64_t TableSize = 16;
+ bool RuntimeEmitted = false;
+
+ // libc — declared lazily on first file use
+ llvm::FunctionCallee FopenFunc, FcloseFunc, FgetcFunc, UngetcFunc,
+ FputsFunc, FputcFunc, MallocFunc, ReallocFunc,
+ StrcmpFunc;
+
+ // open-file table
+ llvm::GlobalVariable *FileNames = nullptr; // [TableSize x ptr] STRING passed to OPENFILE
+ llvm::GlobalVariable *FileHandles = nullptr; // [TableSize x ptr] FILE*
+ llvm::GlobalVariable *FileModes = nullptr; // [TableSize x i8] 0 = free slot, else FileMode
+
+ llvm::Value *ModeReadStr = nullptr, *ModeWriteStr = nullptr, *ModeAppendStr = nullptr;
+ llvm::Value *ErrOpenFail = nullptr, *ErrAlreadyOpen = nullptr, *ErrTableFull = nullptr,
+ *ErrNotOpen = nullptr, *ErrNotRead = nullptr, *ErrNotWrite = nullptr,
+ *ErrPastEof = nullptr;
+
+ // synthesized helpers (internal linkage; pseudocode identifiers cannot
+ // start with '_', so these names are un-collidable)
+ llvm::Function *FindFn = nullptr, *OpenFn = nullptr, *ReadFn = nullptr,
+ *WriteFn = nullptr, *CloseFn = nullptr, *EofFn = nullptr;
+
+ void setupExternalFunctions();
+ void setupGlobals();
+ void ensureRuntime();
+
+ llvm::Function *createHelper(const char *Name, llvm::FunctionType *FT);
+ llvm::Value *emitSlotGEP(llvm::GlobalVariable *Table, llvm::Type *ElemTy, llvm::Value *Idx);
+ llvm::Value *emitLookupOrFail(llvm::Value *Name, llvm::Value *Line);
+ void buildFindFn();
+ void buildOpenFn();
+ void buildReadFn();
+ void buildWriteFn();
+ void buildCloseFn();
+ void buildEofFn();
+
+public:
+ FileHandler(llvm::LLVMContext &Ctx, llvm::IRBuilder<> &B, llvm::Module &M,
+ RuntimeCheck &RC);
+
+ // NameStr/DataStr are already-coerced STRING ptrs; Line is the statement's
+ // source line for runtime diagnostics. All methods null-check and bail;
+ // the caller (CodeGen) does the diagnostics.
+ void emitOpen(llvm::Value *NameStr, FileMode Mode, int Line);
+ llvm::Value *emitRead(llvm::Value *NameStr, int Line); // returns STRING ptr
+ void emitWrite(llvm::Value *NameStr, llvm::Value *DataStr, int Line);
+ void emitClose(llvm::Value *NameStr, int Line);
+ llvm::Value *emitEof(llvm::Value *NameStr, int Line); // returns i1
+};
+
+} // namespace cps
diff --git a/include/cps/FileMode.h b/include/cps/FileMode.h
new file mode 100644
index 0000000..b95e615
--- /dev/null
+++ b/include/cps/FileMode.h
@@ -0,0 +1,11 @@
+#pragma once
+#include <cstdint>
+
+namespace cps {
+
+// Shared between FileAST (parser side) and FileHandler (codegen side).
+// The numeric values are the mode byte stored in the runtime file table;
+// 0 is reserved to mean "free slot".
+enum class FileMode : uint8_t { Read = 1, Write = 2, Append = 3 };
+
+} // namespace cps
diff --git a/include/cps/FunctionAST.h b/include/cps/FunctionAST.h
index 5b4da6c..613079c 100644
--- a/include/cps/FunctionAST.h
+++ b/include/cps/FunctionAST.h
@@ -42,13 +42,15 @@ public:
class CallExprAST : public ExprAST {
std::string Callee;
std::vector<std::unique_ptr<ExprAST>> Args;
+ int Line;
public:
- CallExprAST(const std::string &Callee, std::vector<std::unique_ptr<ExprAST>> Args)
- : Callee(Callee), Args(std::move(Args)) {}
+ CallExprAST(const std::string &Callee, std::vector<std::unique_ptr<ExprAST>> Args, int Line)
+ : Callee(Callee), Args(std::move(Args)), Line(Line) {}
const std::string &getCallee() const { return Callee; }
const std::vector<std::unique_ptr<ExprAST>> &getArgs() const { return Args; }
+ int getLine() const { return Line; }
};
class CallStmtAST : public StmtAST {
diff --git a/include/cps/Lexer.h b/include/cps/Lexer.h
index 8984398..81fbd99 100644
--- a/include/cps/Lexer.h
+++ b/include/cps/Lexer.h
@@ -69,7 +69,16 @@ enum Token {
tok_endprocedure = -405,
tok_call = -406,
tok_byref = -407,
- tok_byval = -408
+ tok_byval = -408,
+
+ tok_openfile = -500,
+ tok_readfile = -501,
+ tok_writefile = -502,
+ tok_closefile = -503,
+ tok_read = -504,
+ tok_write = -505,
+ tok_append = -506,
+ tok_random = -507
};
class Lexer {
diff --git a/include/cps/Parser.h b/include/cps/Parser.h
index 7d19c77..0ecb7f9 100644
--- a/include/cps/Parser.h
+++ b/include/cps/Parser.h
@@ -49,6 +49,11 @@ class Parser {
std::unique_ptr<StmtAST> ParseReturnStmt();
std::optional<std::vector<std::tuple<std::string, std::string, bool>>> ParsePrototypeArgs();
+ std::unique_ptr<StmtAST> ParseOpenFile();
+ std::unique_ptr<StmtAST> ParseReadFile();
+ std::unique_ptr<StmtAST> ParseWriteFile();
+ std::unique_ptr<StmtAST> ParseCloseFile();
+
public:
Parser(Lexer &L);
std::vector<std::unique_ptr<StmtAST>> Parse();
diff --git a/include/cps/RuntimeCheck.h b/include/cps/RuntimeCheck.h
index 3c31f41..97f4765 100644
--- a/include/cps/RuntimeCheck.h
+++ b/include/cps/RuntimeCheck.h
@@ -23,6 +23,13 @@ public:
void emitDivZeroCheck(llvm::Value *Divisor, int Line);
void emitIndexCheck(llvm::Value *Index, llvm::Value *Lower, llvm::Value *Upper, int Line);
+
+ // Runtime-computed variant: FmtArgs are the printf varargs after Msg
+ // (e.g. {i32 line, ptr filename}). Needed when the failure site is inside
+ // a synthesized helper function, where the line number is an argument
+ // rather than a compile-time constant.
+ void emitErrorAndExit(llvm::Value *Condition, llvm::Value *Msg,
+ llvm::ArrayRef<llvm::Value *> FmtArgs);
};
}