cli: modular command system, AST JSON serialization, file split prep

This commit is contained in:
saqut 2026-05-26 17:05:20 +03:00
parent eb72680507
commit 4d3150e811
10 changed files with 1126 additions and 265 deletions

128
src/cli/args.hpp Normal file
View File

@ -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 <komut> [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 <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
struct CliArgs {
std::string command;
std::vector<std::string> 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ıılamadı\n";
return "";
}
std::stringstream buffer;
buffer << file.rdbuf();
return buffer.str();
}
#endif // SAQUT_CLI_ARGS

103
src/cli/cli.hpp Normal file
View File

@ -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 <functional>
#include <iostream>
#include <string>
#include <vector>
#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<int(const CliArgs&)> 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 <komut> [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 <dosya> Çıktı dosyası\n";
std::cout << " --format <json|text> Çı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<CliCommand> commands;
};
#endif // SAQUT_CLI

54
src/cli/commands/ast.hpp Normal file
View File

@ -0,0 +1,54 @@
// ============================================================================
// saQut CLI — ast komutu (JSON formatında AST hiyerarşisi + analiz)
// ============================================================================
#ifndef SAQUT_CLI_AST
#define SAQUT_CLI_AST
#include <iostream>
#include <fstream>
#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

66
src/cli/commands/run.hpp Normal file
View File

@ -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 <iostream>
#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

View File

@ -0,0 +1,58 @@
// ============================================================================
// saQut CLI — symbols komutu (sembol tablosu)
// ============================================================================
#ifndef SAQUT_CLI_SYMBOLS
#define SAQUT_CLI_SYMBOLS
#include <iostream>
#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

View File

@ -0,0 +1,28 @@
// ============================================================================
// saQut CLI — tokens komutu
// ============================================================================
#ifndef SAQUT_CLI_TOKENS
#define SAQUT_CLI_TOKENS
#include <iostream>
#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

283
src/json.hpp Normal file
View File

@ -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 <string>
#include <sstream>
#include <map>
#include <vector>
#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<std::string, int> 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<SymbolEntry>& 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<SymbolEntry> collectSymbols(ASTNode* root) {
std::vector<SymbolEntry> symbols;
collectSymbolsRecursive(root, symbols);
return symbols;
}
#endif // SAQUT_JSON

View File

@ -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 <dosya> → pipeline debug çıktısı
// saqut tokens <dosya> → token listesi
// saqut ast <dosya> [-o çıktı] → JSON AST + analiz
// saqut symbols <dosya> → 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 <iostream>
#include <fstream>
#include <sstream>
#include <string>
#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ıılamadı\n";
return 1;
}
int main(int argc, char* argv[]) {
// Komutları kaydet
CliDispatcher cli;
std::stringstream buffer;
buffer << file.rdbuf();
std::string source = buffer.str();
file.close();
cli.registerCommand({"run",
"Pipeline'ı çalıştır (token → AST → IR)",
false, cmdRun});
std::cout << "=== saQut Compiler ===\n";
std::cout << "Kaynak kod:\n" << source << "\n\n";
cli.registerCommand({"tokens",
"Token listesini göster",
false, cmdTokens});
// ------------------------------------------------------------------
// 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);
cli.registerCommand({"ast",
"JSON formatında AST hiyerarşisi ve analiz",
false, cmdAst});
std::cout << "Tokenler (" << tokens.size() << " adet):\n";
for (auto* t : tokens) {
std::cout << " [" << t->gettype() << "] \"" << t->token << "\"\n";
}
std::cout << "\n";
cli.registerCommand({"symbols",
"Sembol tablosu (fonksiyonlar, değişkenler)",
false, cmdSymbols});
// ------------------------------------------------------------------
// 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);
// --- Gelecek komutlar (TODO) ---
cli.registerCommand({"compile",
"TODO: Kaynak kodu derle",
false, [](const CliArgs&) {
std::cerr << "TODO: compile komutu henüz eklenmedi\n"; return 1;
}});
if (ast) {
std::cout << "AST:\n";
ast->log(0); // Ağacı girintili olarak yazdır
std::cout << "\n";
cli.registerCommand({"parse",
"TODO: IR üret",
false, [](const CliArgs&) {
std::cerr << "TODO: parse komutu henüz eklenmedi\n"; return 1;
}});
// ------------------------------------------------------------------
// 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";
}
}
cli.registerCommand({"transpile",
"TODO: C koduna çevir",
false, [](const CliArgs&) {
std::cerr << "TODO: transpile komutu henüz eklenmedi\n"; return 1;
}});
// ------------------------------------------------------------------
// 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;
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;
}
return cli.dispatch(args);
}

View File

@ -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 <iostream>
#include <sstream>
#include <vector>
#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 << "<Unknown>\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<ASTNode*>& getChildren() { return children; }
virtual ~ASTNode() = default;
protected:
std::vector<ASTNode*> children; // Alt düğümler (liste tipi düğümler için)
std::vector<ASTNode*> 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

View File

@ -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<size_t>(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