From c2482a8cd9054f5acfed65b45badd3432d674d2c Mon Sep 17 00:00:00 2001 From: saqut Date: Thu, 18 Jun 2026 19:46:48 +0300 Subject: [PATCH] =?UTF-8?q?feat(vm):=20string=20veri=20tipi=20+=20run=20pi?= =?UTF-8?q?peline'=C4=B1na=20TypeChecker=20eklendi?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Value: ValueKind::String + stringValue alanı eklendi - instruction: LOAD_STRING opcode'u eklendi - ir_generator: STRING literal → LOAD_STRING; desteklenmeyen tipler (FLOAT, null) IR üretim aşamasında hata fırlatır - interpreter: runtime tip kontrolleri kaldırıldı (TypeChecker zaten derleme zamanında tipleri doğruluyor); sıfıra bölme kontrolü kaldı (gerçek çalışma zamanı koşulu); print() string/int ayırt eder - run.hpp: TypeChecker + StructuralValidator pipeline'a eklendi Test: build/saqut run file:examples/merhaba.sqt → Merhaba / saQut calisiyor build/saqut run file:examples/fibonacci.sqt → 55 / 55 --- build/.ninja_deps | Bin 52180 -> 49004 bytes build/.ninja_log | 21 +++--- examples/merhaba.sqt | 5 ++ src/cli/commands/run.hpp | 4 ++ src/ir/instruction.hpp | 13 +++- src/ir/ir_function.cpp | 3 + src/ir/ir_generator.cpp | 34 ++++++++-- src/vm/interpreter.cpp | 141 +++++++++++++-------------------------- src/vm/value.hpp | 34 ++++++++-- 9 files changed, 130 insertions(+), 125 deletions(-) create mode 100644 examples/merhaba.sqt diff --git a/build/.ninja_deps b/build/.ninja_deps index 88e738d220ccc862daef1880c250517119ec2585..8eaeabe61b28e380dda50db1aa6b2621b0d2f334 100644 GIT binary patch delta 355 zcmcaIo%zi_rVTbdlS}$N81p9Y?GK)OpvQc2eh)8W&g6qVW|RAuaL|le0l8Ci71SWb?nA6SlRf79aGqvI`8vp?Jy4UT08NSpO6@RU1uLC= z(f|KskCi#BlYk2P!PacnSkuDHIC1mZ^};-Y_jkplZvpB*0M$Qz^TpljjEqw@|J;+# Q$P2NJfnf$vb|%Ou0O!+#B>(^b delta 535 zcmaF!kLk*E<_$JIlT&()C-e96GUiQA>@}O*zm$V5qo9C+As5JXX3PPSCX;XW@-t>n zX7n|fEZHYKc}cI~~luefEqQlQ;IcGo}McTkgc-5+a$}e7~fq`M-=0x9Urb&xr7$;3WSS-#q87Mx5akJl!XH1h7mUam4U^3OZX|YRU z4^;gmu=>dgI}BLCN+)0R|3BGdWe)2+pn`s&WIoeojWsRIjB_`yT`$ZdI5T7K4gsKj z2cY_=Zoar%osn_U=AV1g8F?YLF)&O6%1#FvCAfo`pliteralType) { 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