feat(ir,vm): #38 global değişken IR + #45 bitsel operatörler (& | << >> ~ !)

- #38: Üst düzey VariableDecl'lar main'in başına inject ediliyor (Seçenek B)
- #45: BIT_AND / BIT_OR / BIT_SHL / BIT_SHR / BIT_NOT / NOT_UNARY opcode'ları
- IR üretici: AMPERSAND, PIPE, LSHIFT, RSHIFT → binary bitsel
- IR üretici: TILDE (unary ~) → BIT_NOT, BANG (unary !) → NOT_UNARY
- VM: tüm yeni opcode'lar için handler eklendi

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
saqut 2026-06-19 23:36:58 +03:00
parent d51e48dbb2
commit 04465afaef
4 changed files with 90 additions and 10 deletions

25
examples/test_bitwise.sqt Normal file
View File

@ -0,0 +1,25 @@
int global_x = 10;
int global_y = 3;
int main() {
// global değişken testi (#38)
print(global_x);
print(global_y);
// bitsel AND, OR, SHL, SHR (#45)
int a = 12;
int b = 10;
print(a & b);
print(a | b);
print(1 << 3);
print(16 >> 2);
// unary ~ ve !
int c = 0;
print(!c);
int d = 5;
print(!d);
print(~0);
return 0;
}

View File

@ -73,6 +73,14 @@ enum class Opcode {
RETURN, // Bu frame'i kapat, slots[src]'yi caller'a ilet. RETURN, // Bu frame'i kapat, slots[src]'yi caller'a ilet.
// --- Bitsel (tümü: slots[dest] = slots[left] OP slots[right]) ---
BIT_AND, // a & b
BIT_OR, // a | b
BIT_SHL, // a << b
BIT_SHR, // a >> b
BIT_NOT, // ~a (unary: slots[dest] = ~slots[src])
NOT_UNARY, // !a (unary: slots[dest] = slots[src] == 0 ? 1 : 0)
// --- Dış dünya (FFI — Foreign Function Interface) --- // --- Dış dünya (FFI — Foreign Function Interface) ---
CALLHOST, // Host (C++) fonksiyonunu çağır. Şu an sadece "print" destekli. CALLHOST, // Host (C++) fonksiyonunu çağır. Şu an sadece "print" destekli.
// Dönüş değeri yok; sadece yan etki (stdout'a yazmak gibi). // Dönüş değeri yok; sadece yan etki (stdout'a yazmak gibi).
@ -99,6 +107,12 @@ inline const char* opcodeName(Opcode op) {
case Opcode::JIF_FALSE: return "JIF_FALSE"; case Opcode::JIF_FALSE: return "JIF_FALSE";
case Opcode::CALL: return "CALL"; case Opcode::CALL: return "CALL";
case Opcode::RETURN: return "RETURN"; case Opcode::RETURN: return "RETURN";
case Opcode::BIT_AND: return "BIT_AND";
case Opcode::BIT_OR: return "BIT_OR";
case Opcode::BIT_SHL: return "BIT_SHL";
case Opcode::BIT_SHR: return "BIT_SHR";
case Opcode::BIT_NOT: return "BIT_NOT";
case Opcode::NOT_UNARY: return "NOT_UNARY";
case Opcode::CALLHOST: return "CALLHOST"; case Opcode::CALLHOST: return "CALLHOST";
} }
return "UNKNOWN"; return "UNKNOWN";

View File

