cli: modular command system, AST JSON serialization, file split prep
This commit is contained in:
parent
eb72680507
commit
4d3150e811
|
|
@ -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ı açılamadı\n";
|
||||
return "";
|
||||
}
|
||||
std::stringstream buffer;
|
||||
buffer << file.rdbuf();
|
||||
return buffer.str();
|
||||
}
|
||||
|
||||
#endif // SAQUT_CLI_ARGS
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
158
src/main.cpp
158
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 <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ı açı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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue