diff --git a/src/cli/args.hpp b/src/cli/args.hpp new file mode 100644 index 0000000..e1f7df4 --- /dev/null +++ b/src/cli/args.hpp @@ -0,0 +1,128 @@ +// ============================================================================ +// saQut Compiler — CLI Argüman Ayrıştırıcı ve Kaynak Okuma +// ============================================================================ +// +// DİZİN: src/cli/args.hpp +// BAĞIMLI: Yok (sadece standart kütüphane) +// +// AMAÇ: +// 1. POSIX tarzı argüman ayrıştırma +// 2. Kaynak dosya okuma (tüm komutlar tarafından paylaşılır) +// +// DESTEKLENEN FORMATLAR: +// saqut [dosya] [-o çıktı] [--format json] [--help] +// saqut run file:source.sqt (eski sözdizimi) +// saqut - (stdin — TODO) +// +// ============================================================================ + +#ifndef SAQUT_CLI_ARGS +#define SAQUT_CLI_ARGS + +#include +#include +#include +#include +#include + +struct CliArgs { + std::string command; + std::vector positional; + std::string outputFile; + std::string format; + bool showHelp = false; + bool stdinMode = false; +}; + +// ============================================================================ +// parseArgs +// ============================================================================ +inline CliArgs parseArgs(int argc, char* argv[]) { + CliArgs args; + + for (int i = 1; i < argc; i++) { + std::string arg = argv[i]; + + if (arg == "-") { + args.stdinMode = true; + continue; + } + if (arg == "-h" || arg == "--help") { + args.showHelp = true; + continue; + } + if (arg.compare(0, 9, "--output=") == 0) { + args.outputFile = arg.substr(9); + continue; + } + if (arg.compare(0, 9, "--format=") == 0) { + args.format = arg.substr(9); + continue; + } + if (arg == "--output" || arg == "-o") { + if (i + 1 < argc) args.outputFile = argv[++i]; + continue; + } + if (arg == "--format") { + if (i + 1 < argc) args.format = argv[++i]; + continue; + } + if (arg.compare(0, 5, "file:") == 0) { + args.positional.push_back(arg.substr(5)); + continue; + } + if (arg.compare(0, 7, "output:") == 0) { + args.outputFile = arg.substr(7); + continue; + } + if (arg.compare(0, 4, "ast:") == 0) { + args.outputFile = arg.substr(4); + continue; + } + + // İlk argüman komut mu? + if (args.command.empty() && i == 1) { + if (arg == "run" || arg == "tokens" || arg == "ast" || + arg == "symbols" || arg == "compile" || arg == "parse" || + arg == "transpile" || arg == "interpret") { + args.command = arg; + continue; + } + args.command = "run"; + args.positional.push_back(arg); + continue; + } + + args.positional.push_back(arg); + } + + if (args.command.empty()) args.command = "run"; + if (args.positional.empty() && !args.stdinMode) + args.positional.push_back("source.sqt"); + + return args; +} + +// ============================================================================ +// readSource: Dosyadan veya stdin'den kaynak kod oku (TODO: stdin) +// ============================================================================ +inline std::string readSource(const CliArgs& args) { + if (args.stdinMode) { + // TODO: std::cin'den EOF'a kadar oku + std::cerr << "TODO: stdin modu henüz desteklenmiyor\n"; + return ""; + } + if (args.positional.empty()) return ""; + + std::string path = args.positional[0]; + std::ifstream file(path, std::ios::in | std::ios::binary); + if (!file.is_open()) { + std::cerr << "Hata: '" << path << "' dosyası açılamadı\n"; + return ""; + } + std::stringstream buffer; + buffer << file.rdbuf(); + return buffer.str(); +} + +#endif // SAQUT_CLI_ARGS diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp new file mode 100644 index 0000000..7d96e76 --- /dev/null +++ b/src/cli/cli.hpp @@ -0,0 +1,103 @@ +// ============================================================================ +// saQut Compiler — CLI Dispatcher +// ============================================================================ +// +// DİZİN: src/cli/cli.hpp +// BAĞIMLI: args.hpp, commands/* +// +// AMAÇ: +// Komut kaydı ve dağıtımı. Yeni bir komut eklemek için: +// 1. src/cli/commands/x.hpp oluştur +// 2. registerCommand() ile kaydet +// +// MİMARİ: +// Her komut bir CliCommand struct'ıdır: +// - name: "run", "tokens", "ast", ... +// - description: Yardım metninde görünür +// - hidden: true ise yardımda listelenmez (alias'lar için) +// - execute: int(CliArgs&) döndürür (0 = başarılı) +// +// Komutlar lazy olarak include edilmez — her biri kendi header'ında +// inline fonksiyon olarak tanımlanır ve cli.hpp tarafından include edilir. +// +// ============================================================================ + +#ifndef SAQUT_CLI +#define SAQUT_CLI + +#include +#include +#include +#include +#include "cli/args.hpp" + +// ============================================================================ +// CliCommand — Kayıtlı bir komut +// ============================================================================ +struct CliCommand { + std::string name; + std::string description; + bool hidden = false; // true = yardımda gösterme + std::function execute; +}; + +// ============================================================================ +// CliDispatcher — Komut kaydı ve çalıştırma +// ============================================================================ +class CliDispatcher { +public: + void registerCommand(const CliCommand& cmd) { + commands.push_back(cmd); + } + + int dispatch(const CliArgs& args) const { + // Yardım özel durumu + if (args.showHelp || args.command == "help") { + printHelp(); + return 0; + } + + // Komutu bul + for (auto& cmd : commands) { + if (cmd.name == args.command) { + return cmd.execute(args); + } + } + + // Bilinmeyen komut + std::cerr << "Hata: Bilinmeyen komut '" << args.command << "'\n"; + std::cerr << "Kullanılabilir komutlar için: saqut --help\n"; + return 1; + } + + void printHelp() const { + std::cout << "saQut Compiler — Dil Bağımsız Derleyici Alet Çantası\n\n"; + std::cout << "KULLANIM:\n"; + std::cout << " saqut [dosya] [seçenekler]\n"; + std::cout << " saqut - (stdin modu — TODO)\n\n"; + std::cout << "KOMUTLAR:\n"; + + for (auto& cmd : commands) { + if (cmd.hidden) continue; + std::cout << " " << cmd.name; + // 12 karaktere hizala + size_t pad = cmd.name.size() < 11 ? 11 - cmd.name.size() : 1; + std::cout << std::string(pad, ' ') << cmd.description << "\n"; + } + + std::cout << "\nSEÇENEKLER:\n"; + std::cout << " -o, --output Çıktı dosyası\n"; + std::cout << " --format Çıktı formatı (varsayılan: text)\n"; + std::cout << " -h, --help Bu yardım metni\n\n"; + std::cout << "ÖRNEK:\n"; + std::cout << " saqut run source.sqt\n"; + std::cout << " saqut tokens source.sqt\n"; + std::cout << " saqut ast source.sqt --format=json\n"; + std::cout << " saqut symbols source.sqt\n"; + } + +private: + std::vector commands; +}; + +#endif // SAQUT_CLI diff --git a/src/cli/commands/ast.hpp b/src/cli/commands/ast.hpp new file mode 100644 index 0000000..bff459f --- /dev/null +++ b/src/cli/commands/ast.hpp @@ -0,0 +1,54 @@ +// ============================================================================ +// saQut CLI — ast komutu (JSON formatında AST hiyerarşisi + analiz) +// ============================================================================ + +#ifndef SAQUT_CLI_AST +#define SAQUT_CLI_AST + +#include +#include +#include "cli/args.hpp" +#include "tokenizer/tokenizer.hpp" +#include "parser/parser.hpp" +#include "json.hpp" + +inline int cmdAst(const CliArgs& args) { + std::string source = readSource(args); + if (source.empty()) return 1; + + Tokenizer tokenizer; + auto tokens = tokenizer.scan(source); + + Parser parser; + ASTNode* ast = parser.parse(tokens); + + if (!ast) { + std::cerr << "Hata: AST üretilemedi\n"; + for (auto* t : tokens) delete t; + return 1; + } + + AstAnalysis analysis = analyzeAst(ast); + + // Çıktı hedefi + std::ostream* out = &std::cout; + std::ofstream outFile; + if (!args.outputFile.empty()) { + outFile.open(args.outputFile); + if (outFile.is_open()) out = &outFile; + } + + *out << "{\n" + << " \"ast\":\n" + << astToJson(ast, 2) << ",\n\n" + << " \"analysis\": {\n" + << analysisToJson(analysis) << "\n" + << " }\n" + << "}\n"; + + delete ast; + for (auto* t : tokens) delete t; + return 0; +} + +#endif // SAQUT_CLI_AST diff --git a/src/cli/commands/run.hpp b/src/cli/commands/run.hpp new file mode 100644 index 0000000..ef9f636 --- /dev/null +++ b/src/cli/commands/run.hpp @@ -0,0 +1,66 @@ +// ============================================================================ +// saQut CLI — run komutu (pipeline: token → AST → IR debug) +// ============================================================================ +// +// TODO: İleride `saqut -` ile stdin'den okuyup anında çalıştıracak +// interpreter modu bu komutun altına gelecek. +// +// ============================================================================ + +#ifndef SAQUT_CLI_RUN +#define SAQUT_CLI_RUN + +#include +#include "cli/args.hpp" +#include "tokenizer/tokenizer.hpp" +#include "parser/parser.hpp" +#include "ir/ir.hpp" + +inline int cmdRun(const CliArgs& args) { + std::string source = readSource(args); + if (source.empty()) return 1; + + Tokenizer tokenizer; + auto tokens = tokenizer.scan(source); + + std::cout << "=== saQut Compiler ===\n"; + std::cout << "Kaynak kod:\n" << source << "\n\n"; + + std::cout << "Tokenler (" << tokens.size() << " adet):\n"; + for (auto* t : tokens) { + std::cout << " [" << t->gettype() << "] \"" << t->token << "\"\n"; + } + std::cout << "\n"; + + Parser parser; + ASTNode* ast = parser.parse(tokens); + + if (ast) { + std::cout << "AST:\n"; + ast->log(0); + std::cout << "\n"; + + CodeGenerator cg; + cg.parse(ast); + std::cout << "IR (" << cg.IROpDatas.size() << " komut):\n"; + for (size_t i = 0; i < cg.IROpDatas.size(); i++) { + auto& op = cg.IROpDatas[i]; + std::cout << " [" << i << "] reg" << op.targetReg << " = "; + switch (op.op) { + case OPCode::mathadd: std::cout << "add"; break; + case OPCode::mathsub: std::cout << "sub"; break; + case OPCode::mathmul: std::cout << "mul"; break; + case OPCode::mathdiv: std::cout << "div"; break; + case OPCode::declare: std::cout << "literal"; break; + } + std::cout << " (" << op.arg1.value.index() << ")\n"; + } + + delete ast; + } + + for (auto* t : tokens) delete t; + return 0; +} + +#endif // SAQUT_CLI_RUN diff --git a/src/cli/commands/symbols.hpp b/src/cli/commands/symbols.hpp new file mode 100644 index 0000000..985f282 --- /dev/null +++ b/src/cli/commands/symbols.hpp @@ -0,0 +1,58 @@ +// ============================================================================ +// saQut CLI — symbols komutu (sembol tablosu) +// ============================================================================ + +#ifndef SAQUT_CLI_SYMBOLS +#define SAQUT_CLI_SYMBOLS + +#include +#include "cli/args.hpp" +#include "tokenizer/tokenizer.hpp" +#include "parser/parser.hpp" +#include "json.hpp" + +inline int cmdSymbols(const CliArgs& args) { + std::string source = readSource(args); + if (source.empty()) return 1; + + Tokenizer tokenizer; + auto tokens = tokenizer.scan(source); + + Parser parser; + ASTNode* ast = parser.parse(tokens); + + if (!ast) { + std::cerr << "Hata: AST üretilemedi\n"; + for (auto* t : tokens) delete t; + return 1; + } + + auto symbols = collectSymbols(ast); + + std::cout << "Sembol Tablosu (" << symbols.size() << " sembol):\n"; + std::cout << "────────────────────────────────────────────\n"; + + if (symbols.empty()) { + std::cout << " (sembol bulunamadı)\n"; + } + + for (auto& s : symbols) { + std::cout << " [" << s.kind << "] " << s.type << " " << s.name << "\n"; + } + + std::cout << "────────────────────────────────────────────\n"; + + int fnCount = 0, varCount = 0; + for (auto& s : symbols) { + if (s.kind == "function") fnCount++; + else if (s.kind == "variable") varCount++; + } + std::cout << "Fonksiyon: " << fnCount + << " | Değişken: " << varCount << "\n"; + + delete ast; + for (auto* t : tokens) delete t; + return 0; +} + +#endif // SAQUT_CLI_SYMBOLS diff --git a/src/cli/commands/tokens.hpp b/src/cli/commands/tokens.hpp new file mode 100644 index 0000000..9537de6 --- /dev/null +++ b/src/cli/commands/tokens.hpp @@ -0,0 +1,28 @@ +// ============================================================================ +// saQut CLI — tokens komutu +// ============================================================================ + +#ifndef SAQUT_CLI_TOKENS +#define SAQUT_CLI_TOKENS + +#include +#include "cli/args.hpp" +#include "tokenizer/tokenizer.hpp" + +inline int cmdTokens(const CliArgs& args) { + std::string source = readSource(args); + if (source.empty()) return 1; + + Tokenizer tokenizer; + auto tokens = tokenizer.scan(source); + + std::cout << "Tokenler (" << tokens.size() << " adet):\n"; + for (auto* t : tokens) { + std::cout << " [" << t->gettype() << "] \"" << t->token << "\"\n"; + } + + for (auto* t : tokens) delete t; + return 0; +} + +#endif // SAQUT_CLI_TOKENS diff --git a/src/json.hpp b/src/json.hpp new file mode 100644 index 0000000..1dfdc08 --- /dev/null +++ b/src/json.hpp @@ -0,0 +1,283 @@ +// ============================================================================ +// saQut Compiler — AST Analizi ve Sembol Toplayıcı +// ============================================================================ +// +// DİZİN: src/json.hpp +// KATMAN: Tüm katmanlardan sonra — AST'yi tüketir +// BAĞIMLI: AST (src/parser/ast.hpp) +// KULLANAN: main.cpp +// +// AMAÇ: +// 1. AST üzerinde analiz yap (düğüm sayısı, derinlik, tip dağılımı) +// 2. Sembolleri topla (fonksiyon isimleri, dönüş tipleri, değişkenler) +// +// NOT: JSON serileştirme ASTNode::toJson() tarafından yapılır. +// Bu dosya sadece analiz ve sembol toplama işlemlerini içerir. +// Yeni bir AST düğümü eklendiğinde buraya dokunmak gerekmez. +// +// ============================================================================ + +#ifndef SAQUT_JSON +#define SAQUT_JSON + +#include +#include +#include +#include +#include "parser/ast.hpp" + +// ============================================================================ +// astKindName — ASTKind enum'unu string'e çevir (analiz çıktısı için) +// ============================================================================ + +inline const char* astKindName(ASTKind k) { + switch (k) { + case ASTKind::Program: return "Program"; + case ASTKind::FunctionDecl: return "FunctionDecl"; + case ASTKind::Block: return "Block"; + case ASTKind::VariableDecl: return "VariableDecl"; + case ASTKind::BinaryExpression: return "BinaryExpression"; + case ASTKind::UnaryExpression: return "UnaryExpression"; + case ASTKind::Literal: return "Literal"; + case ASTKind::Identifier: return "Identifier"; + case ASTKind::Postfix: return "Postfix"; + case ASTKind::IfStatement: return "IfStatement"; + case ASTKind::ForStatement: return "ForStatement"; + case ASTKind::WhileStatement: return "WhileStatement"; + case ASTKind::DoWhileStatement: return "DoWhileStatement"; + case ASTKind::ReturnStatement: return "ReturnStatement"; + case ASTKind::BreakStatement: return "BreakStatement"; + case ASTKind::ContinueStatement: return "ContinueStatement"; + case ASTKind::ExpressionStatement: return "ExpressionStatement"; + default: return "Unknown"; + } +} + +// ============================================================================ +// AST → JSON (ince sarmalayıcı — asıl iş ASTNode::toJson()'da) +// ============================================================================ + +inline std::string astToJson(ASTNode* node, int depth = 0) { + if (!node) return "null"; + return node->toJson(depth); +} + +// ============================================================================ +// AST Analizi +// ============================================================================ + +struct AstAnalysis { + int totalNodes = 0; + int maxDepth = 0; + int functionCount = 0; + int variableCount = 0; + int ifCount = 0; + int loopCount = 0; // for + while + do-while + std::map nodeTypeCounts; +}; + +inline void analyzeRecursive(ASTNode* node, int currentDepth, AstAnalysis& a) { + if (!node) return; + + a.totalNodes++; + if (currentDepth > a.maxDepth) a.maxDepth = currentDepth; + + const char* name = astKindName(node->kind); + a.nodeTypeCounts[name]++; + + switch (node->kind) { + case ASTKind::FunctionDecl: a.functionCount++; break; + case ASTKind::VariableDecl: a.variableCount++; break; + case ASTKind::IfStatement: a.ifCount++; break; + case ASTKind::WhileStatement: + case ASTKind::ForStatement: + case ASTKind::DoWhileStatement: + a.loopCount++; break; + default: break; + } + + // Tüm çocukları gez + switch (node->kind) { + case ASTKind::Program: + case ASTKind::FunctionDecl: + case ASTKind::Block: { + for (auto* child : node->getChildren()) + analyzeRecursive(child, currentDepth + 1, a); + break; + } + case ASTKind::VariableDecl: { + auto* vd = (VariableDeclNode*)node; + if (vd->initExpr) analyzeRecursive(vd->initExpr, currentDepth + 1, a); + break; + } + case ASTKind::BinaryExpression: { + auto* bin = (BinaryExpressionNode*)node; + if (bin->Left) analyzeRecursive(bin->Left, currentDepth + 1, a); + if (bin->Right) analyzeRecursive(bin->Right, currentDepth + 1, a); + break; + } + case ASTKind::Postfix: { + auto* pf = (PostfixNode*)node; + if (pf->operand) analyzeRecursive(pf->operand, currentDepth + 1, a); + break; + } + case ASTKind::IfStatement: { + auto* ifn = (IfStatementNode*)node; + if (ifn->condition) analyzeRecursive(ifn->condition, currentDepth + 1, a); + if (ifn->thenBranch) analyzeRecursive(ifn->thenBranch, currentDepth + 1, a); + if (ifn->elseBranch) analyzeRecursive(ifn->elseBranch, currentDepth + 1, a); + break; + } + case ASTKind::WhileStatement: { + auto* ws = (WhileStatementNode*)node; + if (ws->condition) analyzeRecursive(ws->condition, currentDepth + 1, a); + if (ws->body) analyzeRecursive(ws->body, currentDepth + 1, a); + break; + } + case ASTKind::ForStatement: { + auto* fs = (ForStatementNode*)node; + if (fs->init) analyzeRecursive(fs->init, currentDepth + 1, a); + if (fs->condition) analyzeRecursive(fs->condition, currentDepth + 1, a); + if (fs->update) analyzeRecursive(fs->update, currentDepth + 1, a); + if (fs->body) analyzeRecursive(fs->body, currentDepth + 1, a); + break; + } + case ASTKind::DoWhileStatement: { + auto* dw = (DoWhileStatementNode*)node; + if (dw->body) analyzeRecursive(dw->body, currentDepth + 1, a); + if (dw->condition) analyzeRecursive(dw->condition, currentDepth + 1, a); + break; + } + case ASTKind::ReturnStatement: { + auto* rs = (ReturnStatementNode*)node; + if (rs->value) analyzeRecursive(rs->value, currentDepth + 1, a); + break; + } + case ASTKind::ExpressionStatement: { + auto* es = (ExpressionStatementNode*)node; + if (es->expression) analyzeRecursive(es->expression, currentDepth + 1, a); + break; + } + default: break; // Literal, Identifier, Break, Continue — yaprak düğüm + } +} + +inline AstAnalysis analyzeAst(ASTNode* root) { + AstAnalysis a; + analyzeRecursive(root, 0, a); + return a; +} + +inline std::string analysisToJson(const AstAnalysis& a) { + std::ostringstream ss; + ss << " \"totalNodes\": " << a.totalNodes << ",\n" + << " \"maxDepth\": " << a.maxDepth << ",\n" + << " \"functionCount\": " << a.functionCount << ",\n" + << " \"variableCount\": " << a.variableCount << ",\n" + << " \"ifCount\": " << a.ifCount << ",\n" + << " \"loopCount\": " << a.loopCount << ",\n" + << " \"nodeTypes\": {\n"; + bool first = true; + for (auto& [name, count] : a.nodeTypeCounts) { + if (!first) ss << ",\n"; + ss << " \"" << name << "\": " << count; + first = false; + } + ss << "\n }"; + return ss.str(); +} + +// ============================================================================ +// Sembol Toplayıcı +// ============================================================================ + +struct SymbolEntry { + std::string name; + std::string kind; // "function" veya "variable" + std::string type; // Dönüş tipi veya değişken tipi +}; + +inline void collectSymbolsRecursive(ASTNode* node, std::vector& symbols) { + if (!node) return; + + // Sadece FunctionDecl ve VariableDecl toplanır — diğerleri gezilir + if (node->kind == ASTKind::FunctionDecl) { + auto* fn = (FunctionDeclNode*)node; + symbols.push_back({fn->name, "function", fn->returnType}); + } else if (node->kind == ASTKind::VariableDecl) { + auto* vd = (VariableDeclNode*)node; + symbols.push_back({vd->name, "variable", vd->varType}); + } + + // Çocukları gez + switch (node->kind) { + case ASTKind::Program: + case ASTKind::FunctionDecl: + case ASTKind::Block: + for (auto* child : node->getChildren()) + collectSymbolsRecursive(child, symbols); + break; + case ASTKind::VariableDecl: { + auto* vd = (VariableDeclNode*)node; + if (vd->initExpr) collectSymbolsRecursive(vd->initExpr, symbols); + break; + } + case ASTKind::BinaryExpression: { + auto* bin = (BinaryExpressionNode*)node; + if (bin->Left) collectSymbolsRecursive(bin->Left, symbols); + if (bin->Right) collectSymbolsRecursive(bin->Right, symbols); + break; + } + case ASTKind::Postfix: { + auto* pf = (PostfixNode*)node; + if (pf->operand) collectSymbolsRecursive(pf->operand, symbols); + break; + } + case ASTKind::IfStatement: { + auto* ifn = (IfStatementNode*)node; + if (ifn->condition) collectSymbolsRecursive(ifn->condition, symbols); + if (ifn->thenBranch) collectSymbolsRecursive(ifn->thenBranch, symbols); + if (ifn->elseBranch) collectSymbolsRecursive(ifn->elseBranch, symbols); + break; + } + case ASTKind::WhileStatement: { + auto* ws = (WhileStatementNode*)node; + if (ws->condition) collectSymbolsRecursive(ws->condition, symbols); + if (ws->body) collectSymbolsRecursive(ws->body, symbols); + break; + } + case ASTKind::ForStatement: { + auto* fs = (ForStatementNode*)node; + if (fs->init) collectSymbolsRecursive(fs->init, symbols); + if (fs->condition) collectSymbolsRecursive(fs->condition, symbols); + if (fs->update) collectSymbolsRecursive(fs->update, symbols); + if (fs->body) collectSymbolsRecursive(fs->body, symbols); + break; + } + case ASTKind::DoWhileStatement: { + auto* dw = (DoWhileStatementNode*)node; + if (dw->body) collectSymbolsRecursive(dw->body, symbols); + if (dw->condition) collectSymbolsRecursive(dw->condition, symbols); + break; + } + case ASTKind::ReturnStatement: { + auto* rs = (ReturnStatementNode*)node; + if (rs->value) collectSymbolsRecursive(rs->value, symbols); + break; + } + case ASTKind::ExpressionStatement: { + auto* es = (ExpressionStatementNode*)node; + if (es->expression) collectSymbolsRecursive(es->expression, symbols); + break; + } + default: break; + } +} + +inline std::vector collectSymbols(ASTNode* root) { + std::vector symbols; + collectSymbolsRecursive(root, symbols); + return symbols; +} + +#endif // SAQUT_JSON diff --git a/src/main.cpp b/src/main.cpp index 2a6d5e0..2f3321c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,118 +3,84 @@ // ============================================================================ // // DİZİN: src/main.cpp -// KATMAN: En üst — tüm alt katmanları birleştirir -// BAĞIMLI: Tokenizer, Parser, IR (ve dolaylı olarak Lexer, AST, Token) -// -// AMAÇ: -// Derleyici pipeline'ını başlatır: -// 1. source.sqt dosyasını oku -// 2. Lexing + Tokenizing -// 3. Parsing (AST üretimi) -// 4. IR üretimi -// 5. Sonuçları konsola yazdır (debug modu) +// KATMAN: En üst — CLI dispatcher'ı başlatır // // KULLANIM: -// ./saqut → source.sqt dosyasını derler -// echo "1+2" > source.sqt && ./saqut → hızlı test +// saqut → yardım +// saqut run → pipeline debug çıktısı +// saqut tokens → token listesi +// saqut ast [-o çıktı] → JSON AST + analiz +// saqut symbols → sembol tablosu +// saqut - → stdin modu (TODO) // -// GELECEK: -// - Komut satırı argümanları: ./saqut file.sqt -o output -// - Mod seçimi: ./saqut --mode=parse|ir|compile|run -// - Birden fazla dosya: ./saqut file1.sqt file2.sqt +// YENİ KOMUT EKLEMEK İÇİN: +// 1. src/cli/commands/x.hpp oluştur +// 2. Bu dosyada #include et +// 3. cli.registerCommand({...}) ile kaydet // // ============================================================================ #include -#include -#include -#include -#include "tokenizer/tokenizer.hpp" -#include "parser/parser.hpp" -#include "ir/ir.hpp" +#include "cli/args.hpp" +#include "cli/cli.hpp" +#include "cli/commands/run.hpp" +#include "cli/commands/tokens.hpp" +#include "cli/commands/ast.hpp" +#include "cli/commands/symbols.hpp" -int main() { - // ------------------------------------------------------------------ - // 1. Kaynak dosyayı oku - // ------------------------------------------------------------------ - // Şimdilik sabit dosya adı: source.sqt. - // TODO: argc/argv ile dosya adı al. - // ------------------------------------------------------------------ - std::ifstream file("source.sqt", std::ios::in | std::ios::binary); - if (!file.is_open()) { - std::cerr << "Hata: source.sqt dosyası açılamadı\n"; - return 1; +int main(int argc, char* argv[]) { + // Komutları kaydet + CliDispatcher cli; + + cli.registerCommand({"run", + "Pipeline'ı çalıştır (token → AST → IR)", + false, cmdRun}); + + cli.registerCommand({"tokens", + "Token listesini göster", + false, cmdTokens}); + + cli.registerCommand({"ast", + "JSON formatında AST hiyerarşisi ve analiz", + false, cmdAst}); + + cli.registerCommand({"symbols", + "Sembol tablosu (fonksiyonlar, değişkenler)", + false, cmdSymbols}); + + // --- Gelecek komutlar (TODO) --- + cli.registerCommand({"compile", + "TODO: Kaynak kodu derle", + false, [](const CliArgs&) { + std::cerr << "TODO: compile komutu henüz eklenmedi\n"; return 1; + }}); + + cli.registerCommand({"parse", + "TODO: IR üret", + false, [](const CliArgs&) { + std::cerr << "TODO: parse komutu henüz eklenmedi\n"; return 1; + }}); + + cli.registerCommand({"transpile", + "TODO: C koduna çevir", + false, [](const CliArgs&) { + std::cerr << "TODO: transpile komutu henüz eklenmedi\n"; return 1; + }}); + + cli.registerCommand({"interpret", + "TODO: Interpreter modu", + true, [](const CliArgs&) { + std::cerr << "TODO: interpret komutu henüz eklenmedi\n"; return 1; + }}); + + // Argümanları ayrıştır + CliArgs args = parseArgs(argc, argv); + + // Argümansız çağrı → help + if (argc <= 1) { + cli.printHelp(); + return 0; } - std::stringstream buffer; - buffer << file.rdbuf(); - std::string source = buffer.str(); - file.close(); - - std::cout << "=== saQut Compiler ===\n"; - std::cout << "Kaynak kod:\n" << source << "\n\n"; - - // ------------------------------------------------------------------ - // 2. Lexing → Tokenizing - // ------------------------------------------------------------------ - // Tokenizer, Lexer'ı içerir. scan() tüm pipeline'ı çalıştırır. - // Token'lar heap'te new ile oluşturulur, iş bitince silinmeli. - // ------------------------------------------------------------------ - Tokenizer tokenizer; - auto tokens = tokenizer.scan(source); - - std::cout << "Tokenler (" << tokens.size() << " adet):\n"; - for (auto* t : tokens) { - std::cout << " [" << t->gettype() << "] \"" << t->token << "\"\n"; - } - std::cout << "\n"; - - // ------------------------------------------------------------------ - // 3. Parsing → AST - // ------------------------------------------------------------------ - // Parser, token listesini alır, AST üretir. - // parse() artık parseProgram()'ı çağırır — birden fazla deklarasyon - // veya statement içeren tam programları ayrıştırabilir. - // ------------------------------------------------------------------ - Parser parser; - ASTNode* ast = parser.parse(tokens); - - if (ast) { - std::cout << "AST:\n"; - ast->log(0); // Ağacı girintili olarak yazdır - std::cout << "\n"; - - // ------------------------------------------------------------------ - // 4. IR Üretimi - // ------------------------------------------------------------------ - // CodeGenerator AST'yi dolaşır, sanal register makine komutları üretir. - // Şu anda sadece matematik işlemleri ve literal'lar destekleniyor. - // ------------------------------------------------------------------ - CodeGenerator cg; - cg.parse(ast); - std::cout << "IR (" << cg.IROpDatas.size() << " komut):\n"; - for (size_t i = 0; i < cg.IROpDatas.size(); i++) { - auto& op = cg.IROpDatas[i]; - std::cout << " [" << i << "] reg" << op.targetReg << " = "; - switch (op.op) { - case OPCode::mathadd: std::cout << "add"; break; - case OPCode::mathsub: std::cout << "sub"; break; - case OPCode::mathmul: std::cout << "mul"; break; - case OPCode::mathdiv: std::cout << "div"; break; - case OPCode::declare: std::cout << "literal"; break; - } - // arg1.value.index(): 0=int, 1=float - std::cout << " (" << op.arg1.value.index() << ")\n"; - } - } - - // ------------------------------------------------------------------ - // 5. Temizlik - // ------------------------------------------------------------------ - // Token'lar heap'te oluşturuldu, manuel silinmeli. - // TODO: std::unique_ptr ile otomatik bellek yönetimi. - // ------------------------------------------------------------------ - for (auto* t : tokens) delete t; - - return 0; + return cli.dispatch(args); } diff --git a/src/parser/ast.hpp b/src/parser/ast.hpp index a86746b..e53428f 100644 --- a/src/parser/ast.hpp +++ b/src/parser/ast.hpp @@ -5,7 +5,7 @@ // DİZİN: src/parser/ast.hpp // KATMAN: Katman 3 — Parser'ın ürettiği, IR'nin tükettiği // BAĞIMLI: Token (src/parser/token.hpp), Tools (src/tools.hpp) -// KULLANAN: Parser (src/parser/parser.hpp), IR (src/ir/ir.hpp) +// KULLANAN: Parser (src/parser/parser.hpp), IR (src/ir/ir.hpp), JSON (src/json.hpp) // // AMAÇ: // Kaynak kodun hiyerarşik, anlamsal gösterimi. Her dil yapısı (ifade, @@ -31,26 +31,17 @@ // └── PostfixNode : Son ek işlem (a++, a--) // // TASARIM KARARLARI: -// 1. ASTKind enum: Her düğüm tipi için bir enum değeri. -// RTTI (dynamic_cast) yerine manuel tip kontrolü sağlar. -// Daha hızlı ve hata ayıklaması kolay. +// 1. ASTKind enum + log() + toJson(): Her düğüm kendi tipini bilir, +// kendini konsola yazdırabilir ve JSON olarak serileştirebilir. +// Yeni bir düğüm eklendiğinde tüm davranışlar tek yerde tanımlanır. // // 2. parent pointer: Her düğüm ebeveynini bilir. -// Yukarı doğru gezinme (ör: bir döngü içinde break'in hedefini bulma). // -// 3. children vektörü (protected): Sadece addChild() ile ekleme. -// ProgramNode, FunctionDeclNode, BlockNode gibi liste tutan düğümler -// bu vektörü kullanır. İkili işlem gibi sabit sayıda çocuğu olan -// düğümler kendi üye değişkenlerini kullanır (Left, Right). -// -// 4. log() metodu: Her düğüm kendi alt ağacını girintili olarak yazdırır. -// Debug ve test için. Gerçek kod üretimi için kullanılmaz. +// 3. children vektörü (protected): Liste tipi düğümler için. // // BİLİNEN SINIRLAMALAR (TODO): -// TODO: Bellek yönetimi: AST düğümleri heap'te new ile oluşturuluyor, -// silme sorumluluğu yok (sızıntı). unique_ptr veya arena allocator. -// TODO: Ziyaretçi deseni (Visitor pattern) eklenerek log() ve IR -// üretimi ayrı sınıflara taşınabilir. +// TODO: Bellek yönetimi (unique_ptr veya arena allocator) +// TODO: Visitor pattern ile log/toJson/IR üretimi ayrıştırılabilir // // ============================================================================ @@ -58,6 +49,7 @@ #define SAQUT_AST #include +#include #include #include "parser/token.hpp" #include "tools.hpp" @@ -65,14 +57,7 @@ // ============================================================================ // ASTKind — AST Düğüm Tipi Enum'u // ============================================================================ -// -// Her AST düğüm sınıfı, constructor'ında kendi kind değerini atar. -// CodeGenerator (IR) ve diğer AST işlemcileri, düğümün tipini bu enum -// üzerinden belirler. -// -// İsimlendirme: Düğüm sınıf adları "Node" ile biter, enum değerleri bitmez. -// Örn: sınıf=IfStatementNode, enum=IfStatement -// + enum class ASTKind { Program, // Kök düğüm FunctionDecl, // Fonksiyon tanımı @@ -96,113 +81,143 @@ enum class ASTKind { // ============================================================================ // ASTNode — Soyut Temel Sınıf // ============================================================================ -// -// Tüm AST düğümlerinin ortak atası. Minimum arayüz: -// - kind: Düğüm tipi (ASTKind enum) -// - parent: Ebeveyn düğüm (kök için nullptr) -// - addChild() / getChildren(): Çocuk yönetimi -// - log(): Debug çıktısı (virtual, her alt sınıf override eder) -// + class ASTNode { public: - ASTKind kind; // Düğüm tipi (alt sınıf constructor'ında atanır) - ASTNode* parent = nullptr; // Ebeveyn düğüm (kök = nullptr) + ASTKind kind; + ASTNode* parent = nullptr; virtual void log(int indent = 0) { - (void)indent; // Kullanılmayan parametre uyarısını sustur + (void)indent; std::cout << "\n"; } - // Çocuk ekleme. Otomatik olarak parent pointer'ı ayarlar. + // JSON serileştirme — her alt sınıf kendi implemente eder + virtual std::string toJson(int indent = 0) { + (void)indent; + return "{\"kind\":\"Unknown\"}"; + } + void addChild(ASTNode* child) { children.push_back(child); child->parent = this; } std::vector& getChildren() { return children; } - virtual ~ASTNode() = default; protected: - std::vector children; // Alt düğümler (liste tipi düğümler için) + std::vector children; }; +// ============================================================================ +// JSON yardımcısı: alt düğüm listesini JSON array olarak yaz +// ============================================================================ +inline std::string childrenToJson(ASTNode* node, int depth) { + std::ostringstream ss; + std::string in = jsonIndent(depth); + auto& ch = node->getChildren(); + for (size_t i = 0; i < ch.size(); i++) { + ss << ch[i]->toJson(depth); + if (i + 1 < ch.size()) ss << ","; + ss << "\n"; + } + return ss.str(); +} + // ============================================================================ // ProgramNode — Kök Düğüm // ============================================================================ -// -// Her saQut programı tek bir ProgramNode ile başlar. -// Çocukları: FunctionDeclNode, VariableDeclNode (global), ExpressionStatement. -// + class ProgramNode : public ASTNode { public: ProgramNode() { kind = ASTKind::Program; } void log(int indent = 0) override { std::cout << padRight("", indent) << "Program\n"; - for (auto* c : getChildren()) - c->log(indent + 2); + for (auto* c : getChildren()) c->log(indent + 2); + } + + std::string toJson(int depth = 0) override { + std::string in = jsonIndent(depth); + std::ostringstream ss; + ss << in << "{\n" + << in << " \"kind\": \"Program\",\n" + << in << " \"children\": [\n" + << childrenToJson(this, depth + 3) + << in << " ]\n" + << in << "}"; + return ss.str(); } }; // ============================================================================ // FunctionDeclNode — Fonksiyon Tanımı // ============================================================================ -// -// Örnek: int main() { ... } -// returnType: "int", "void", "float", ... -// name: "main", "calculate", ... -// children: gövde (genellikle tek bir BlockNode) -// -// TODO: Parametre listesi (şu anda boş) -// + class FunctionDeclNode : public ASTNode { public: - std::string name; // Fonksiyon adı - std::string returnType; // Dönüş tipi (string olarak, ileride tip sistemi) + std::string name; + std::string returnType; FunctionDeclNode() { kind = ASTKind::FunctionDecl; } void log(int indent = 0) override { std::cout << padRight("", indent) << "FunctionDecl " << returnType << " " << name << "()\n"; - for (auto* c : getChildren()) - c->log(indent + 2); + for (auto* c : getChildren()) c->log(indent + 2); + } + + std::string toJson(int depth = 0) override { + std::string in = jsonIndent(depth); + std::ostringstream ss; + ss << in << "{\n" + << in << " \"kind\": \"FunctionDecl\",\n" + << in << " \"name\": \"" << jsonEscape(name) << "\",\n" + << in << " \"returnType\": \"" << jsonEscape(returnType) << "\",\n" + << in << " \"children\": [\n" + << childrenToJson(this, depth + 3) + << in << " ]\n" + << in << "}"; + return ss.str(); } }; // ============================================================================ // BlockNode — Blok { ... } // ============================================================================ -// -// Bir dizi statement'i gruplar. Kendi scope (kapsam) alanı oluşturur. -// Örnek: { int x = 1; x = x + 2; } -// + class BlockNode : public ASTNode { public: BlockNode() { kind = ASTKind::Block; } void log(int indent = 0) override { std::cout << padRight("", indent) << "Block\n"; - for (auto* c : getChildren()) - c->log(indent + 2); + for (auto* c : getChildren()) c->log(indent + 2); + } + + std::string toJson(int depth = 0) override { + std::string in = jsonIndent(depth); + std::ostringstream ss; + ss << in << "{\n" + << in << " \"kind\": \"Block\",\n" + << in << " \"children\": [\n" + << childrenToJson(this, depth + 3) + << in << " ]\n" + << in << "}"; + return ss.str(); } }; // ============================================================================ // VariableDeclNode — Değişken Tanımı // ============================================================================ -// -// Örnek: int x = 10; -// varType: "int", "float", "bool", ... -// name: "x", "counter", ... -// initExpr: Başlangıç değeri (nullptr = tanımsız, örn: int x;) -// + class VariableDeclNode : public ASTNode { public: - std::string varType; // Değişken tipi - std::string name; // Değişken adı - ASTNode* initExpr = nullptr; // Başlangıç ifadesi (opsiyonel) + std::string varType; + std::string name; + ASTNode* initExpr = nullptr; VariableDeclNode() { kind = ASTKind::VariableDecl; } @@ -216,34 +231,36 @@ public: std::cout << "\n"; } } + + std::string toJson(int depth = 0) override { + std::string in = jsonIndent(depth); + std::ostringstream ss; + ss << in << "{\n" + << in << " \"kind\": \"VariableDecl\",\n" + << in << " \"name\": \"" << jsonEscape(name) << "\",\n" + << in << " \"varType\": \"" << jsonEscape(varType) << "\""; + if (initExpr) { + ss << ",\n" << in << " \"initExpr\":\n" + << initExpr->toJson(depth + 2); + } + ss << "\n" << in << "}"; + return ss.str(); + } }; // ============================================================================ // BinaryExpressionNode — İkili İşlem (a OP b) // ============================================================================ -// -// İki operandlı tüm işlemler: a + b, a * b, a == b, a && b, ... -// Unary prefix operatörler de burada temsil edilir (Left = nullptr). -// -// Operator: İşlem tipi (PLUS, MINUS, STAR, EQUAL_EQUAL, ...) -// Left: Sol operand (unary prefix'te nullptr) -// Right: Sağ operand (her zaman dolu) -// -// NEDEN AYRI BİR UnaryExpressionNode YOK? -// Pratt parser'da unary ve binary operatörler aynı akışta işlenir. -// Left'in null olması unary olduğunu belirtir. Bu, kod tekrarını önler. -// İleride AST işlemcisi Left'e bakarak unary/binary ayrımı yapabilir. -// + class BinaryExpressionNode : public ASTNode { public: - TokenType Operator; // İşlem tipi - ASTNode* Left = nullptr; // Sol operand - ASTNode* Right = nullptr; // Sağ operand + TokenType Operator; + ASTNode* Left = nullptr; + ASTNode* Right = nullptr; BinaryExpressionNode() { kind = ASTKind::BinaryExpression; } void log(int indent = 0) override { - // Operatörün enum ismini ve sembolünü göster auto it = OPERATOR_MAP_STRREV.find(Operator); std::string sym = (it != OPERATOR_MAP_STRREV.end()) ? std::string(it->second) : "?"; std::string val; @@ -252,24 +269,41 @@ public: std::cout << padRight("", indent) << "BinaryExpr " << sym << " (" << val << ")\n"; - // Önce sağ, sonra sol yazdır — ağaç görselleştirmesi için if (Right) Right->log(indent + 2); if (Left) Left->log(indent + 2); } + + std::string toJson(int depth = 0) override { + std::string in = jsonIndent(depth); + std::string opSym = "?"; + auto it = OPERATOR_MAP_REV.find(Operator); + if (it != OPERATOR_MAP_REV.end()) opSym = std::string(it->second); + + std::ostringstream ss; + ss << in << "{\n" + << in << " \"kind\": \"BinaryExpression\",\n" + << in << " \"operator\": \"" << jsonEscape(opSym) << "\""; + if (Left) { + ss << ",\n" << in << " \"left\":\n" + << Left->toJson(depth + 2); + } + if (Right) { + ss << ",\n" << in << " \"right\":\n" + << Right->toJson(depth + 2); + } + ss << "\n" << in << "}"; + return ss.str(); + } }; // ============================================================================ // LiteralNode — Sabit Değer // ============================================================================ -// -// Kaynak kodda doğrudan yazılan değerler: 42, "hello", true, false, null. -// lexerToken: Orijinal Token (NumberToken ise isFloat/base bilgisi) -// parserToken: Parser'ın atadığı tip bilgisi -// + class LiteralNode : public ASTNode { public: - Token* lexerToken = nullptr; // Tokenizer'dan gelen orijinal token - ParserToken parserToken; // Parser tarafından zenginleştirilmiş token + Token* lexerToken = nullptr; + ParserToken parserToken; LiteralNode() { kind = ASTKind::Literal; } @@ -277,15 +311,23 @@ public: std::cout << padRight("", indent) << "Literal {" << parserToken.token->token << "}\n"; } + + std::string toJson(int depth = 0) override { + std::string in = jsonIndent(depth); + std::string val = parserToken.token ? parserToken.token->token : "?"; + std::ostringstream ss; + ss << in << "{\n" + << in << " \"kind\": \"Literal\",\n" + << in << " \"value\": \"" << jsonEscape(val) << "\"\n" + << in << "}"; + return ss.str(); + } }; // ============================================================================ // IdentifierNode — Tanımlayıcı Referansı // ============================================================================ -// -// Değişken, fonksiyon, veya tip ismi. Örn: x, myVar, calculate. -// İleride symbol table ile çözümlenecek (bu değişken nerede tanımlı?). -// + class IdentifierNode : public ASTNode { public: Token* lexerToken = nullptr; @@ -297,27 +339,33 @@ public: std::cout << padRight("", indent) << "Identifier {" << parserToken.token->token << "}\n"; } + + std::string toJson(int depth = 0) override { + std::string in = jsonIndent(depth); + std::string name = parserToken.token ? parserToken.token->token : "?"; + std::ostringstream ss; + ss << in << "{\n" + << in << " \"kind\": \"Identifier\",\n" + << in << " \"name\": \"" << jsonEscape(name) << "\"\n" + << in << "}"; + return ss.str(); + } }; // ============================================================================ // PostfixNode — Son Ek İşlem (a++, a--) // ============================================================================ -// -// Operand'dan SONRA gelen operatör. Şu anda sadece ++ ve --. -// operand: İşlem yapılan ifade (genellikle IdentifierNode) -// Operator: PLUS_PLUS veya MINUS_MINUS -// + class PostfixNode : public ASTNode { public: - ASTNode* operand = nullptr; // İşlem yapılan ifade - TokenType Operator; // PLUS_PLUS veya MINUS_MINUS + ASTNode* operand = nullptr; + TokenType Operator; PostfixNode() { kind = ASTKind::Postfix; } void log(int indent = 0) override { auto it = OPERATOR_MAP_STRREV.find(Operator); std::string sym = (it != OPERATOR_MAP_STRREV.end()) ? std::string(it->second) : "?"; - std::cout << padRight("", indent) << "Postfix " << sym; auto it2 = OPERATOR_MAP_REV.find(Operator); if (it2 != OPERATOR_MAP_REV.end()) @@ -325,21 +373,35 @@ public: std::cout << "\n"; if (operand) operand->log(indent + 2); } + + std::string toJson(int depth = 0) override { + std::string in = jsonIndent(depth); + std::string opSym = "?"; + auto it = OPERATOR_MAP_REV.find(Operator); + if (it != OPERATOR_MAP_REV.end()) opSym = std::string(it->second); + + std::ostringstream ss; + ss << in << "{\n" + << in << " \"kind\": \"Postfix\",\n" + << in << " \"operator\": \"" << jsonEscape(opSym) << "\""; + if (operand) { + ss << ",\n" << in << " \"operand\":\n" + << operand->toJson(depth + 2); + } + ss << "\n" << in << "}"; + return ss.str(); + } }; // ============================================================================ // IfStatementNode — if / else // ============================================================================ -// -// condition: Koşul ifadesi (parantez içindeki) -// thenBranch: if gövdesi (BlockNode veya tek statement) -// elseBranch: else gövdesi (opsiyonel, nullptr = else yok) -// + class IfStatementNode : public ASTNode { public: - ASTNode* condition = nullptr; // Koşul - ASTNode* thenBranch = nullptr; // if gövdesi - ASTNode* elseBranch = nullptr; // else gövdesi (opsiyonel) + ASTNode* condition = nullptr; + ASTNode* thenBranch = nullptr; + ASTNode* elseBranch = nullptr; IfStatementNode() { kind = ASTKind::IfStatement; } @@ -354,18 +416,37 @@ public: elseBranch->log(indent + 4); } } + + std::string toJson(int depth = 0) override { + std::string in = jsonIndent(depth); + std::ostringstream ss; + ss << in << "{\n" + << in << " \"kind\": \"IfStatement\""; + if (condition) { + ss << ",\n" << in << " \"condition\":\n" + << condition->toJson(depth + 2); + } + if (thenBranch) { + ss << ",\n" << in << " \"then\":\n" + << thenBranch->toJson(depth + 2); + } + if (elseBranch) { + ss << ",\n" << in << " \"else\":\n" + << elseBranch->toJson(depth + 2); + } + ss << "\n" << in << "}"; + return ss.str(); + } }; // ============================================================================ // WhileStatementNode — while Döngüsü // ============================================================================ -// -// while (condition) body -// + class WhileStatementNode : public ASTNode { public: - ASTNode* condition = nullptr; // Döngü koşulu - ASTNode* body = nullptr; // Döngü gövdesi + ASTNode* condition = nullptr; + ASTNode* body = nullptr; WhileStatementNode() { kind = ASTKind::WhileStatement; } @@ -376,25 +457,35 @@ public: std::cout << padRight("", indent + 2) << "Body:\n"; if (body) body->log(indent + 4); } + + std::string toJson(int depth = 0) override { + std::string in = jsonIndent(depth); + std::ostringstream ss; + ss << in << "{\n" + << in << " \"kind\": \"WhileStatement\""; + if (condition) { + ss << ",\n" << in << " \"condition\":\n" + << condition->toJson(depth + 2); + } + if (body) { + ss << ",\n" << in << " \"body\":\n" + << body->toJson(depth + 2); + } + ss << "\n" << in << "}"; + return ss.str(); + } }; // ============================================================================ // ForStatementNode — for Döngüsü // ============================================================================ -// -// for (init; condition; update) body -// -// init: Başlangıç (VariableDeclNode veya ExpressionStatementNode) -// condition: Devam koşulu (nullptr = sonsuz döngü) -// update: Her adımda çalışan ifade -// body: Döngü gövdesi -// + class ForStatementNode : public ASTNode { public: - ASTNode* init = nullptr; // Başlangıç - ASTNode* condition = nullptr; // Koşul - ASTNode* update = nullptr; // Güncelleme - ASTNode* body = nullptr; // Gövde + ASTNode* init = nullptr; + ASTNode* condition = nullptr; + ASTNode* update = nullptr; + ASTNode* body = nullptr; ForStatementNode() { kind = ASTKind::ForStatement; } @@ -415,14 +506,37 @@ public: std::cout << padRight("", indent + 2) << "Body:\n"; if (body) body->log(indent + 4); } + + std::string toJson(int depth = 0) override { + std::string in = jsonIndent(depth); + std::ostringstream ss; + ss << in << "{\n" + << in << " \"kind\": \"ForStatement\""; + if (init) { + ss << ",\n" << in << " \"init\":\n" + << init->toJson(depth + 2); + } + if (condition) { + ss << ",\n" << in << " \"condition\":\n" + << condition->toJson(depth + 2); + } + if (update) { + ss << ",\n" << in << " \"update\":\n" + << update->toJson(depth + 2); + } + if (body) { + ss << ",\n" << in << " \"body\":\n" + << body->toJson(depth + 2); + } + ss << "\n" << in << "}"; + return ss.str(); + } }; // ============================================================================ // DoWhileStatementNode — do-while Döngüsü // ============================================================================ -// -// do body while (condition); -// + class DoWhileStatementNode : public ASTNode { public: ASTNode* condition = nullptr; @@ -437,18 +551,32 @@ public: std::cout << padRight("", indent + 2) << "Condition:\n"; if (condition) condition->log(indent + 4); } + + std::string toJson(int depth = 0) override { + std::string in = jsonIndent(depth); + std::ostringstream ss; + ss << in << "{\n" + << in << " \"kind\": \"DoWhileStatement\""; + if (body) { + ss << ",\n" << in << " \"body\":\n" + << body->toJson(depth + 2); + } + if (condition) { + ss << ",\n" << in << " \"condition\":\n" + << condition->toJson(depth + 2); + } + ss << "\n" << in << "}"; + return ss.str(); + } }; // ============================================================================ // ReturnStatementNode — return [ifade] // ============================================================================ -// -// value = nullptr ise "return;" (void fonksiyonda) -// value dolu ise "return expr;" -// + class ReturnStatementNode : public ASTNode { public: - ASTNode* value = nullptr; // Dönüş değeri (opsiyonel) + ASTNode* value = nullptr; ReturnStatementNode() { kind = ASTKind::ReturnStatement; } @@ -461,46 +589,64 @@ public: std::cout << " (void)\n"; } } + + std::string toJson(int depth = 0) override { + std::string in = jsonIndent(depth); + std::ostringstream ss; + ss << in << "{\n" + << in << " \"kind\": \"ReturnStatement\""; + if (value) { + ss << ",\n" << in << " \"value\":\n" + << value->toJson(depth + 2); + } + ss << "\n" << in << "}"; + return ss.str(); + } }; // ============================================================================ // BreakStatementNode — break // ============================================================================ -// -// En yakın döngüden veya switch'ten çıkar. -// + class BreakStatementNode : public ASTNode { public: BreakStatementNode() { kind = ASTKind::BreakStatement; } + void log(int indent = 0) override { std::cout << padRight("", indent) << "BreakStatement\n"; } + + std::string toJson(int depth = 0) override { + std::string in = jsonIndent(depth); + return in + "{\n" + in + " \"kind\": \"BreakStatement\"\n" + in + "}"; + } }; // ============================================================================ // ContinueStatementNode — continue // ============================================================================ -// -// En yakın döngünün başına atlar. -// + class ContinueStatementNode : public ASTNode { public: ContinueStatementNode() { kind = ASTKind::ContinueStatement; } + void log(int indent = 0) override { std::cout << padRight("", indent) << "ContinueStatement\n"; } + + std::string toJson(int depth = 0) override { + std::string in = jsonIndent(depth); + return in + "{\n" + in + " \"kind\": \"ContinueStatement\"\n" + in + "}"; + } }; // ============================================================================ // ExpressionStatementNode — İfadeyi Statement Olarak Sarma // ============================================================================ -// -// Bir ifadeyi (expression) statement bağlamında kullanmak için sarar. -// Örn: x = 5; → ExpressionStatementNode( BinaryExpressionNode(x, =, 5) ) -// + class ExpressionStatementNode : public ASTNode { public: - ASTNode* expression = nullptr; // İç ifade + ASTNode* expression = nullptr; ExpressionStatementNode() { kind = ASTKind::ExpressionStatement; } @@ -508,6 +654,19 @@ public: std::cout << padRight("", indent) << "ExpressionStatement\n"; if (expression) expression->log(indent + 2); } + + std::string toJson(int depth = 0) override { + std::string in = jsonIndent(depth); + std::ostringstream ss; + ss << in << "{\n" + << in << " \"kind\": \"ExpressionStatement\""; + if (expression) { + ss << ",\n" << in << " \"expression\":\n" + << expression->toJson(depth + 2); + } + ss << "\n" << in << "}"; + return ss.str(); + } }; #endif // SAQUT_AST diff --git a/src/tools.hpp b/src/tools.hpp index 1ef3768..7b835b0 100644 --- a/src/tools.hpp +++ b/src/tools.hpp @@ -8,7 +8,6 @@ // // AMAÇ: // Tüm derleyici modüllerinin ihtiyaç duyduğu ortak yardımcı fonksiyonlar. -// Şu anda sadece padRight() içerir. // // ============================================================================ @@ -19,15 +18,6 @@ // -------------------------------------------------------------------------- // padRight: String'i sağdan boşluk ile belirtilen uzunluğa tamamla. -// -// KULLANIM: AST ağacını konsola yazdırırken girintileme (indent) için. -// padRight("", indent) → indent adet boşluk döndürür. -// -// ÖRNEK: -// padRight("", 4) → " " -// padRight("abc", 6) → "abc " -// -// NOT: std::setw + std::left ile de yapılabilirdi, ancak bu daha basit. // -------------------------------------------------------------------------- inline std::string padRight(std::string str, size_t totalLen) { if (str.size() < totalLen) { @@ -36,4 +26,30 @@ inline std::string padRight(std::string str, size_t totalLen) { return str; } +// -------------------------------------------------------------------------- +// jsonIndent: JSON çıktısı için girinti (her seviye 2 boşluk) +// -------------------------------------------------------------------------- +inline std::string jsonIndent(int n) { + return std::string(static_cast(n) * 2, ' '); +} + +// -------------------------------------------------------------------------- +// jsonEscape: JSON string değerleri için kaçış karakterleri +// -------------------------------------------------------------------------- +inline std::string jsonEscape(const std::string& s) { + std::string out; + out.reserve(s.size() + 4); + for (char c : s) { + switch (c) { + case '"': out += "\\\""; break; + case '\\': out += "\\\\"; break; + case '\n': out += "\\n"; break; + case '\r': out += "\\r"; break; + case '\t': out += "\\t"; break; + default: out += c; + } + } + return out; +} + #endif // SAQUT_TOOLS