@ -17,25 +17,30 @@
IRProgram IRGenerator::generate(ASTNode* programNode, SymbolTable& /*symbolTable*/) { IRProgram IRGenerator::generate(ASTNode* programNode, SymbolTable& /*symbolTable*/) {
IRProgram program; IRProgram program;
// ProgramNode'un her çocuğunu gez. // Üst düzey global değişken bildirimlerini topla (#38)
// Bizi ilgilendiren: FunctionDecl. StructDecl/GlobalVar → TODO. std::vector<ASTNode*> globalVars;
for (ASTNode* child : programNode->getChildren()) {
if (child->kind == ASTKind::VariableDecl)
globalVars.push_back(child);
}
for (ASTNode* child : programNode->getChildren()) { for (ASTNode* child : programNode->getChildren()) {
if (child->kind == ASTKind::FunctionDecl) { if (child->kind == ASTKind::FunctionDecl) {
// Her fonksiyon üretimi için sıfırla
nameToSlot_.clear(); nameToSlot_.clear();
nextSlot_ = 0; nextSlot_ = 0;
// IRFunction oluştur, currentFunction_ olarak işaretle
auto* fnDecl = (FunctionDeclNode*)child; auto* fnDecl = (FunctionDeclNode*)child;
IRFunction irFn(fnDecl->name, (int)fnDecl->params.size()); IRFunction irFn(fnDecl->name, (int)fnDecl->params.size());
program.addFunction(std::move(irFn)); program.addFunction(std::move(irFn));
// addFunction std::move yaptığı için pointer'ı haritadan alalım
currentFunction_ = program.findFunction(fnDecl->name); currentFunction_ = program.findFunction(fnDecl->name);
generateFunction(child); // Global değişkenleri main'in başına ekle (Seçenek B)
if (fnDecl->name == "main") {
for (ASTNode* gv : globalVars)
generateStatement(gv);
}
// Fonksiyon bitti — toplam slot sayısını kaydet generateFunction(child);
currentFunction_->slotCount = nextSlot_; currentFunction_->slotCount = nextSlot_;
} }
} }
@ -374,12 +379,18 @@ int IRGenerator::generateExpression(ASTNode* node) {
int resultSlot = freshSlot(); int resultSlot = freshSlot();
if (bin->Operator == TokenType::MINUS) { if (bin->Operator == TokenType::MINUS) {
// -x → 0 - x
int zeroSlot = freshSlot(); int zeroSlot = freshSlot();
emitLoadConst(zeroSlot, 0); emitLoadConst(zeroSlot, 0);
emitBinaryOp(Opcode::SUB, resultSlot, zeroSlot, operandSlot); emitBinaryOp(Opcode::SUB, resultSlot, zeroSlot, operandSlot);
} else if (bin->Operator == TokenType::BANG) {
Instruction ins(Opcode::NOT_UNARY);
ins.dest = resultSlot; ins.src = operandSlot;
currentFunction_->instructions.push_back(std::move(ins));
} else if (bin->Operator == TokenType::TILDE) {
Instruction ins(Opcode::BIT_NOT);
ins.dest = resultSlot; ins.src = operandSlot;
currentFunction_->instructions.push_back(std::move(ins));
} else { } else {
// Diğer unary operatörler → TODO
emitLoadSlot(resultSlot, operandSlot); emitLoadSlot(resultSlot, operandSlot);
} }
return resultSlot; return resultSlot;
@ -399,6 +410,11 @@ int IRGenerator::generateExpression(ASTNode* node) {
case TokenType::GREATER_EQUAL: return generateBinaryArithmetic(Opcode::GREATER_EQUAL, bin->Left, bin->Right); case TokenType::GREATER_EQUAL: return generateBinaryArithmetic(Opcode::GREATER_EQUAL, bin->Left, bin->Right);
case TokenType::EQUAL_EQUAL: return generateBinaryArithmetic(Opcode::EQUAL_EQUAL, bin->Left, bin->Right); 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); case TokenType::BANG_EQUAL: return generateBinaryArithmetic(Opcode::NOT_EQUAL, bin->Left, bin->Right);
// Bitsel
case TokenType::AMPERSAND: return generateBinaryArithmetic(Opcode::BIT_AND, bin->Left, bin->Right);
case TokenType::PIPE: return generateBinaryArithmetic(Opcode::BIT_OR, bin->Left, bin->Right);
case TokenType::LSHIFT: return generateBinaryArithmetic(Opcode::BIT_SHL, bin->Left, bin->Right);
case TokenType::RSHIFT: return generateBinaryArithmetic(Opcode::BIT_SHR, bin->Left, bin->Right);
default: { default: {
// Bilinmeyen operatör — boş slot döndür // Bilinmeyen operatör — boş slot döndür
int slot = freshSlot(); int slot = freshSlot();

View File

@ -148,6 +148,31 @@ int Interpreter::run() {
continue; continue;
} }
// ── Bitsel ────────────────────────────────────────────────────────
case Opcode::BIT_AND:
frame.slots[instr.dest] = Value::fromInt(
frame.slots[instr.left].intValue & frame.slots[instr.right].intValue);
break;
case Opcode::BIT_OR:
frame.slots[instr.dest] = Value::fromInt(
frame.slots[instr.left].intValue | frame.slots[instr.right].intValue);
break;
case Opcode::BIT_SHL:
frame.slots[instr.dest] = Value::fromInt(
frame.slots[instr.left].intValue << frame.slots[instr.right].intValue);
break;
case Opcode::BIT_SHR:
frame.slots[instr.dest] = Value::fromInt(
frame.slots[instr.left].intValue >> frame.slots[instr.right].intValue);
break;
case Opcode::BIT_NOT:
frame.slots[instr.dest] = Value::fromInt(~frame.slots[instr.src].intValue);
break;
case Opcode::NOT_UNARY:
frame.slots[instr.dest] = Value::fromInt(
frame.slots[instr.src].intValue == 0 ? 1 : 0);
break;
// ── FFI ─────────────────────────────────────────────────────────── // ── FFI ───────────────────────────────────────────────────────────
case Opcode::CALLHOST: case Opcode::CALLHOST:
executeHostFunction(instr.functionName, frame.slots, instr.argSlots); executeHostFunction(instr.functionName, frame.slots, instr.argSlots);