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 --- lib/CodeGen/CodeGen.cc | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) (limited to 'lib/CodeGen/CodeGen.cc') diff --git a/lib/CodeGen/CodeGen.cc b/lib/CodeGen/CodeGen.cc index ffe10b1..ae82914 100644 --- a/lib/CodeGen/CodeGen.cc +++ b/lib/CodeGen/CodeGen.cc @@ -1,5 +1,7 @@ #include "cps/CodeGen.h" #include "cps/ArrayHandler.h" +#include "cps/FileAST.h" +#include "cps/FileHandler.h" #include "cps/Lexer.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Verifier.h" @@ -32,6 +34,7 @@ constexpr BuiltinInfo Builtins[] = { {"IS_NUM", 1, "BOOLEAN"}, {"NUM_TO_STR", 1, "STRING"}, {"STR_TO_NUM", 1, "REAL"}, + {"EOF", 1, "BOOLEAN"}, }; const BuiltinInfo *findBuiltin(const std::string &Name) { @@ -84,6 +87,7 @@ CodeGen::CodeGen() { ArithHandler = std::make_unique(*TheContext, *Builder, HadError); StrHandler = std::make_unique(*TheContext, *Builder, *TheModule); StrConvHandler = std::make_unique(*TheContext, *Builder, *TheModule); + Files = std::make_unique(*TheContext, *Builder, *TheModule, *RuntimeChecker); SetupExternalFunctions(); } @@ -144,6 +148,16 @@ const SymbolInfo *CodeGen::getSymbolInfo(const std::string &Name) const { return &It->second; } +// A whole-array variable reference evaluates to the raw buffer pointer, which +// the pointer->STRING coercion would accept unchanged; file-statement operands +// must reject it explicitly or garbage gets treated as a C string. +bool CodeGen::isWholeArrayVar(ExprAST *Expr) const { + auto *Var = dynamic_cast(Expr); + if (!Var) return false; + const SymbolInfo *Info = getSymbolInfo(Var->getName()); + return Info && Info->IsArray; +} + const TypeInfo *CodeGen::resolveType(const std::string &TypeName) const { return Types->resolve(TypeName); } @@ -590,6 +604,16 @@ Value *CodeGen::emitExpr(ExprAST *Expr) { if (!Str) return nullptr; return StrConvHandler->emitStrToNum(Str, true); } + if (Name == "EOF") { + if (isWholeArrayVar(Call->getArgs()[0].get())) { + reportError("Cannot use a whole array as the EOF filename"); + return nullptr; + } + Value *FileName = coerceValueToType(emitExpr(Call->getArgs()[0].get()), + resolveType("STRING")); + if (!FileName) return nullptr; + return Files->emitEof(FileName, Call->getLine()); + } std::vector Args; if (!marshalCallArgs(Name, Call->getArgs(), Args)) return nullptr; @@ -935,6 +959,67 @@ void CodeGen::emitStmt(StmtAST *Stmt) { return; } + if (auto *Open = dynamic_cast(Stmt)) { + if (isWholeArrayVar(Open->getFileName())) { + reportError("Cannot use a whole array as the OPENFILE filename"); + return; + } + Value *Name = coerceValueToType(emitExpr(Open->getFileName()), resolveType("STRING")); + if (!Name) return; + Files->emitOpen(Name, Open->getMode(), Open->getLine()); + return; + } + + if (auto *Read = dynamic_cast(Stmt)) { + const SymbolInfo *Info = getSymbolInfo(Read->getVarName()); + if (!Info) { + reportError("Unknown variable name %s", Read->getVarName().c_str()); + return; + } + if (Info->IsArray) { + reportError("READFILE into entire array %s is not supported", Read->getVarName().c_str()); + return; + } + if (Info->TypeName != "STRING") { + reportError("READFILE variable %s must be of type STRING (got %s)", + Read->getVarName().c_str(), Info->TypeName.c_str()); + return; + } + if (isWholeArrayVar(Read->getFileName())) { + reportError("Cannot use a whole array as the READFILE filename"); + return; + } + Value *Name = coerceValueToType(emitExpr(Read->getFileName()), resolveType("STRING")); + if (!Name) return; + Value *LineStr = Files->emitRead(Name, Read->getLine()); + if (!LineStr) return; + Builder->CreateStore(LineStr, Info->Storage); + return; + } + + if (auto *Write = dynamic_cast(Stmt)) { + if (isWholeArrayVar(Write->getFileName()) || isWholeArrayVar(Write->getData())) { + reportError("Cannot use a whole array in WRITEFILE"); + return; + } + Value *Name = coerceValueToType(emitExpr(Write->getFileName()), resolveType("STRING")); + Value *Data = coerceValueToType(emitExpr(Write->getData()), resolveType("STRING")); + if (!Name || !Data) return; + Files->emitWrite(Name, Data, Write->getLine()); + return; + } + + if (auto *Close = dynamic_cast(Stmt)) { + if (isWholeArrayVar(Close->getFileName())) { + reportError("Cannot use a whole array as the CLOSEFILE filename"); + return; + } + Value *Name = coerceValueToType(emitExpr(Close->getFileName()), resolveType("STRING")); + if (!Name) return; + Files->emitClose(Name, Close->getLine()); + return; + } + if (auto *IfStmt = dynamic_cast(Stmt)) { emitIfStmt(IfStmt); return; -- cgit v1.3.1-4-g156e