diff --git a/build/.ninja_deps b/build/.ninja_deps index 88e738d..8eaeabe 100644 Binary files a/build/.ninja_deps and b/build/.ninja_deps differ diff --git a/build/.ninja_log b/build/.ninja_log index 5a291ca..266c597 100644 --- a/build/.ninja_log +++ b/build/.ninja_log @@ -10,19 +10,14 @@ 21 7502 1781796718449424977 CMakeFiles/saqut.dir/src/parser/nodes/statements.cpp.o 3c8869307381c930 14 6864 1781796718442362341 CMakeFiles/saqut.dir/src/parser/nodes/binary_expr.cpp.o 5cc8b697133bcf64 15 6733 1781796718442847556 CMakeFiles/saqut.dir/src/parser/nodes/declarations.cpp.o c3d262615ede4c95 -2 4490 1781799598563879630 CMakeFiles/saqut.dir/src/main.cpp.o 3cfef7a665d5bf87 -4490 4758 1781799603051859470 saqut f2e198803c4dbffb -0 22 1781799611852960564 build.ninja 1876a59d627a585 -0 22 1781799611852960564 /home/saqut/Masaüstü/saqutcompiler/build/cmake_install.cmake 1876a59d627a585 +1 4629 1781801148234045650 CMakeFiles/saqut.dir/src/main.cpp.o 3cfef7a665d5bf87 +4629 4900 1781801152862380672 saqut f2e198803c4dbffb +0 22 1781801180493522122 build.ninja 1876a59d627a585 +0 22 1781801180493522122 /home/saqut/Masaüstü/saqutcompiler/build/cmake_install.cmake 1876a59d627a585 6733 11112 1781796725160284765 CMakeFiles/saqut.dir/src/symbol/symbol_collector.cpp.o ec4e483b8ddb4007 4805 9685 1781796723232278341 CMakeFiles/saqut.dir/src/semantic/structural_validator.cpp.o 248faa3675024351 6700 10405 1781796725127284655 CMakeFiles/saqut.dir/src/semantic/type_checker.cpp.o b29c133293d988b0 -2 795 1781799345769990010 CMakeFiles/saqut.dir/src/vm/interpreter.cpp.o b7dd80e002d68a1d -1 668 1781799598562879634 CMakeFiles/saqut.dir/src/ir/ir_function.cpp.o 10f5e8dfd1461d69 -1 1001 1781799106947865509 CMakeFiles/saqut.dir/src/ir/ir_program.cpp.o 9518231d970828da -2 3078 1781799345769137653 CMakeFiles/saqut.dir/src/ir/ir_generator.cpp.o 10a1ed4e1f52e530 -1 636 1781799663202595202 CMakeFiles/saqut.dir/src/ir/ir_function.cpp.o 10f5e8dfd1461d69 -636 892 1781799663837592468 saqut f2e198803c4dbffb -1 653 1781800137590930314 CMakeFiles/saqut.dir/src/ir/ir_program.cpp.o 9518231d970828da -1 658 1781800137589789659 CMakeFiles/saqut.dir/src/ir/ir_function.cpp.o 10f5e8dfd1461d69 -658 919 1781800138246787400 saqut f2e198803c4dbffb +1 850 1781801148235507662 CMakeFiles/saqut.dir/src/vm/interpreter.cpp.o b7dd80e002d68a1d +2 957 1781800866770475511 CMakeFiles/saqut.dir/src/ir/ir_function.cpp.o 10f5e8dfd1461d69 +2 718 1781800866771246136 CMakeFiles/saqut.dir/src/ir/ir_program.cpp.o 9518231d970828da +2 3169 1781800866771136888 CMakeFiles/saqut.dir/src/ir/ir_generator.cpp.o 10a1ed4e1f52e530 diff --git a/examples/merhaba.sqt b/examples/merhaba.sqt new file mode 100644 index 0000000..79f2a9e --- /dev/null +++ b/examples/merhaba.sqt @@ -0,0 +1,5 @@ +int main() { + print("Merhaba"); + print("saQut calisiyor"); + return 0; +} diff --git a/src/cli/commands/run.hpp b/src/cli/commands/run.hpp index 4649799..565a729 100644 --- a/src/cli/commands/run.hpp +++ b/src/cli/commands/run.hpp @@ -19,6 +19,8 @@ #include "parser/parser.hpp" #include "symbol/symbol_table.hpp" #include "symbol/symbol_collector.hpp" +#include "semantic/type_checker.hpp" +#include "semantic/structural_validator.hpp" #include "diagnostic/diagnostic_engine.hpp" #include "ir/ir_generator.hpp" #include "vm/interpreter.hpp" @@ -46,6 +48,8 @@ inline int cmdRun(const CliArgs& args) { SymbolTable symbolTable; DiagnosticEngine diag; SymbolCollector(symbolTable, diag).collect(ast); + TypeChecker(symbolTable, diag).check(ast); + StructuralValidator(diag).validate(ast); if (diag.hasErrors()) { std::cerr << "Derleme hataları var, program çalıştırılamaz:\n"; diff --git a/src/ir/instruction.hpp b/src/ir/instruction.hpp index 831f72a..9ce720f 100644 --- a/src/ir/instruction.hpp +++ b/src/ir/instruction.hpp @@ -37,9 +37,12 @@ enum class Opcode { // --- Değer yükleme --- - LOAD_CONST, // slots[dest] = intValue + LOAD_CONST, // slots[dest] = intValue (tam sayı sabitini slota yükle) // Örnek: LOAD_CONST dest=3 val=10 → slot[3] = 10 + LOAD_STRING, // slots[dest] = stringValue (metin sabitini slota yükle) + // Örnek: LOAD_STRING dest=2 val="Merhaba" → slot[2] = "Merhaba" + LOAD_SLOT, // slots[dest] = slots[src] // Bir slotun değerini başka bir slota kopyalar. // Atama işlemlerinde (x = y) kullanılır. @@ -79,6 +82,7 @@ enum class Opcode { inline const char* opcodeName(Opcode op) { switch (op) { case Opcode::LOAD_CONST: return "LOAD_CONST"; + case Opcode::LOAD_STRING: return "LOAD_STRING"; case Opcode::LOAD_SLOT: return "LOAD_SLOT"; case Opcode::ADD: return "ADD"; case Opcode::SUB: return "SUB"; @@ -121,8 +125,11 @@ struct Instruction { int left = -1; int right = -1; - // LOAD_CONST için yüklenecek sabit değer - int intValue = 0; + // LOAD_CONST için yüklenecek tam sayı sabiti + int intValue = 0; + + // LOAD_STRING için yüklenecek metin sabiti (tırnak işaretleri olmadan) + std::string stringValue; // JMP / JIF_FALSE için hedef instruction indeksi // Üretim sırasında bilinmiyorsa -1 bırakılır, sonradan doldurulur (backpatch). diff --git a/src/ir/ir_function.cpp b/src/ir/ir_function.cpp index 9ef7683..690b566 100644 --- a/src/ir/ir_function.cpp +++ b/src/ir/ir_function.cpp @@ -80,6 +80,9 @@ void IRFunction::dump() const { if (ins.opcode == Opcode::LOAD_CONST) { std::cout << slot(ins.dest) << " = " << ins.intValue; + } else if (ins.opcode == Opcode::LOAD_STRING) { + std::cout << slot(ins.dest) << " = \"" << ins.stringValue << "\""; + } else if (ins.opcode == Opcode::LOAD_SLOT) { std::cout << slot(ins.dest) << " = " << slot(ins.src); diff --git a/src/ir/ir_generator.cpp b/src/ir/ir_generator.cpp index 63355b0..947355d 100644 --- a/src/ir/ir_generator.cpp +++ b/src/ir/ir_generator.cpp @@ -1,4 +1,5 @@ #include "ir/ir_generator.hpp" +#include "tokenizer/token.hpp" #include "parser/nodes/program.hpp" #include "parser/nodes/declarations.hpp" #include "parser/nodes/statements.hpp" @@ -269,25 +270,44 @@ int IRGenerator::generateExpression(ASTNode* node) { switch (lit->literalType) { case LiteralType::INTEGER: { - // Sayı metnini int'e çevir int value = 0; - if (lit->parserToken.token) { + if (lit->parserToken.token) value = std::stoi(lit->parserToken.token->token); - } emitLoadConst(slot, value); break; } case LiteralType::BOOLEAN: { - // true → 1, false → 0 int value = (lit->parserToken.token && lit->parserToken.token->token == "true") ? 1 : 0; emitLoadConst(slot, value); break; } - default: - // float, string vb. → TODO(vm-genişletme) - emitLoadConst(slot, 0); + case LiteralType::STRING: { + // StringToken::context tırnak işaretleri olmadan içeriği tutar + std::string content; + if (auto* st = dynamic_cast(lit->lexerToken)) + content = st->context; + else if (lit->parserToken.token) { + // Fallback: token'ın başındaki ve sonundaki " işaretlerini sıyır + std::string raw = lit->parserToken.token->token; + if (raw.size() >= 2 && raw.front() == '"' && raw.back() == '"') + content = raw.substr(1, raw.size() - 2); + else + content = raw; + } + Instruction ins(Opcode::LOAD_STRING); + ins.dest = slot; + ins.stringValue = std::move(content); + currentFunction_->instructions.push_back(std::move(ins)); break; + } + case LiteralType::FLOAT: + throw std::runtime_error( + "IR üretim hatası: float literal şu an VM tarafından desteklenmiyor. " + "Tam sayı kullanın veya float desteği eklenene kadar bekleyin."); + case LiteralType::BOŞ: + throw std::runtime_error( + "IR üretim hatası: null literal şu an VM tarafından desteklenmiyor."); } return slot; } diff --git a/src/vm/interpreter.cpp b/src/vm/interpreter.cpp index 2be1d36..be466c2 100644 --- a/src/vm/interpreter.cpp +++ b/src/vm/interpreter.cpp @@ -2,223 +2,172 @@ #include #include -// ───────────────────────────────────────────────────────────────────────────── -// run — Ana yorumlayıcı döngüsü -// ───────────────────────────────────────────────────────────────────────────── - int Interpreter::run() { - // "main" fonksiyonunu bul IRFunction* mainFunction = program_.findFunction("main"); - if (!mainFunction) { + if (!mainFunction) throw std::runtime_error("Çalışma hatası: 'main' fonksiyonu bulunamadı"); - } - // main için ilk frame'i oluştur ve stack'e ekle CallFrame mainFrame; mainFrame.function = mainFunction; mainFrame.instructionPointer = 0; mainFrame.slots.resize(mainFunction->slotCount, Value::fromInt(0)); - mainFrame.returnDestSlot = -1; // caller yok - + mainFrame.returnDestSlot = -1; callStack_.push_back(std::move(mainFrame)); - // ── Ana döngü ───────────────────────────────────────────────────────── - // Her iterasyonun başında mevcut frame'i TAZEDEN alırız. - // CALL ve RETURN callStack'i değiştirir; `continue` ile döngü başına - // dönülür ve frame yeniden alınır — dangling pointer sorunu olmaz. - while (!callStack_.empty()) { - - // Her iterasyonda taze referans al (CALL sonrası vector büyüyebilir) CallFrame& frame = callStack_.back(); - // Tüm instruction'lar tükendi mi? (RETURN olmadan biten fonksiyon) if (frame.instructionPointer >= (int)frame.function->instructions.size()) { - // void fonksiyon gibi davran — 0 döndür int destSlot = frame.returnDestSlot; callStack_.pop_back(); - if (!callStack_.empty() && destSlot != -1) { + if (!callStack_.empty() && destSlot != -1) callStack_.back().slots[destSlot] = Value::fromInt(0); - } continue; } - // Sıradaki talimatı al ve ip'yi ÖNCE İLERLET. - // Neden önce? CALL veya RETURN ip'ye dokunmaz. Böylece: - // - CALL: yeni frame ip=0 ile açılır, caller'ın ip'si zaten ilerletilmiş. - // - RETURN sonrası caller kaldığı yerden (ip zaten doğru) devam eder. const Instruction& instr = frame.function->instructions[frame.instructionPointer]; frame.instructionPointer++; - // ── Talimat switch'i ────────────────────────────────────────────── switch (instr.opcode) { - // slots[dest] = sabit değer case Opcode::LOAD_CONST: frame.slots[instr.dest] = Value::fromInt(instr.intValue); break; - // slots[dest] = slots[src] (kopyala) + case Opcode::LOAD_STRING: + frame.slots[instr.dest] = Value::fromString(instr.stringValue); + break; + case Opcode::LOAD_SLOT: frame.slots[instr.dest] = frame.slots[instr.src]; break; - // ── Aritmetik ──────────────────────────────────────────────────── + // ── Aritmetik ───────────────────────────────────────────────────── + // TypeChecker derleme zamanında tipleri doğruladı — burada sadece hesap yapılır. + // İstisna: sıfıra bölme gerçek bir çalışma zamanı koşuludur, kontrol edilir. case Opcode::ADD: frame.slots[instr.dest] = Value::fromInt( frame.slots[instr.left].intValue + frame.slots[instr.right].intValue); break; - case Opcode::SUB: frame.slots[instr.dest] = Value::fromInt( frame.slots[instr.left].intValue - frame.slots[instr.right].intValue); break; - case Opcode::MUL: frame.slots[instr.dest] = Value::fromInt( frame.slots[instr.left].intValue * frame.slots[instr.right].intValue); break; - case Opcode::DIV: { - int divisor = frame.slots[instr.right].intValue; - if (divisor == 0) { - throw std::runtime_error("Çalışma hatası: sıfıra bölme"); - } - frame.slots[instr.dest] = Value::fromInt( - frame.slots[instr.left].intValue / divisor); + int d = frame.slots[instr.right].intValue; + if (d == 0) throw std::runtime_error("Çalışma hatası: sıfıra bölme"); + frame.slots[instr.dest] = Value::fromInt(frame.slots[instr.left].intValue / d); break; } - case Opcode::MOD: { - int divisor = frame.slots[instr.right].intValue; - if (divisor == 0) { - throw std::runtime_error("Çalışma hatası: sıfıra bölme (mod)"); - } - frame.slots[instr.dest] = Value::fromInt( - frame.slots[instr.left].intValue % divisor); + int d = frame.slots[instr.right].intValue; + if (d == 0) throw std::runtime_error("Çalışma hatası: sıfıra bölme (mod)"); + frame.slots[instr.dest] = Value::fromInt(frame.slots[instr.left].intValue % d); break; } - // ── Karşılaştırma (sonuç: 1=doğru, 0=yanlış) ──────────────────── + // ── Karşılaştırma ───────────────────────────────────────────────── case Opcode::LESS: frame.slots[instr.dest] = Value::fromInt( frame.slots[instr.left].intValue < frame.slots[instr.right].intValue ? 1 : 0); break; - case Opcode::LESS_EQUAL: frame.slots[instr.dest] = Value::fromInt( frame.slots[instr.left].intValue <= frame.slots[instr.right].intValue ? 1 : 0); break; - case Opcode::GREATER: frame.slots[instr.dest] = Value::fromInt( frame.slots[instr.left].intValue > frame.slots[instr.right].intValue ? 1 : 0); break; - case Opcode::GREATER_EQUAL: frame.slots[instr.dest] = Value::fromInt( frame.slots[instr.left].intValue >= frame.slots[instr.right].intValue ? 1 : 0); break; - - case Opcode::EQUAL_EQUAL: - frame.slots[instr.dest] = Value::fromInt( - frame.slots[instr.left].intValue == frame.slots[instr.right].intValue ? 1 : 0); + case Opcode::EQUAL_EQUAL: { + auto& lv = frame.slots[instr.left]; auto& rv = frame.slots[instr.right]; + int r = (lv.kind == ValueKind::String) + ? (lv.stringValue == rv.stringValue ? 1 : 0) + : (lv.intValue == rv.intValue ? 1 : 0); + frame.slots[instr.dest] = Value::fromInt(r); break; - - case Opcode::NOT_EQUAL: - frame.slots[instr.dest] = Value::fromInt( - frame.slots[instr.left].intValue != frame.slots[instr.right].intValue ? 1 : 0); + } + case Opcode::NOT_EQUAL: { + auto& lv = frame.slots[instr.left]; auto& rv = frame.slots[instr.right]; + int r = (lv.kind == ValueKind::String) + ? (lv.stringValue != rv.stringValue ? 1 : 0) + : (lv.intValue != rv.intValue ? 1 : 0); + frame.slots[instr.dest] = Value::fromInt(r); break; + } // ── Kontrol akışı ───────────────────────────────────────────────── - - // Koşulsuz atlama case Opcode::JMP: frame.instructionPointer = instr.jumpTarget; break; - - // Koşullu atlama: slot[cond] == 0 (yanlış) ise atla case Opcode::JIF_FALSE: - if (!frame.slots[instr.cond].isTruthy()) { + if (!frame.slots[instr.cond].isTruthy()) frame.instructionPointer = instr.jumpTarget; - } break; // ── Fonksiyon çağrısı ───────────────────────────────────────────── - // Yeni frame oluştur, argümanları parametre slotlarına kopyala, - // stack'e ekle. `continue` ile döngü başına dön — yeni frame çalışmaya başlar. case Opcode::CALL: { IRFunction* callee = program_.findFunction(instr.functionName); - if (!callee) { + if (!callee) throw std::runtime_error( "Çalışma hatası: '" + instr.functionName + "' fonksiyonu bulunamadı"); - } - // Yeni frame hazırla CallFrame newFrame; newFrame.function = callee; newFrame.instructionPointer = 0; newFrame.slots.resize(callee->slotCount, Value::fromInt(0)); - newFrame.returnDestSlot = instr.dest; // sonuç bu slota yazılacak + newFrame.returnDestSlot = instr.dest; - // Argümanları parametre slotlarına kopyala (slot 0, 1, 2, ...) - for (int i = 0; i < (int)instr.argSlots.size(); i++) { + for (int i = 0; i < (int)instr.argSlots.size(); i++) newFrame.slots[i] = frame.slots[instr.argSlots[i]]; - } - // Frame'i stack'e ekle — SONRA `continue` ile döngü başına dön. - // Böylece bir sonraki iterasyonda bu yeni frame çalışmaya başlar. callStack_.push_back(std::move(newFrame)); - continue; // ← frame referansı burada yenilenir, dangling pointer yok + continue; } // ── Dönüş ───────────────────────────────────────────────────────── - // Dönüş değerini caller'ın beklediği slota yaz, bu frame'i kapat. case Opcode::RETURN: { Value returnValue = frame.slots[instr.src]; int returnDestSlot = frame.returnDestSlot; - - // Bu frame'i kapat callStack_.pop_back(); - // Caller varsa dönüş değerini onun slotuna yaz - if (!callStack_.empty() && returnDestSlot != -1) { + if (!callStack_.empty() && returnDestSlot != -1) callStack_.back().slots[returnDestSlot] = returnValue; - } - // main fonksiyonu döndü → program bitti - if (callStack_.empty()) { + if (callStack_.empty()) return returnValue.intValue; - } - continue; // ← bir sonraki iterasyonda caller frame tazeden alınır + continue; } - // ── FFI: Host fonksiyon çağrısı ─────────────────────────────────── + // ── FFI ─────────────────────────────────────────────────────────── case Opcode::CALLHOST: executeHostFunction(instr.functionName, frame.slots, instr.argSlots); break; } } - return 0; // Normal çıkış + return 0; } -// ───────────────────────────────────────────────────────────────────────────── -// executeHostFunction — C++ tarafında tanımlı fonksiyonları çağır -// ───────────────────────────────────────────────────────────────────────────── - -void Interpreter::executeHostFunction(const std::string& name, +void Interpreter::executeHostFunction(const std::string& name, const std::vector& slots, const std::vector& argSlots) { if (name == "print") { - // print(değer) — stdout'a değeri yazdır if (!argSlots.empty()) { - std::cout << slots[argSlots[0]].intValue << "\n"; + const Value& val = slots[argSlots[0]]; + if (val.kind == ValueKind::String) std::cout << val.stringValue << "\n"; + else std::cout << val.intValue << "\n"; } return; } - - // Bilinmeyen host fonksiyon throw std::runtime_error("Çalışma hatası: bilinmeyen host fonksiyonu '" + name + "'"); } diff --git a/src/vm/value.hpp b/src/vm/value.hpp index c1103d2..70b93df 100644 --- a/src/vm/value.hpp +++ b/src/vm/value.hpp @@ -15,20 +15,22 @@ #ifndef SAQUT_VM_VALUE #define SAQUT_VM_VALUE +#include + // Gelecekte float/bool/string eklendiğinde burası genişleyecek. // Şimdilik sadece int. enum class ValueKind { Int, + String, // Float, // TODO(vm-genişletme) // Bool, // TODO(vm-genişletme) - // String, // TODO(vm-genişletme) }; struct Value { - ValueKind kind = ValueKind::Int; - int intValue = 0; + ValueKind kind = ValueKind::Int; + int intValue = 0; + std::string stringValue; // yalnızca kind == String için geçerli - // Kolay oluşturma static Value fromInt(int n) { Value v; v.kind = ValueKind::Int; @@ -36,8 +38,28 @@ struct Value { return v; } - // JIF_FALSE için: 0 = yanlış, diğer = doğru - bool isTruthy() const { return intValue != 0; } + static Value fromString(std::string s) { + Value v; + v.kind = ValueKind::String; + v.stringValue = std::move(s); + return v; + } + + // JIF_FALSE için: int 0 = yanlış, boş string = yanlış, diğer = doğru + bool isTruthy() const { + if (kind == ValueKind::Int) return intValue != 0; + if (kind == ValueKind::String) return !stringValue.empty(); + return false; + } + + // Okunabilir metin — dump ve hata mesajları için + std::string typeName() const { + switch (kind) { + case ValueKind::Int: return "int"; + case ValueKind::String: return "string"; + } + return "?"; + } }; #endif // SAQUT_VM_VALUE