#45 — BAND, BOR, SHL, SHR (binary) ve BNOT (unary ~) opcode'ları eklendi. Bileşik atama &=, |=, <<=, >>= destekleniyor. Sabit katlama güncellendi. ^ (XOR) CARET çakışması nedeniyle atlandı. #38 — LOAD_GLOBAL / STORE_GLOBAL opcode'ları eklendi (Seçenek A: gerçek global slot). IRProgram.globalCount + globalNames; Interpreter.globalSlots_. Global init ifadeleri main'in başında üretiliyor. Tüm fonksiyonlar global alana erişebilir. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
2d641863d8
commit
808efc5b4a
|
|
@ -55,6 +55,13 @@ enum class Opcode {
|
|||
DIV, // UYARI: sıfıra bölme → runtime_error fırlatılır
|
||||
MOD,
|
||||
|
||||
// --- Bitsel (tümü: slots[dest] = slots[left] OP slots[right]) ---
|
||||
BAND, // slots[left] & slots[right]
|
||||
BOR, // slots[left] | slots[right]
|
||||
SHL, // slots[left] << slots[right]
|
||||
SHR, // slots[left] >> slots[right]
|
||||
BNOT, // ~slots[src] → slots[dest] (tekli operatör; src kullanır, left/right değil)
|
||||
|
||||
// --- Karşılaştırma (sonuç: 1 = doğru, 0 = yanlış) ---
|
||||
LESS, // slots[left] < slots[right]
|
||||
LESS_EQUAL, // slots[left] <= slots[right]
|
||||
|
|
@ -75,6 +82,10 @@ enum class Opcode {
|
|||
|
||||
RETURN, // Bu frame'i kapat, slots[src]'yi caller'a ilet.
|
||||
|
||||
// --- Global değişken erişimi ---
|
||||
LOAD_GLOBAL, // slots[dest] = globalSlots[intValue]
|
||||
STORE_GLOBAL, // globalSlots[intValue] = slots[src]
|
||||
|
||||
// --- Dış dünya (FFI — Foreign Function Interface) ---
|
||||
CALLHOST, // Host (C++) fonksiyonunu çağır. Şu an sadece "print" destekli.
|
||||
// Dönüş değeri yok; sadece yan etki (stdout'a yazmak gibi).
|
||||
|
|
@ -91,6 +102,13 @@ inline const char* opcodeName(Opcode op) {
|
|||
case Opcode::MUL: return "MUL";
|
||||
case Opcode::DIV: return "DIV";
|
||||
case Opcode::MOD: return "MOD";
|
||||
case Opcode::BAND: return "BAND";
|
||||
case Opcode::BOR: return "BOR";
|
||||
case Opcode::SHL: return "SHL";
|
||||
case Opcode::SHR: return "SHR";
|
||||
case Opcode::BNOT: return "BNOT";
|
||||
case Opcode::LOAD_GLOBAL: return "LOAD_GLOBAL";
|
||||
case Opcode::STORE_GLOBAL: return "STORE_GLOBAL";
|
||||
case Opcode::LESS: return "LESS";
|
||||
case Opcode::LESS_EQUAL: return "LESS_EQUAL";
|
||||
case Opcode::GREATER: return "GREATER";
|
||||
|
|
|
|||
|
|
@ -21,6 +21,10 @@ static const char* opSymbol(Opcode op) {
|
|||
case Opcode::MUL: return "*";
|
||||
case Opcode::DIV: return "/";
|
||||
case Opcode::MOD: return "%";
|
||||
case Opcode::BAND: return "&";
|
||||
case Opcode::BOR: return "|";
|
||||
case Opcode::SHL: return "<<";
|
||||
case Opcode::SHR: return ">>";
|
||||
case Opcode::LESS: return "<";
|
||||
case Opcode::LESS_EQUAL: return "<=";
|
||||
case Opcode::GREATER: return ">";
|
||||
|
|
@ -35,6 +39,7 @@ static bool isBinaryOp(Opcode op) {
|
|||
switch (op) {
|
||||
case Opcode::ADD: case Opcode::SUB: case Opcode::MUL:
|
||||
case Opcode::DIV: case Opcode::MOD:
|
||||
case Opcode::BAND: case Opcode::BOR: case Opcode::SHL: case Opcode::SHR:
|
||||
case Opcode::LESS: case Opcode::LESS_EQUAL:
|
||||
case Opcode::GREATER: case Opcode::GREATER_EQUAL:
|
||||
case Opcode::EQUAL_EQUAL: case Opcode::NOT_EQUAL:
|
||||
|
|
@ -113,6 +118,15 @@ void IRFunction::dump() const {
|
|||
}
|
||||
std::cout << ")";
|
||||
|
||||
} else if (ins.opcode == Opcode::BNOT) {
|
||||
std::cout << slot(ins.dest) << " = ~" << slot(ins.src);
|
||||
|
||||
} else if (ins.opcode == Opcode::LOAD_GLOBAL) {
|
||||
std::cout << slot(ins.dest) << " = global[" << ins.intValue << "]";
|
||||
|
||||
} else if (ins.opcode == Opcode::STORE_GLOBAL) {
|
||||
std::cout << "global[" << ins.intValue << "] = " << slot(ins.src);
|
||||
|
||||
} else if (ins.opcode == Opcode::RETURN) {
|
||||
std::cout << slot(ins.src);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,25 +17,40 @@
|
|||
IRProgram IRGenerator::generate(ASTNode* programNode, SymbolTable& /*symbolTable*/) {
|
||||
IRProgram program;
|
||||
|
||||
// ProgramNode'un her çocuğunu gez.
|
||||
// Bizi ilgilendiren: FunctionDecl. StructDecl/GlobalVar → TODO.
|
||||
// 1. Geçiş: global VariableDecl'leri topla ve kayıt et
|
||||
std::vector<VariableDeclNode*> globalVars;
|
||||
for (ASTNode* child : programNode->getChildren()) {
|
||||
if (child->kind == ASTKind::VariableDecl) {
|
||||
auto* vd = (VariableDeclNode*)child;
|
||||
nameToGlobal_[vd->name] = globalCount_++;
|
||||
program.globalCount++;
|
||||
program.globalNames.push_back(vd->name);
|
||||
globalVars.push_back(vd);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Geçiş: fonksiyonları üret
|
||||
for (ASTNode* child : programNode->getChildren()) {
|
||||
if (child->kind == ASTKind::FunctionDecl) {
|
||||
// Her fonksiyon üretimi için sıfırla
|
||||
nameToSlot_.clear();
|
||||
nextSlot_ = 0;
|
||||
|
||||
// IRFunction oluştur, currentFunction_ olarak işaretle
|
||||
auto* fnDecl = (FunctionDeclNode*)child;
|
||||
IRFunction irFn(fnDecl->name, (int)fnDecl->params.size());
|
||||
program.addFunction(std::move(irFn));
|
||||
|
||||
// addFunction std::move yaptığı için pointer'ı haritadan alalım
|
||||
currentFunction_ = program.findFunction(fnDecl->name);
|
||||
|
||||
generateFunction(child);
|
||||
// main'in başında global değişkenlerin init ifadelerini üret
|
||||
if (fnDecl->name == "main") {
|
||||
for (VariableDeclNode* gv : globalVars) {
|
||||
if (gv->initExpr) {
|
||||
int initSlot = generateExpression(gv->initExpr);
|
||||
emitStoreGlobal(initSlot, nameToGlobal_[gv->name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fonksiyon bitti — toplam slot sayısını kaydet
|
||||
generateFunction(child);
|
||||
currentFunction_->slotCount = nextSlot_;
|
||||
}
|
||||
}
|
||||
|
|
@ -344,13 +359,15 @@ int IRGenerator::generateExpression(ASTNode* node) {
|
|||
}
|
||||
|
||||
// ── Değişken ismi: n, first, second ... ──────────────────────────────
|
||||
// Bu değişkenin değeri zaten bir slotta. O slotu döndür.
|
||||
case ASTKind::Identifier: {
|
||||
auto* id = (IdentifierNode*)node;
|
||||
std::string name = id->parserToken.token ? id->parserToken.token->token : "";
|
||||
|
||||
// Önce builtin mi? (print gibi) — identifier olarak gelen builtin fonksiyon
|
||||
// çağrıları CallExpression içinde yakalanıyor, burada sadece değişken kalır
|
||||
if (isGlobal(name)) {
|
||||
int tempSlot = freshSlot();
|
||||
emitLoadGlobal(tempSlot, getGlobalIndex(name));
|
||||
return tempSlot;
|
||||
}
|
||||
return lookupVariable(name);
|
||||
}
|
||||
|
||||
|
|
@ -358,44 +375,59 @@ int IRGenerator::generateExpression(ASTNode* node) {
|
|||
case ASTKind::BinaryExpression: {
|
||||
auto* bin = (BinaryExpressionNode*)node;
|
||||
|
||||
// Atama operatörleri: x = expr, x += expr ...
|
||||
// Sol taraf bir değişken, sağ taraf hesaplanır ve o değişkene yazılır.
|
||||
// Atama operatörleri: x = expr
|
||||
if (bin->Operator == TokenType::EQUAL) {
|
||||
// Sağ tarafı hesapla
|
||||
int rhsSlot = generateExpression(bin->Right);
|
||||
|
||||
// Sol taraf değişkenin slotunu bul
|
||||
auto* lhsId = (IdentifierNode*)bin->Left;
|
||||
std::string varName = lhsId->parserToken.token->token;
|
||||
int varSlot = lookupVariable(varName);
|
||||
|
||||
// Sonucu değişkenin slotuna kopyala
|
||||
if (rhsSlot != varSlot) {
|
||||
emitLoadSlot(varSlot, rhsSlot);
|
||||
if (isGlobal(varName)) {
|
||||
emitStoreGlobal(rhsSlot, getGlobalIndex(varName));
|
||||
return rhsSlot;
|
||||
}
|
||||
|
||||
int varSlot = lookupVariable(varName);
|
||||
if (rhsSlot != varSlot) emitLoadSlot(varSlot, rhsSlot);
|
||||
return varSlot;
|
||||
}
|
||||
|
||||
// Birleşik atama: += -= *= /= %=
|
||||
// Birleşik atama: += -= *= /= %= &= |= <<= >>=
|
||||
// x OP= y ≡ x = x OP y
|
||||
if (bin->Operator == TokenType::PLUS_EQUAL ||
|
||||
bin->Operator == TokenType::MINUS_EQUAL ||
|
||||
bin->Operator == TokenType::STAR_EQUAL ||
|
||||
bin->Operator == TokenType::SLASH_EQUAL ||
|
||||
bin->Operator == TokenType::PERCENT_EQUAL) {
|
||||
bin->Operator == TokenType::PERCENT_EQUAL ||
|
||||
bin->Operator == TokenType::AMPERSAND_EQUAL ||
|
||||
bin->Operator == TokenType::PIPE_EQUAL ||
|
||||
bin->Operator == TokenType::LSHIFT_EQUAL ||
|
||||
bin->Operator == TokenType::RSHIFT_EQUAL) {
|
||||
|
||||
auto* lhsId = (IdentifierNode*)bin->Left;
|
||||
std::string varName = lhsId->parserToken.token->token;
|
||||
int varSlot = lookupVariable(varName);
|
||||
int rhsSlot = generateExpression(bin->Right);
|
||||
|
||||
Opcode arithOp = Opcode::ADD;
|
||||
if (bin->Operator == TokenType::MINUS_EQUAL) arithOp = Opcode::SUB;
|
||||
else if (bin->Operator == TokenType::STAR_EQUAL) arithOp = Opcode::MUL;
|
||||
else if (bin->Operator == TokenType::SLASH_EQUAL) arithOp = Opcode::DIV;
|
||||
else if (bin->Operator == TokenType::PERCENT_EQUAL) arithOp = Opcode::MOD;
|
||||
if (bin->Operator == TokenType::MINUS_EQUAL) arithOp = Opcode::SUB;
|
||||
else if (bin->Operator == TokenType::STAR_EQUAL) arithOp = Opcode::MUL;
|
||||
else if (bin->Operator == TokenType::SLASH_EQUAL) arithOp = Opcode::DIV;
|
||||
else if (bin->Operator == TokenType::PERCENT_EQUAL) arithOp = Opcode::MOD;
|
||||
else if (bin->Operator == TokenType::AMPERSAND_EQUAL) arithOp = Opcode::BAND;
|
||||
else if (bin->Operator == TokenType::PIPE_EQUAL) arithOp = Opcode::BOR;
|
||||
else if (bin->Operator == TokenType::LSHIFT_EQUAL) arithOp = Opcode::SHL;
|
||||
else if (bin->Operator == TokenType::RSHIFT_EQUAL) arithOp = Opcode::SHR;
|
||||
|
||||
int resultSlot = freshSlot();
|
||||
|
||||
if (isGlobal(varName)) {
|
||||
int currentSlot = freshSlot();
|
||||
emitLoadGlobal(currentSlot, getGlobalIndex(varName));
|
||||
emitBinaryOp(arithOp, resultSlot, currentSlot, rhsSlot);
|
||||
emitStoreGlobal(resultSlot, getGlobalIndex(varName));
|
||||
return resultSlot;
|
||||
}
|
||||
|
||||
int varSlot = lookupVariable(varName);
|
||||
emitBinaryOp(arithOp, resultSlot, varSlot, rhsSlot);
|
||||
emitLoadSlot(varSlot, resultSlot);
|
||||
return varSlot;
|
||||
|
|
@ -412,12 +444,17 @@ int IRGenerator::generateExpression(ASTNode* node) {
|
|||
emitLoadConst(zeroSlot, 0);
|
||||
emitBinaryOp(Opcode::SUB, resultSlot, zeroSlot, operandSlot);
|
||||
} else if (bin->Operator == TokenType::BANG) {
|
||||
// !x → (x == 0): sıfırsa 1, değilse 0 — her zaman 0 ya da 1
|
||||
// !x → (x == 0): sıfırsa 1, değilse 0
|
||||
int zeroSlot = freshSlot();
|
||||
emitLoadConst(zeroSlot, 0);
|
||||
emitBinaryOp(Opcode::EQUAL_EQUAL, resultSlot, operandSlot, zeroSlot);
|
||||
} else if (bin->Operator == TokenType::TILDE) {
|
||||
// ~x — bitsel değil
|
||||
Instruction ins(Opcode::BNOT);
|
||||
ins.dest = resultSlot;
|
||||
ins.src = operandSlot;
|
||||
currentFunction_->instructions.push_back(std::move(ins));
|
||||
} else {
|
||||
// Diğer unary operatörler (ör. ~) → TODO
|
||||
emitLoadSlot(resultSlot, operandSlot);
|
||||
}
|
||||
return resultSlot;
|
||||
|
|
@ -438,6 +475,12 @@ int IRGenerator::generateExpression(ASTNode* node) {
|
|||
case TokenType::EQUAL_EQUAL: return generateBinaryArithmetic(Opcode::EQUAL_EQUAL, bin->Left, bin->Right);
|
||||
case TokenType::BANG_EQUAL: return generateBinaryArithmetic(Opcode::NOT_EQUAL, bin->Left, bin->Right);
|
||||
|
||||
// Bitsel operatörler
|
||||
case TokenType::AMPERSAND: return generateBinaryArithmetic(Opcode::BAND, bin->Left, bin->Right);
|
||||
case TokenType::PIPE: return generateBinaryArithmetic(Opcode::BOR, bin->Left, bin->Right);
|
||||
case TokenType::LSHIFT: return generateBinaryArithmetic(Opcode::SHL, bin->Left, bin->Right);
|
||||
case TokenType::RSHIFT: return generateBinaryArithmetic(Opcode::SHR, bin->Left, bin->Right);
|
||||
|
||||
// Mantıksal operatörler: kısa devre dallanmasıyla üretilir (ADR-008).
|
||||
// NOT: sıradan ikili işlem değil — b, a'nın değerine göre atlanabilir.
|
||||
case TokenType::AMPERSAND_AMPERSAND: {
|
||||
|
|
@ -593,6 +636,29 @@ void IRGenerator::emitLoadSlot(int destSlot, int srcSlot) {
|
|||
currentFunction_->instructions.push_back(std::move(ins));
|
||||
}
|
||||
|
||||
void IRGenerator::emitLoadGlobal(int destSlot, int globalIndex) {
|
||||
Instruction ins(Opcode::LOAD_GLOBAL);
|
||||
ins.dest = destSlot;
|
||||
ins.intValue = globalIndex;
|
||||
currentFunction_->instructions.push_back(std::move(ins));
|
||||
}
|
||||
|
||||
void IRGenerator::emitStoreGlobal(int srcSlot, int globalIndex) {
|
||||
Instruction ins(Opcode::STORE_GLOBAL);
|
||||
ins.src = srcSlot;
|
||||
ins.intValue = globalIndex;
|
||||
currentFunction_->instructions.push_back(std::move(ins));
|
||||
}
|
||||
|
||||
bool IRGenerator::isGlobal(const std::string& name) const {
|
||||
return nameToGlobal_.count(name) > 0;
|
||||
}
|
||||
|
||||
int IRGenerator::getGlobalIndex(const std::string& name) const {
|
||||
auto it = nameToGlobal_.find(name);
|
||||
return (it != nameToGlobal_.end()) ? it->second : -1;
|
||||
}
|
||||
|
||||
void IRGenerator::emitBinaryOp(Opcode op, int destSlot, int leftSlot, int rightSlot) {
|
||||
Instruction ins(op);
|
||||
ins.dest = destSlot;
|
||||
|
|
|
|||
|
|
@ -56,6 +56,8 @@ private:
|
|||
|
||||
void emitLoadConst(int destSlot, int value);
|
||||
void emitLoadSlot(int destSlot, int srcSlot);
|
||||
void emitLoadGlobal(int destSlot, int globalIndex);
|
||||
void emitStoreGlobal(int srcSlot, int globalIndex);
|
||||
void emitBinaryOp(Opcode op, int destSlot, int leftSlot, int rightSlot);
|
||||
void emitReturn(int srcSlot);
|
||||
// Koşulsuz atlama yazar; instruction indeksini döndürür (backpatch için).
|
||||
|
|
@ -88,9 +90,15 @@ private:
|
|||
IRFunction* currentFunction_ = nullptr; // şu an üretilen fonksiyon
|
||||
int nextSlot_ = 0; // sıradaki boş slot numarası
|
||||
|
||||
// Değişken ismi → slot numarası.
|
||||
// Sınırlama: aynı isimdeki farklı scope değişkenleri çakışır (TODO).
|
||||
// Değişken ismi → slot numarası (lokal).
|
||||
std::unordered_map<std::string, int> nameToSlot_;
|
||||
|
||||
// Global değişken ismi → global index
|
||||
std::unordered_map<std::string, int> nameToGlobal_;
|
||||
int globalCount_ = 0;
|
||||
|
||||
bool isGlobal(const std::string& name) const;
|
||||
int getGlobalIndex(const std::string& name) const;
|
||||
};
|
||||
|
||||
#endif // SAQUT_IR_GENERATOR
|
||||
|
|
|
|||
|
|
@ -3,6 +3,14 @@
|
|||
|
||||
void IRProgram::dump() const {
|
||||
std::cout << "IR DUMP\n\n";
|
||||
|
||||
if (globalCount > 0) {
|
||||
std::cout << "GLOBALS (" << globalCount << ")\n";
|
||||
for (int i = 0; i < (int)globalNames.size(); i++)
|
||||
std::cout << " global[" << i << "] = " << globalNames[i] << "\n";
|
||||
std::cout << "\n";
|
||||
}
|
||||
|
||||
for (const auto& name : functionOrder) {
|
||||
auto it = functions.find(name);
|
||||
if (it != functions.end()) it->second.dump();
|
||||
|
|
|
|||
|
|
@ -29,6 +29,10 @@ struct IRProgram {
|
|||
// Ekleme sırası (dump'ta orijinal sırayla göstermek için)
|
||||
std::vector<std::string> functionOrder;
|
||||
|
||||
// Global değişkenler (LOAD_GLOBAL / STORE_GLOBAL için)
|
||||
int globalCount = 0;
|
||||
std::vector<std::string> globalNames; // index → isim (dump için)
|
||||
|
||||
// Yeni fonksiyon ekle
|
||||
void addFunction(IRFunction fn) {
|
||||
functionOrder.push_back(fn.name);
|
||||
|
|
|
|||
|
|
@ -104,6 +104,10 @@ private:
|
|||
case TokenType::GREATER:
|
||||
case TokenType::LESS_EQUAL:
|
||||
case TokenType::GREATER_EQUAL:
|
||||
case TokenType::AMPERSAND:
|
||||
case TokenType::PIPE:
|
||||
case TokenType::LSHIFT:
|
||||
case TokenType::RSHIFT:
|
||||
case TokenType::AMPERSAND_AMPERSAND:
|
||||
case TokenType::PIPE_PIPE:
|
||||
return true;
|
||||
|
|
@ -125,6 +129,10 @@ private:
|
|||
case TokenType::GREATER: return l > r ? 1 : 0;
|
||||
case TokenType::LESS_EQUAL: return l <= r ? 1 : 0;
|
||||
case TokenType::GREATER_EQUAL: return l >= r ? 1 : 0;
|
||||
case TokenType::AMPERSAND: return l & r;
|
||||
case TokenType::PIPE: return l | r;
|
||||
case TokenType::LSHIFT: return l << r;
|
||||
case TokenType::RSHIFT: return l >> r;
|
||||
case TokenType::AMPERSAND_AMPERSAND: return (l && r) ? 1 : 0;
|
||||
case TokenType::PIPE_PIPE: return (l || r) ? 1 : 0;
|
||||
default: return 0;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@
|
|||
#include <stdexcept>
|
||||
|
||||
int Interpreter::run() {
|
||||
// Global slot'ları sıfırla
|
||||
globalSlots_.assign(program_.globalCount, Value::fromInt(0));
|
||||
|
||||
IRFunction* mainFunction = program_.findFunction("main");
|
||||
if (!mainFunction)
|
||||
throw std::runtime_error("Çalışma hatası: 'main' fonksiyonu bulunamadı");
|
||||
|
|
@ -70,6 +73,35 @@ int Interpreter::run() {
|
|||
break;
|
||||
}
|
||||
|
||||
// ── Bitsel ────────────────────────────────────────────────────────
|
||||
case Opcode::BAND:
|
||||
frame.slots[instr.dest] = Value::fromInt(
|
||||
frame.slots[instr.left].intValue & frame.slots[instr.right].intValue);
|
||||
break;
|
||||
case Opcode::BOR:
|
||||
frame.slots[instr.dest] = Value::fromInt(
|
||||
frame.slots[instr.left].intValue | frame.slots[instr.right].intValue);
|
||||
break;
|
||||
case Opcode::SHL:
|
||||
frame.slots[instr.dest] = Value::fromInt(
|
||||
frame.slots[instr.left].intValue << frame.slots[instr.right].intValue);
|
||||
break;
|
||||
case Opcode::SHR:
|
||||
frame.slots[instr.dest] = Value::fromInt(
|
||||
frame.slots[instr.left].intValue >> frame.slots[instr.right].intValue);
|
||||
break;
|
||||
case Opcode::BNOT:
|
||||
frame.slots[instr.dest] = Value::fromInt(~frame.slots[instr.src].intValue);
|
||||
break;
|
||||
|
||||
// ── Global değişken erişimi ────────────────────────────────────────
|
||||
case Opcode::LOAD_GLOBAL:
|
||||
frame.slots[instr.dest] = globalSlots_[instr.intValue];
|
||||
break;
|
||||
case Opcode::STORE_GLOBAL:
|
||||
globalSlots_[instr.intValue] = frame.slots[instr.src];
|
||||
break;
|
||||
|
||||
// ── Karşılaştırma ─────────────────────────────────────────────────
|
||||
case Opcode::LESS:
|
||||
frame.slots[instr.dest] = Value::fromInt(
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ public:
|
|||
private:
|
||||
IRProgram& program_;
|
||||
std::vector<CallFrame> callStack_;
|
||||
std::vector<Value> globalSlots_;
|
||||
|
||||
// Host (C++) fonksiyon çağrısı — şu an sadece "print" destekli
|
||||
void executeHostFunction(const std::string& name,
|
||||
|
|
|
|||
Loading…
Reference in New Issue