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/ArithmeticHandler.cc | 60 ++++++++++++++++++++++++++++++---------- 1 file changed, 45 insertions(+), 15 deletions(-) (limited to 'lib/CodeGen/ArithmeticHandler.cc') diff --git a/lib/CodeGen/ArithmeticHandler.cc b/lib/CodeGen/ArithmeticHandler.cc index bfee395..439e4b1 100644 --- a/lib/CodeGen/ArithmeticHandler.cc +++ b/lib/CodeGen/ArithmeticHandler.cc @@ -5,6 +5,16 @@ using namespace llvm; using namespace cps; +// Widen an i1 (BOOLEAN) or i8 (CHAR) operand to i64 with the same ZExt +// coerceValueToType uses, so a mixed pair like EOF(f) - 1 cannot reach the +// instruction builders with mismatched integer widths (invalid IR). +static Value *widenSmallInt(IRBuilder<> &Builder, LLVMContext &Context, Value *V) { + Type *Ty = V->getType(); + if (Ty->isIntegerTy() && Ty->getIntegerBitWidth() < 64) + return Builder.CreateZExt(V, Type::getInt64Ty(Context), "widetmp"); + return V; +} + Value *ArithmeticHandler::emitBinaryOp(int Op, Value *LHS, Value *RHS, int Line) { bool LIsInt = LHS->getType()->isIntegerTy(64); bool RIsInt = RHS->getType()->isIntegerTy(64); @@ -12,11 +22,16 @@ Value *ArithmeticHandler::emitBinaryOp(int Op, Value *LHS, Value *RHS, int Line) bool RIsDouble = RHS->getType()->isDoubleTy(); if (Op == '/') { - Value *LVal = LHS; - Value *RVal = RHS; - if (LIsInt) LVal = Builder.CreateSIToFP(LHS, Type::getDoubleTy(Context)); - if (RIsInt) RVal = Builder.CreateSIToFP(RHS, Type::getDoubleTy(Context)); - + Value *LVal = widenSmallInt(Builder, Context, LHS); + Value *RVal = widenSmallInt(Builder, Context, RHS); + if (LVal->getType()->isIntegerTy(64)) LVal = Builder.CreateSIToFP(LVal, Type::getDoubleTy(Context)); + if (RVal->getType()->isIntegerTy(64)) RVal = Builder.CreateSIToFP(RVal, Type::getDoubleTy(Context)); + if (!LVal->getType()->isDoubleTy() || !RVal->getType()->isDoubleTy()) { + fprintf(stderr, "Error: '/' requires numeric operands (line %d).\n", Line); + HadError = true; + return nullptr; + } + // TODO: DIV divide by 0 runtime check return Builder.CreateFDiv(LVal, RVal, "divtmp"); } @@ -68,17 +83,32 @@ Value *ArithmeticHandler::emitBinaryOp(int Op, Value *LHS, Value *RHS, int Line) case tok_ge: return Builder.CreateFCmpOGE(LVal, RVal, "sgetmp"); } } else { + // Same-type pairs (i64/i64, i8/i8 CHAR, i1/i1, ptr/ptr STRING) go + // through untouched; mixed integer widths are widened, anything else + // mismatched is a diagnostic instead of invalid IR. + Value *L = LHS; + Value *R = RHS; + if (L->getType() != R->getType() && + L->getType()->isIntegerTy() && R->getType()->isIntegerTy()) { + L = widenSmallInt(Builder, Context, L); + R = widenSmallInt(Builder, Context, R); + } + if (L->getType() != R->getType()) { + fprintf(stderr, "Error: Mismatched operand types for binary operator (line %d).\n", Line); + HadError = true; + return nullptr; + } switch (Op) { - case '+': return Builder.CreateAdd(LHS, RHS, "addtmp"); - case '-': return Builder.CreateSub(LHS, RHS, "subtmp"); - case '*': return Builder.CreateMul(LHS, RHS, "multmp"); - - case tok_eq: return Builder.CreateICmpEQ(LHS, RHS, "eqtmp"); - case tok_ne: return Builder.CreateICmpNE(LHS, RHS, "netmp"); - case '<': return Builder.CreateICmpSLT(LHS, RHS, "slttmp"); - case '>': return Builder.CreateICmpSGT(LHS, RHS, "sgttmp"); - case tok_le: return Builder.CreateICmpSLE(LHS, RHS, "sletmp"); - case tok_ge: return Builder.CreateICmpSGE(LHS, RHS, "sgetmp"); + case '+': return Builder.CreateAdd(L, R, "addtmp"); + case '-': return Builder.CreateSub(L, R, "subtmp"); + case '*': return Builder.CreateMul(L, R, "multmp"); + + case tok_eq: return Builder.CreateICmpEQ(L, R, "eqtmp"); + case tok_ne: return Builder.CreateICmpNE(L, R, "netmp"); + case '<': return Builder.CreateICmpSLT(L, R, "slttmp"); + case '>': return Builder.CreateICmpSGT(L, R, "sgttmp"); + case tok_le: return Builder.CreateICmpSLE(L, R, "sletmp"); + case tok_ge: return Builder.CreateICmpSGE(L, R, "sgetmp"); } } -- cgit v1.3.1-4-g156e