feat(vm): string veri tipi + run pipeline'ına TypeChecker eklendi
- 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 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
a4bd5110b3
commit
539e08e521
Binary file not shown.
|
|
@ -10,19 +10,14 @@
|
||||||
21 7502 1781796718449424977 CMakeFiles/saqut.dir/src/parser/nodes/statements.cpp.o 3c8869307381c930
|
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
|
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
|
15 6733 1781796718442847556 CMakeFiles/saqut.dir/src/parser/nodes/declarations.cpp.o c3d262615ede4c95
|
||||||
2 4490 1781799598563879630 CMakeFiles/saqut.dir/src/main.cpp.o 3cfef7a665d5bf87
|
1 4629 1781801148234045650 CMakeFiles/saqut.dir/src/main.cpp.o 3cfef7a665d5bf87
|
||||||
4490 4758 1781799603051859470 saqut f2e198803c4dbffb
|
4629 4900 1781801152862380672 saqut f2e198803c4dbffb
|
||||||
0 22 1781799611852960564 build.ninja 1876a59d627a585
|
0 22 1781801180493522122 build.ninja 1876a59d627a585
|
||||||
0 22 1781799611852960564 /home/saqut/Masaüstü/saqutcompiler/build/cmake_install.cmake 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
|
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
|
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
|
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 850 1781801148235507662 CMakeFiles/saqut.dir/src/vm/interpreter.cpp.o b7dd80e002d68a1d
|
||||||
1 668 1781799598562879634 CMakeFiles/saqut.dir/src/ir/ir_function.cpp.o 10f5e8dfd1461d69
|
2 957 1781800866770475511 CMakeFiles/saqut.dir/src/ir/ir_function.cpp.o 10f5e8dfd1461d69
|
||||||
1 1001 1781799106947865509 CMakeFiles/saqut.dir/src/ir/ir_program.cpp.o 9518231d970828da
|
2 718 1781800866771246136 CMakeFiles/saqut.dir/src/ir/ir_program.cpp.o 9518231d970828da
|
||||||
2 3078 1781799345769137653 CMakeFiles/saqut.dir/src/ir/ir_generator.cpp.o 10a1ed4e1f52e530
|
2 3169 1781800866771136888 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
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
int main() {
|
||||||
|
print("Merhaba");
|
||||||
|
print("saQut calisiyor");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -19,6 +19,8 @@
|
||||||
#include "parser/parser.hpp"
|
#include "parser/parser.hpp"
|
||||||
#include "symbol/symbol_table.hpp"
|
#include "symbol/symbol_table.hpp"
|
||||||
#include "symbol/symbol_collector.hpp"
|
#include "symbol/symbol_collector.hpp"
|
||||||
|
#include "semantic/type_checker.hpp"
|
||||||
|
#include "semantic/structural_validator.hpp"
|
||||||
#include "diagnostic/diagnostic_engine.hpp"
|
#include "diagnostic/diagnostic_engine.hpp"
|
||||||
#include "ir/ir_generator.hpp"
|
#include "ir/ir_generator.hpp"
|
||||||
#include "vm/interpreter.hpp"
|
#include "vm/interpreter.hpp"
|
||||||
|
|
@ -46,6 +48,8 @@ inline int cmdRun(const CliArgs& args) {
|
||||||
SymbolTable symbolTable;
|
SymbolTable symbolTable;
|
||||||
DiagnosticEngine diag;
|
DiagnosticEngine diag;
|
||||||
SymbolCollector(symbolTable, diag).collect(ast);
|
SymbolCollector(symbolTable, diag).collect(ast);
|
||||||
|
TypeChecker(symbolTable, diag).check(ast);
|
||||||
|
StructuralValidator(diag).validate(ast);
|
||||||
|
|
||||||
if (diag.hasErrors()) {
|
if (diag.hasErrors()) {
|
||||||
std::cerr << "Derleme hataları var, program çalıştırılamaz:\n";
|
std::cerr << "Derleme hataları var, program çalıştırılamaz:\n";
|
||||||
|
|
|
||||||
|
|
@ -37,9 +37,12 @@
|
||||||
enum class Opcode {
|
enum class Opcode {
|
||||||
|
|
||||||
// --- Değer yükleme ---
|
// --- 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
|
// Ö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]
|
LOAD_SLOT, // slots[dest] = slots[src]
|
||||||
// Bir slotun değerini başka bir slota kopyalar.
|
// Bir slotun değerini başka bir slota kopyalar.
|
||||||
// Atama işlemlerinde (x = y) kullanılır.
|
// Atama işlemlerinde (x = y) kullanılır.
|
||||||
|
|
@ -79,6 +82,7 @@ enum class Opcode {
|
||||||
inline const char* opcodeName(Opcode op) {
|
inline const char* opcodeName(Opcode op) {
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case Opcode::LOAD_CONST: return "LOAD_CONST";
|
case Opcode::LOAD_CONST: return "LOAD_CONST";
|
||||||
|
case Opcode::LOAD_STRING: return "LOAD_STRING";
|
||||||
case Opcode::LOAD_SLOT: return "LOAD_SLOT";
|
case Opcode::LOAD_SLOT: return "LOAD_SLOT";
|
||||||
case Opcode::ADD: return "ADD";
|
case Opcode::ADD: return "ADD";
|
||||||
case Opcode::SUB: return "SUB";
|
case Opcode::SUB: return "SUB";
|
||||||
|
|
@ -121,9 +125,12 @@ struct Instruction {
|
||||||
int left = -1;
|
int left = -1;
|
||||||
int right = -1;
|
int right = -1;
|
||||||
|
|
||||||
// LOAD_CONST için yüklenecek sabit değer
|
// LOAD_CONST için yüklenecek tam sayı sabiti
|
||||||
int intValue = 0;
|
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
|
// JMP / JIF_FALSE için hedef instruction indeksi
|
||||||
// Üretim sırasında bilinmiyorsa -1 bırakılır, sonradan doldurulur (backpatch).
|
// Üretim sırasında bilinmiyorsa -1 bırakılır, sonradan doldurulur (backpatch).
|
||||||
int jumpTarget = -1;
|
int jumpTarget = -1;
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,9 @@ void IRFunction::dump() const {
|
||||||
if (ins.opcode == Opcode::LOAD_CONST) {
|
if (ins.opcode == Opcode::LOAD_CONST) {
|
||||||
std::cout << slot(ins.dest) << " = " << ins.intValue;
|
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) {
|
} else if (ins.opcode == Opcode::LOAD_SLOT) {
|
||||||
std::cout << slot(ins.dest) << " = " << slot(ins.src);
|
std::cout << slot(ins.dest) << " = " << slot(ins.src);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include "ir/ir_generator.hpp"
|
#include "ir/ir_generator.hpp"
|
||||||
|
#include "tokenizer/token.hpp"
|
||||||
#include "parser/nodes/program.hpp"
|
#include "parser/nodes/program.hpp"
|
||||||
#include "parser/nodes/declarations.hpp"
|
#include "parser/nodes/declarations.hpp"
|
||||||
#include "parser/nodes/statements.hpp"
|
#include "parser/nodes/statements.hpp"
|
||||||
|
|
@ -269,26 +270,45 @@ int IRGenerator::generateExpression(ASTNode* node) {
|
||||||
|
|
||||||
switch (lit->literalType) {
|
switch (lit->literalType) {
|
||||||
case LiteralType::INTEGER: {
|
case LiteralType::INTEGER: {
|
||||||
// Sayı metnini int'e çevir
|
|
||||||
int value = 0;
|
int value = 0;
|
||||||
if (lit->parserToken.token) {
|
if (lit->parserToken.token)
|
||||||
value = std::stoi(lit->parserToken.token->token);
|
value = std::stoi(lit->parserToken.token->token);
|
||||||
}
|
|
||||||
emitLoadConst(slot, value);
|
emitLoadConst(slot, value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case LiteralType::BOOLEAN: {
|
case LiteralType::BOOLEAN: {
|
||||||
// true → 1, false → 0
|
|
||||||
int value = (lit->parserToken.token &&
|
int value = (lit->parserToken.token &&
|
||||||
lit->parserToken.token->token == "true") ? 1 : 0;
|
lit->parserToken.token->token == "true") ? 1 : 0;
|
||||||
emitLoadConst(slot, value);
|
emitLoadConst(slot, value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
case LiteralType::STRING: {
|
||||||
// float, string vb. → TODO(vm-genişletme)
|
// StringToken::context tırnak işaretleri olmadan içeriği tutar
|
||||||
emitLoadConst(slot, 0);
|
std::string content;
|
||||||
|
if (auto* st = dynamic_cast<StringToken*>(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;
|
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;
|
return slot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,223 +2,172 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
// ─────────────────────────────────────────────────────────────────────────────
|
|
||||||
// run — Ana yorumlayıcı döngüsü
|
|
||||||
// ─────────────────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
int Interpreter::run() {
|
int Interpreter::run() {
|
||||||
// "main" fonksiyonunu bul
|
|
||||||
IRFunction* mainFunction = program_.findFunction("main");
|
IRFunction* mainFunction = program_.findFunction("main");
|
||||||
if (!mainFunction) {
|
if (!mainFunction)
|
||||||
throw std::runtime_error("Çalışma hatası: 'main' fonksiyonu bulunamadı");
|
throw std::runtime_error("Çalışma hatası: 'main' fonksiyonu bulunamadı");
|
||||||
}
|
|
||||||
|
|
||||||
// main için ilk frame'i oluştur ve stack'e ekle
|
|
||||||
CallFrame mainFrame;
|
CallFrame mainFrame;
|
||||||
mainFrame.function = mainFunction;
|
mainFrame.function = mainFunction;
|
||||||
mainFrame.instructionPointer = 0;
|
mainFrame.instructionPointer = 0;
|
||||||
mainFrame.slots.resize(mainFunction->slotCount, Value::fromInt(0));
|
mainFrame.slots.resize(mainFunction->slotCount, Value::fromInt(0));
|
||||||
mainFrame.returnDestSlot = -1; // caller yok
|
mainFrame.returnDestSlot = -1;
|
||||||
|
|
||||||
callStack_.push_back(std::move(mainFrame));
|
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()) {
|
while (!callStack_.empty()) {
|
||||||
|
|
||||||
// Her iterasyonda taze referans al (CALL sonrası vector büyüyebilir)
|
|
||||||
CallFrame& frame = callStack_.back();
|
CallFrame& frame = callStack_.back();
|
||||||
|
|
||||||
// Tüm instruction'lar tükendi mi? (RETURN olmadan biten fonksiyon)
|
|
||||||
if (frame.instructionPointer >= (int)frame.function->instructions.size()) {
|
if (frame.instructionPointer >= (int)frame.function->instructions.size()) {
|
||||||
// void fonksiyon gibi davran — 0 döndür
|
|
||||||
int destSlot = frame.returnDestSlot;
|
int destSlot = frame.returnDestSlot;
|
||||||
callStack_.pop_back();
|
callStack_.pop_back();
|
||||||
if (!callStack_.empty() && destSlot != -1) {
|
if (!callStack_.empty() && destSlot != -1)
|
||||||
callStack_.back().slots[destSlot] = Value::fromInt(0);
|
callStack_.back().slots[destSlot] = Value::fromInt(0);
|
||||||
}
|
|
||||||
continue;
|
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];
|
const Instruction& instr = frame.function->instructions[frame.instructionPointer];
|
||||||
frame.instructionPointer++;
|
frame.instructionPointer++;
|
||||||
|
|
||||||
// ── Talimat switch'i ──────────────────────────────────────────────
|
|
||||||
switch (instr.opcode) {
|
switch (instr.opcode) {
|
||||||
|
|
||||||
// slots[dest] = sabit değer
|
|
||||||
case Opcode::LOAD_CONST:
|
case Opcode::LOAD_CONST:
|
||||||
frame.slots[instr.dest] = Value::fromInt(instr.intValue);
|
frame.slots[instr.dest] = Value::fromInt(instr.intValue);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// slots[dest] = slots[src] (kopyala)
|
case Opcode::LOAD_STRING:
|
||||||
|
frame.slots[instr.dest] = Value::fromString(instr.stringValue);
|
||||||
|
break;
|
||||||
|
|
||||||
case Opcode::LOAD_SLOT:
|
case Opcode::LOAD_SLOT:
|
||||||
frame.slots[instr.dest] = frame.slots[instr.src];
|
frame.slots[instr.dest] = frame.slots[instr.src];
|
||||||
break;
|
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:
|
case Opcode::ADD:
|
||||||
frame.slots[instr.dest] = Value::fromInt(
|
frame.slots[instr.dest] = Value::fromInt(
|
||||||
frame.slots[instr.left].intValue + frame.slots[instr.right].intValue);
|
frame.slots[instr.left].intValue + frame.slots[instr.right].intValue);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Opcode::SUB:
|
case Opcode::SUB:
|
||||||
frame.slots[instr.dest] = Value::fromInt(
|
frame.slots[instr.dest] = Value::fromInt(
|
||||||
frame.slots[instr.left].intValue - frame.slots[instr.right].intValue);
|
frame.slots[instr.left].intValue - frame.slots[instr.right].intValue);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Opcode::MUL:
|
case Opcode::MUL:
|
||||||
frame.slots[instr.dest] = Value::fromInt(
|
frame.slots[instr.dest] = Value::fromInt(
|
||||||
frame.slots[instr.left].intValue * frame.slots[instr.right].intValue);
|
frame.slots[instr.left].intValue * frame.slots[instr.right].intValue);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Opcode::DIV: {
|
case Opcode::DIV: {
|
||||||
int divisor = frame.slots[instr.right].intValue;
|
int d = frame.slots[instr.right].intValue;
|
||||||
if (divisor == 0) {
|
if (d == 0) throw std::runtime_error("Çalışma hatası: sıfıra bölme");
|
||||||
throw std::runtime_error("Çalışma hatası: sıfıra bölme");
|
frame.slots[instr.dest] = Value::fromInt(frame.slots[instr.left].intValue / d);
|
||||||
}
|
|
||||||
frame.slots[instr.dest] = Value::fromInt(
|
|
||||||
frame.slots[instr.left].intValue / divisor);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Opcode::MOD: {
|
case Opcode::MOD: {
|
||||||
int divisor = frame.slots[instr.right].intValue;
|
int d = frame.slots[instr.right].intValue;
|
||||||
if (divisor == 0) {
|
if (d == 0) throw std::runtime_error("Çalışma hatası: sıfıra bölme (mod)");
|
||||||
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);
|
||||||
}
|
|
||||||
frame.slots[instr.dest] = Value::fromInt(
|
|
||||||
frame.slots[instr.left].intValue % divisor);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Karşılaştırma (sonuç: 1=doğru, 0=yanlış) ────────────────────
|
// ── Karşılaştırma ─────────────────────────────────────────────────
|
||||||
case Opcode::LESS:
|
case Opcode::LESS:
|
||||||
frame.slots[instr.dest] = Value::fromInt(
|
frame.slots[instr.dest] = Value::fromInt(
|
||||||
frame.slots[instr.left].intValue < frame.slots[instr.right].intValue ? 1 : 0);
|
frame.slots[instr.left].intValue < frame.slots[instr.right].intValue ? 1 : 0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Opcode::LESS_EQUAL:
|
case Opcode::LESS_EQUAL:
|
||||||
frame.slots[instr.dest] = Value::fromInt(
|
frame.slots[instr.dest] = Value::fromInt(
|
||||||
frame.slots[instr.left].intValue <= frame.slots[instr.right].intValue ? 1 : 0);
|
frame.slots[instr.left].intValue <= frame.slots[instr.right].intValue ? 1 : 0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Opcode::GREATER:
|
case Opcode::GREATER:
|
||||||
frame.slots[instr.dest] = Value::fromInt(
|
frame.slots[instr.dest] = Value::fromInt(
|
||||||
frame.slots[instr.left].intValue > frame.slots[instr.right].intValue ? 1 : 0);
|
frame.slots[instr.left].intValue > frame.slots[instr.right].intValue ? 1 : 0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Opcode::GREATER_EQUAL:
|
case Opcode::GREATER_EQUAL:
|
||||||
frame.slots[instr.dest] = Value::fromInt(
|
frame.slots[instr.dest] = Value::fromInt(
|
||||||
frame.slots[instr.left].intValue >= frame.slots[instr.right].intValue ? 1 : 0);
|
frame.slots[instr.left].intValue >= frame.slots[instr.right].intValue ? 1 : 0);
|
||||||
break;
|
break;
|
||||||
|
case Opcode::EQUAL_EQUAL: {
|
||||||
case Opcode::EQUAL_EQUAL:
|
auto& lv = frame.slots[instr.left]; auto& rv = frame.slots[instr.right];
|
||||||
frame.slots[instr.dest] = Value::fromInt(
|
int r = (lv.kind == ValueKind::String)
|
||||||
frame.slots[instr.left].intValue == frame.slots[instr.right].intValue ? 1 : 0);
|
? (lv.stringValue == rv.stringValue ? 1 : 0)
|
||||||
|
: (lv.intValue == rv.intValue ? 1 : 0);
|
||||||
|
frame.slots[instr.dest] = Value::fromInt(r);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case Opcode::NOT_EQUAL:
|
case Opcode::NOT_EQUAL: {
|
||||||
frame.slots[instr.dest] = Value::fromInt(
|
auto& lv = frame.slots[instr.left]; auto& rv = frame.slots[instr.right];
|
||||||
frame.slots[instr.left].intValue != frame.slots[instr.right].intValue ? 1 : 0);
|
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;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// ── Kontrol akışı ─────────────────────────────────────────────────
|
// ── Kontrol akışı ─────────────────────────────────────────────────
|
||||||
|
|
||||||
// Koşulsuz atlama
|
|
||||||
case Opcode::JMP:
|
case Opcode::JMP:
|
||||||
frame.instructionPointer = instr.jumpTarget;
|
frame.instructionPointer = instr.jumpTarget;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Koşullu atlama: slot[cond] == 0 (yanlış) ise atla
|
|
||||||
case Opcode::JIF_FALSE:
|
case Opcode::JIF_FALSE:
|
||||||
if (!frame.slots[instr.cond].isTruthy()) {
|
if (!frame.slots[instr.cond].isTruthy())
|
||||||
frame.instructionPointer = instr.jumpTarget;
|
frame.instructionPointer = instr.jumpTarget;
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// ── Fonksiyon çağrısı ─────────────────────────────────────────────
|
// ── 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: {
|
case Opcode::CALL: {
|
||||||
IRFunction* callee = program_.findFunction(instr.functionName);
|
IRFunction* callee = program_.findFunction(instr.functionName);
|
||||||
if (!callee) {
|
if (!callee)
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"Çalışma hatası: '" + instr.functionName + "' fonksiyonu bulunamadı");
|
"Çalışma hatası: '" + instr.functionName + "' fonksiyonu bulunamadı");
|
||||||
}
|
|
||||||
|
|
||||||
// Yeni frame hazırla
|
|
||||||
CallFrame newFrame;
|
CallFrame newFrame;
|
||||||
newFrame.function = callee;
|
newFrame.function = callee;
|
||||||
newFrame.instructionPointer = 0;
|
newFrame.instructionPointer = 0;
|
||||||
newFrame.slots.resize(callee->slotCount, Value::fromInt(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]];
|
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));
|
callStack_.push_back(std::move(newFrame));
|
||||||
continue; // ← frame referansı burada yenilenir, dangling pointer yok
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Dönüş ─────────────────────────────────────────────────────────
|
// ── Dönüş ─────────────────────────────────────────────────────────
|
||||||
// Dönüş değerini caller'ın beklediği slota yaz, bu frame'i kapat.
|
|
||||||
case Opcode::RETURN: {
|
case Opcode::RETURN: {
|
||||||
Value returnValue = frame.slots[instr.src];
|
Value returnValue = frame.slots[instr.src];
|
||||||
int returnDestSlot = frame.returnDestSlot;
|
int returnDestSlot = frame.returnDestSlot;
|
||||||
|
|
||||||
// Bu frame'i kapat
|
|
||||||
callStack_.pop_back();
|
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;
|
callStack_.back().slots[returnDestSlot] = returnValue;
|
||||||
}
|
|
||||||
|
|
||||||
// main fonksiyonu döndü → program bitti
|
if (callStack_.empty())
|
||||||
if (callStack_.empty()) {
|
|
||||||
return returnValue.intValue;
|
return returnValue.intValue;
|
||||||
|
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
continue; // ← bir sonraki iterasyonda caller frame tazeden alınır
|
// ── FFI ───────────────────────────────────────────────────────────
|
||||||
}
|
|
||||||
|
|
||||||
// ── FFI: Host fonksiyon çağrısı ───────────────────────────────────
|
|
||||||
case Opcode::CALLHOST:
|
case Opcode::CALLHOST:
|
||||||
executeHostFunction(instr.functionName, frame.slots, instr.argSlots);
|
executeHostFunction(instr.functionName, frame.slots, instr.argSlots);
|
||||||
break;
|
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<Value>& slots,
|
const std::vector<Value>& slots,
|
||||||
const std::vector<int>& argSlots) {
|
const std::vector<int>& argSlots) {
|
||||||
if (name == "print") {
|
if (name == "print") {
|
||||||
// print(değer) — stdout'a değeri yazdır
|
|
||||||
if (!argSlots.empty()) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bilinmeyen host fonksiyon
|
|
||||||
throw std::runtime_error("Çalışma hatası: bilinmeyen host fonksiyonu '" + name + "'");
|
throw std::runtime_error("Çalışma hatası: bilinmeyen host fonksiyonu '" + name + "'");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,20 +15,22 @@
|
||||||
#ifndef SAQUT_VM_VALUE
|
#ifndef SAQUT_VM_VALUE
|
||||||
#define SAQUT_VM_VALUE
|
#define SAQUT_VM_VALUE
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
// Gelecekte float/bool/string eklendiğinde burası genişleyecek.
|
// Gelecekte float/bool/string eklendiğinde burası genişleyecek.
|
||||||
// Şimdilik sadece int.
|
// Şimdilik sadece int.
|
||||||
enum class ValueKind {
|
enum class ValueKind {
|
||||||
Int,
|
Int,
|
||||||
|
String,
|
||||||
// Float, // TODO(vm-genişletme)
|
// Float, // TODO(vm-genişletme)
|
||||||
// Bool, // TODO(vm-genişletme)
|
// Bool, // TODO(vm-genişletme)
|
||||||
// String, // TODO(vm-genişletme)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Value {
|
struct Value {
|
||||||
ValueKind kind = ValueKind::Int;
|
ValueKind kind = ValueKind::Int;
|
||||||
int intValue = 0;
|
int intValue = 0;
|
||||||
|
std::string stringValue; // yalnızca kind == String için geçerli
|
||||||
|
|
||||||
// Kolay oluşturma
|
|
||||||
static Value fromInt(int n) {
|
static Value fromInt(int n) {
|
||||||
Value v;
|
Value v;
|
||||||
v.kind = ValueKind::Int;
|
v.kind = ValueKind::Int;
|
||||||
|
|
@ -36,8 +38,28 @@ struct Value {
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
// JIF_FALSE için: 0 = yanlış, diğer = doğru
|
static Value fromString(std::string s) {
|
||||||
bool isTruthy() const { return intValue != 0; }
|
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
|
#endif // SAQUT_VM_VALUE
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue