From cc7b9b34e99fa17357e14ac69f5fb2d1535b5512 Mon Sep 17 00:00:00 2001 From: _ov4 Date: Tue, 3 Mar 2026 07:22:19 +0000 Subject: Added all built-in functions --- CMakeLists.txt | 2 + include/cps/CharHandler.h | 23 +++++++++++ include/cps/CodeGen.h | 4 ++ include/cps/Lexer.h | 1 + include/cps/StringConversionHandler.h | 33 ++++++++++++++++ include/cps/StringHandler.h | 1 + lib/CodeGen/ArithmeticHandler.cc | 26 +++++++++++++ lib/CodeGen/CharHandler.cc | 15 +++++++ lib/CodeGen/CodeGen.cc | 41 ++++++++++++++++++++ lib/CodeGen/StringConversionHandler.cc | 71 ++++++++++++++++++++++++++++++++++ lib/CodeGen/StringHandler.cc | 29 ++++++++++++++ lib/Lexer/Lexer.cc | 18 +++++++-- lib/Parser/Parser.cc | 2 + 13 files changed, 262 insertions(+), 4 deletions(-) create mode 100644 include/cps/CharHandler.h create mode 100644 include/cps/StringConversionHandler.h create mode 100644 lib/CodeGen/CharHandler.cc create mode 100644 lib/CodeGen/StringConversionHandler.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 0763129..acc96a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,8 @@ add_library(CPSCodeGen lib/CodeGen/BooleanHandler.cc lib/CodeGen/ArithmeticHandler.cc lib/CodeGen/StringHandler.cc + lib/CodeGen/CharHandler.cc + lib/CodeGen/StringConversionHandler.cc ) llvm_map_components_to_libnames(llvm_libs core support native irreader) diff --git a/include/cps/CharHandler.h b/include/cps/CharHandler.h new file mode 100644 index 0000000..03c86e7 --- /dev/null +++ b/include/cps/CharHandler.h @@ -0,0 +1,23 @@ +#pragma once +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" + +namespace cps { + +class CharHandler { + llvm::LLVMContext &Context; + llvm::IRBuilder<> &Builder; + llvm::Module &Module; + +public: + CharHandler(llvm::LLVMContext &Ctx, llvm::IRBuilder<> &B, llvm::Module &M); + + // ASC(ThisChar: CHAR) RETURNS INTEGER + llvm::Value *emitAsc(llvm::Value *CharVal); + + // CHR(x: INTEGER) RETURNS CHAR + llvm::Value *emitChr(llvm::Value *IntVal); +}; + +} // namespace cps diff --git a/include/cps/CodeGen.h b/include/cps/CodeGen.h index cb8cecb..121f30d 100644 --- a/include/cps/CodeGen.h +++ b/include/cps/CodeGen.h @@ -11,6 +11,8 @@ #include "cps/BooleanHandler.h" #include "cps/ArithmeticHandler.h" #include "cps/StringHandler.h" +#include "cps/CharHandler.h" +#include "cps/StringConversionHandler.h" #include #include @@ -34,6 +36,8 @@ class CodeGen { std::unique_ptr BoolHandler; std::unique_ptr ArithHandler; std::unique_ptr StrHandler; + std::unique_ptr ChrHandler; + std::unique_ptr StrConvHandler; llvm::FunctionCallee PrintfFunc; llvm::FunctionCallee ScanfFunc; diff --git a/include/cps/Lexer.h b/include/cps/Lexer.h index c36599f..df84d0b 100644 --- a/include/cps/Lexer.h +++ b/include/cps/Lexer.h @@ -48,6 +48,7 @@ enum Token { tok_lcase = -34, tok_ucase = -35, tok_string_literal = -36, + tok_left = -37, tok_identifier = -100, tok_number_int = -101, diff --git a/include/cps/StringConversionHandler.h b/include/cps/StringConversionHandler.h new file mode 100644 index 0000000..db493bf --- /dev/null +++ b/include/cps/StringConversionHandler.h @@ -0,0 +1,33 @@ +#pragma once +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" + +namespace cps { + +class StringConversionHandler { + llvm::LLVMContext &Context; + llvm::IRBuilder<> &Builder; + llvm::Module &Module; + + llvm::FunctionCallee SprintfFunc; + llvm::FunctionCallee StrtolFunc; + llvm::FunctionCallee StrtodFunc; + llvm::FunctionCallee MallocFunc; + +public: + StringConversionHandler(llvm::LLVMContext &Ctx, llvm::IRBuilder<> &B, llvm::Module &M); + + void setupExternalFunctions(); + + // NUM TO STR(x) RETURNS STRING + llvm::Value *emitNumToStr(llvm::Value *Num, bool IsReal); + + // STR TO NUM(x) RETURNS NUMBER + llvm::Value *emitStrToNum(llvm::Value *Str, bool AsReal); + + // IS NUM(ThisString) RETURNS BOOLEAN + llvm::Value *emitIsNum(llvm::Value *Str); +}; + +} // namespace cps diff --git a/include/cps/StringHandler.h b/include/cps/StringHandler.h index 87710fb..a31fd2b 100644 --- a/include/cps/StringHandler.h +++ b/include/cps/StringHandler.h @@ -31,6 +31,7 @@ public: llvm::Value *emitLength(llvm::Value *Str); llvm::Value *emitMid(llvm::Value *Str, llvm::Value *Start, llvm::Value *Len); llvm::Value *emitRight(llvm::Value *Str, llvm::Value *Len); + llvm::Value *emitLeft(llvm::Value *Str, llvm::Value *Len); llvm::Value *emitLCase(llvm::Value *Str); llvm::Value *emitUCase(llvm::Value *Str); }; diff --git a/lib/CodeGen/ArithmeticHandler.cc b/lib/CodeGen/ArithmeticHandler.cc index df83513..585104d 100644 --- a/lib/CodeGen/ArithmeticHandler.cc +++ b/lib/CodeGen/ArithmeticHandler.cc @@ -11,6 +11,32 @@ Value *ArithmeticHandler::emitBinaryOp(int Op, Value *LHS, Value *RHS, int Line) bool LIsDouble = LHS->getType()->isDoubleTy(); bool RIsDouble = RHS->getType()->isDoubleTy(); + if (Op == '&') { + Module *M = Builder.GetInsertBlock()->getParent()->getParent(); + + FunctionType *StrLenTy = FunctionType::get(Type::getInt64Ty(Context), {PointerType::getUnqual(Context)}, false); + FunctionCallee StrLenF = M->getOrInsertFunction("strlen", StrLenTy); + + FunctionType *MallocTy = FunctionType::get(PointerType::getUnqual(Context), {Type::getInt64Ty(Context)}, false); + FunctionCallee MallocF = M->getOrInsertFunction("malloc", MallocTy); + + FunctionType *StrCpyTy = FunctionType::get(PointerType::getUnqual(Context), {PointerType::getUnqual(Context), PointerType::getUnqual(Context)}, false); + FunctionCallee StrCpyF = M->getOrInsertFunction("strcpy", StrCpyTy); + FunctionCallee StrCatF = M->getOrInsertFunction("strcat", StrCpyTy); + + Value *LLen = Builder.CreateCall(StrLenF, {LHS}, "llen"); + Value *RLen = Builder.CreateCall(StrLenF, {RHS}, "rlen"); + Value *TotalLen = Builder.CreateAdd(LLen, RLen, "totallen"); + Value *AllocSize = Builder.CreateAdd(TotalLen, ConstantInt::get(Type::getInt64Ty(Context), 1), "allocsize"); + + Value *NewStr = Builder.CreateCall(MallocF, {AllocSize}, "concat_str"); + + Builder.CreateCall(StrCpyF, {NewStr, LHS}); + Builder.CreateCall(StrCatF, {NewStr, RHS}); + + return NewStr; + } + if (Op == '/') { Value *LVal = LHS; Value *RVal = RHS; diff --git a/lib/CodeGen/CharHandler.cc b/lib/CodeGen/CharHandler.cc new file mode 100644 index 0000000..5e9b502 --- /dev/null +++ b/lib/CodeGen/CharHandler.cc @@ -0,0 +1,15 @@ +#include "cps/CharHandler.h" + +using namespace llvm; +using namespace cps; + +CharHandler::CharHandler(LLVMContext &Ctx, IRBuilder<> &B, llvm::Module &M) + : Context(Ctx), Builder(B), Module(M) {} + +Value *CharHandler::emitAsc(Value *CharVal) { + return Builder.CreateZExt(CharVal, Type::getInt32Ty(Context), "asc_val"); +} + +Value *CharHandler::emitChr(Value *IntVal) { + return Builder.CreateTrunc(IntVal, Type::getInt8Ty(Context), "chr_val"); +} diff --git a/lib/CodeGen/CodeGen.cc b/lib/CodeGen/CodeGen.cc index ee7ab04..c5b0ff3 100644 --- a/lib/CodeGen/CodeGen.cc +++ b/lib/CodeGen/CodeGen.cc @@ -25,6 +25,8 @@ CodeGen::CodeGen() { BoolHandler = std::make_unique(*TheContext, *Builder, *TheModule, NamedValues); ArithHandler = std::make_unique(*TheContext, *Builder); StrHandler = std::make_unique(*TheContext, *Builder, *TheModule, NamedValues); + ChrHandler = std::make_unique(*TheContext, *Builder, *TheModule); + StrConvHandler = std::make_unique(*TheContext, *Builder, *TheModule); SetupExternalFunctions(); } @@ -151,6 +153,11 @@ Value *CodeGen::emitExpr(ExprAST *Expr) { return StrHandler->emitRight(emitExpr(Call->getArgs()[0].get()), emitExpr(Call->getArgs()[1].get())); } + if (Name == "LEFT") { + if (Call->getArgs().size() != 2) { fprintf(stderr, "LEFT expects 2 args\n"); return nullptr; } + return StrHandler->emitLeft(emitExpr(Call->getArgs()[0].get()), + emitExpr(Call->getArgs()[1].get())); + } if (Name == "LCASE") { if (Call->getArgs().size() != 1) { fprintf(stderr, "LCASE expects 1 arg\n"); return nullptr; } return StrHandler->emitLCase(emitExpr(Call->getArgs()[0].get())); @@ -159,6 +166,36 @@ Value *CodeGen::emitExpr(ExprAST *Expr) { if (Call->getArgs().size() != 1) { fprintf(stderr, "UCASE expects 1 arg\n"); return nullptr; } return StrHandler->emitUCase(emitExpr(Call->getArgs()[0].get())); } + if (Name == "ASC") { + if (Call->getArgs().size() != 1) return nullptr; + Value *StrPtr = emitExpr(Call->getArgs()[0].get()); + Value *CharVal = Builder->CreateLoad(Type::getInt8Ty(*TheContext), StrPtr, "char_load"); + return ChrHandler->emitAsc(CharVal); + } + if (Name == "CHR") { + if (Call->getArgs().size() != 1) return nullptr; + Value *CharVal = ChrHandler->emitChr(emitExpr(Call->getArgs()[0].get())); + Function *MallocF = TheModule->getFunction("malloc"); + Value *Mem = Builder->CreateCall(MallocF, ConstantInt::get(*TheContext, APInt(64, 2))); + Builder->CreateStore(CharVal, Mem); + Value *NullPtr = Builder->CreateInBoundsGEP(Type::getInt8Ty(*TheContext), Mem, ConstantInt::get(*TheContext, APInt(64, 1))); + Builder->CreateStore(ConstantInt::get(Type::getInt8Ty(*TheContext), 0), NullPtr); + return Mem; + } + if (Name == "IS_NUM") { + if (Call->getArgs().size() != 1) return nullptr; + return StrConvHandler->emitIsNum(emitExpr(Call->getArgs()[0].get())); + } + if (Name == "NUM_TO_STR") { + if (Call->getArgs().size() != 1) return nullptr; + Value *NumV = emitExpr(Call->getArgs()[0].get()); + bool isReal = NumV->getType()->isDoubleTy(); + return StrConvHandler->emitNumToStr(NumV, isReal); + } + if (Name == "STR_TO_NUM") { + if (Call->getArgs().size() != 1) return nullptr; + return StrConvHandler->emitStrToNum(emitExpr(Call->getArgs()[0].get()), true); + } Function *CalleeF = TheModule->getFunction(Call->getCallee()); std::vector Args; @@ -432,6 +469,10 @@ void CodeGen::emitStmt(StmtAST *Stmt) { if (AI->getAllocatedType() == Type::getDoubleTy(*TheContext) && Val->getType()->isIntegerTy(64)) { Val = Builder->CreateSIToFP(Val, Type::getDoubleTy(*TheContext)); } + + if (AI->getAllocatedType() == Type::getInt64Ty(*TheContext) && Val->getType()->isDoubleTy()) { + Val = Builder->CreateFPToSI(Val, Type::getInt64Ty(*TheContext), "double_to_int"); + } if (AI->getAllocatedType() == Type::getInt64Ty(*TheContext) && Val->getType()->isIntegerTy(1)) { Val = Builder->CreateZExt(Val, Type::getInt64Ty(*TheContext)); diff --git a/lib/CodeGen/StringConversionHandler.cc b/lib/CodeGen/StringConversionHandler.cc new file mode 100644 index 0000000..62b99d7 --- /dev/null +++ b/lib/CodeGen/StringConversionHandler.cc @@ -0,0 +1,71 @@ +#include "cps/StringConversionHandler.h" + +using namespace llvm; +using namespace cps; + +StringConversionHandler::StringConversionHandler(LLVMContext &Ctx, IRBuilder<> &B, llvm::Module &M) + : Context(Ctx), Builder(B), Module(M) { + setupExternalFunctions(); +} + +void StringConversionHandler::setupExternalFunctions() { + FunctionType *SprintfType = FunctionType::get(Type::getInt32Ty(Context), {PointerType::getUnqual(Context), PointerType::getUnqual(Context)}, true); + SprintfFunc = Module.getOrInsertFunction("sprintf", SprintfType); + + FunctionType *StrtolType = FunctionType::get(Type::getInt64Ty(Context), {PointerType::getUnqual(Context), PointerType::getUnqual(Context), Type::getInt32Ty(Context)}, false); + StrtolFunc = Module.getOrInsertFunction("strtol", StrtolType); + + FunctionType *StrtodType = FunctionType::get(Type::getDoubleTy(Context), {PointerType::getUnqual(Context), PointerType::getUnqual(Context)}, false); + StrtodFunc = Module.getOrInsertFunction("strtod", StrtodType); + + FunctionType *MallocType = FunctionType::get(PointerType::getUnqual(Context), {Type::getInt64Ty(Context)}, false); + MallocFunc = Module.getOrInsertFunction("malloc", MallocType); +} + +Value *StringConversionHandler::emitNumToStr(Value *Num, bool IsReal) { + Value *AllocSize = ConstantInt::get(Type::getInt64Ty(Context), 64); + Value *Buffer = Builder.CreateCall(MallocFunc, AllocSize, "num_str_buf"); + + Value *FormatStr; + if (IsReal) { + FormatStr = Builder.CreateGlobalStringPtr("%f"); + } else { + FormatStr = Builder.CreateGlobalStringPtr("%d"); + } + + Builder.CreateCall(SprintfFunc, {Buffer, FormatStr, Num}); + return Buffer; +} + +Value *StringConversionHandler::emitStrToNum(Value *Str, bool AsReal) { + Value *NullPtr = ConstantPointerNull::get(PointerType::getUnqual(Context)); + if (AsReal) { + return Builder.CreateCall(StrtodFunc, {Str, NullPtr}, "str_to_real"); + } else { + Value *Base = ConstantInt::get(Type::getInt32Ty(Context), 10); + return Builder.CreateCall(StrtolFunc, {Str, NullPtr, Base}, "str_to_int"); + } +} + +Value *StringConversionHandler::emitIsNum(Value *Str) { + Function *TheFunction = Builder.GetInsertBlock()->getParent(); + + IRBuilder<> TmpB(&TheFunction->getEntryBlock(), TheFunction->getEntryBlock().begin()); + AllocaInst *EndPtrAlloc = TmpB.CreateAlloca(PointerType::getUnqual(Context), nullptr, "endptr_alloc"); + + Builder.CreateCall(StrtodFunc, {Str, EndPtrAlloc}); + Value *EndPtrVal = Builder.CreateLoad(PointerType::getUnqual(Context), EndPtrAlloc, "endptr_val"); + + FunctionType *StrLenType = FunctionType::get(Type::getInt64Ty(Context), {PointerType::getUnqual(Context)}, false); + FunctionCallee StrLenFunc = Module.getOrInsertFunction("strlen", StrLenType); + Value *Len = Builder.CreateCall(StrLenFunc, Str, "str_len"); + + Value *ExpectedEndPtr = Builder.CreateInBoundsGEP(Type::getInt8Ty(Context), Str, Len); + + Value *IsMatch = Builder.CreateICmpEQ(EndPtrVal, ExpectedEndPtr, "is_full_match"); + + Value *Zero = ConstantInt::get(Type::getInt64Ty(Context), 0); + Value *IsNotEmpty = Builder.CreateICmpSGT(Len, Zero, "is_not_empty"); + + return Builder.CreateAnd(IsMatch, IsNotEmpty, "is_num_bool"); +} diff --git a/lib/CodeGen/StringHandler.cc b/lib/CodeGen/StringHandler.cc index fda9ea5..f777059 100644 --- a/lib/CodeGen/StringHandler.cc +++ b/lib/CodeGen/StringHandler.cc @@ -112,6 +112,35 @@ Value *StringHandler::emitRight(Value *Str, Value *Len) { return NewStrMem; } +Value *StringHandler::emitLeft(Value *Str, Value *Len) { + Value *FullLen = emitLength(Str); + if (Len->getType()->isIntegerTy() && Len->getType()->getIntegerBitWidth() != 64) { + Len = Builder.CreateSExt(Len, Type::getInt64Ty(Context), "len_ext"); + } + else if (Len->getType()->isDoubleTy()) { + Len = Builder.CreateFPToSI(Len, Type::getInt64Ty(Context), "len_int"); + } + + Value *Zero = ConstantInt::get(Context, APInt(64, 0)); + Value *IsNeg = Builder.CreateICmpSLT(Len, Zero); + Value *SafeLen = Builder.CreateSelect(IsNeg, Zero, Len); + + Value *IsTooBig = Builder.CreateICmpSGT(SafeLen, FullLen); + Value *ActualLen = Builder.CreateSelect(IsTooBig, FullLen, SafeLen); + + Value *One = ConstantInt::get(Context, APInt(64, 1)); + Value *AllocSize = Builder.CreateAdd(ActualLen, One); + Value *NewStrMem = Builder.CreateCall(MallocFunc, AllocSize, "left_str_mem"); + + std::vector Args = {NewStrMem, Str, ActualLen}; + Builder.CreateCall(MemCpyFunc, Args); + + Value *NullTermPtr = Builder.CreateInBoundsGEP(Type::getInt8Ty(Context), NewStrMem, ActualLen); + Builder.CreateStore(ConstantInt::get(Type::getInt8Ty(Context), 0), NullTermPtr); + + return NewStrMem; +} + Value *StringHandler::emitLCase(Value *Str) { Value *Len = emitLength(Str); Value *One = ConstantInt::get(Context, APInt(64, 1)); diff --git a/lib/Lexer/Lexer.cc b/lib/Lexer/Lexer.cc index 040c3e8..ef3a920 100644 --- a/lib/Lexer/Lexer.cc +++ b/lib/Lexer/Lexer.cc @@ -15,15 +15,24 @@ int Lexer::gettok() { LastChar = getchar(); } - if (LastChar == '"') { + if (LastChar == '"' || LastChar == '\'') { + char QuoteType = LastChar; StringVal = ""; + LastChar = getchar(); - while (LastChar != '"' && LastChar != EOF) { + + while (LastChar != QuoteType && LastChar != EOF && LastChar != '\n') { StringVal += LastChar; LastChar = getchar(); } - if (LastChar == '"') LastChar = getchar(); - return tok_string_literal; + + if (LastChar == QuoteType) { + LastChar = getchar(); + } else { + fprintf(stderr, "Error: Unterminated string or char literal\n"); + } + + return tok_string_literal; } if (isalpha(LastChar)) { @@ -73,6 +82,7 @@ int Lexer::gettok() { if (IdentifierStr == "LENGTH") return tok_length; if (IdentifierStr == "MID") return tok_mid; if (IdentifierStr == "RIGHT") return tok_right; + if (IdentifierStr == "LEFT") return tok_left; if (IdentifierStr == "LCASE") return tok_lcase; if (IdentifierStr == "UCASE") return tok_ucase; diff --git a/lib/Parser/Parser.cc b/lib/Parser/Parser.cc index 236aa01..9bdd821 100644 --- a/lib/Parser/Parser.cc +++ b/lib/Parser/Parser.cc @@ -19,6 +19,7 @@ Parser::Parser(Lexer &L) : Lex(L) { BinopPrecedence['+'] = 20; BinopPrecedence['-'] = 20; + BinopPrecedence['&'] = 20; BinopPrecedence['*'] = 40; BinopPrecedence['/'] = 40; @@ -148,6 +149,7 @@ std::unique_ptr Parser::ParsePrimary() { case tok_length: return ParseStringBuiltin("LENGTH"); case tok_mid: return ParseStringBuiltin("MID"); case tok_right: return ParseStringBuiltin("RIGHT"); + case tok_left: return ParseStringBuiltin("LEFT"); case tok_lcase: return ParseStringBuiltin("LCASE"); case tok_ucase: return ParseStringBuiltin("UCASE"); -- cgit v1.3.1-4-g156e