From 344206d57e834b2394cbe2ec0df50b04a8b3c171 Mon Sep 17 00:00:00 2001 From: _ov4 Date: Sun, 26 Jul 2026 15:45:40 +0000 Subject: feat: implement CIE 9.1 text file handling (OPENFILE/READFILE/WRITEFILE/CLOSEFILE/EOF) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- include/cps/CodeGen.h | 3 ++ include/cps/FileAST.h | 63 ++++++++++++++++++++++++++++++++++++++ include/cps/FileHandler.h | 75 ++++++++++++++++++++++++++++++++++++++++++++++ include/cps/FileMode.h | 11 +++++++ include/cps/FunctionAST.h | 6 ++-- include/cps/Lexer.h | 11 ++++++- include/cps/Parser.h | 5 ++++ include/cps/RuntimeCheck.h | 7 +++++ 8 files changed, 178 insertions(+), 3 deletions(-) create mode 100644 include/cps/FileAST.h create mode 100644 include/cps/FileHandler.h create mode 100644 include/cps/FileMode.h (limited to 'include') 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 TheContext; @@ -33,6 +34,7 @@ class CodeGen { std::unique_ptr ArithHandler; std::unique_ptr StrHandler; std::unique_ptr StrConvHandler; + std::unique_ptr 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 +#include + +namespace cps { + +class OpenFileStmtAST : public StmtAST { + std::unique_ptr FileName; + FileMode Mode; + int Line; + +public: + OpenFileStmtAST(std::unique_ptr 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 FileName; + std::string VarName; + int Line; + +public: + ReadFileStmtAST(std::unique_ptr 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 FileName; + std::unique_ptr Data; + int Line; + +public: + WriteFileStmtAST(std::unique_ptr FileName, std::unique_ptr 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 FileName; + int Line; + +public: + CloseFileStmtAST(std::unique_ptr 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 + +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> Args; + int Line; public: - CallExprAST(const std::string &Callee, std::vector> Args) - : Callee(Callee), Args(std::move(Args)) {} + CallExprAST(const std::string &Callee, std::vector> Args, int Line) + : Callee(Callee), Args(std::move(Args)), Line(Line) {} const std::string &getCallee() const { return Callee; } const std::vector> &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 ParseReturnStmt(); std::optional>> ParsePrototypeArgs(); + std::unique_ptr ParseOpenFile(); + std::unique_ptr ParseReadFile(); + std::unique_ptr ParseWriteFile(); + std::unique_ptr ParseCloseFile(); + public: Parser(Lexer &L); std::vector> 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 FmtArgs); }; } -- cgit v1.3.1-4-g156e