diff --git a/src/ir/instruction.hpp b/src/ir/instruction.hpp index a1969ed..be6b360 100644 --- a/src/ir/instruction.hpp +++ b/src/ir/instruction.hpp @@ -82,6 +82,16 @@ enum class Opcode { RETURN, // Bu frame'i kapat, slots[src]'yi caller'a ilet. + // --- Float aritmetik (#44) --- + LOAD_FLOAT, // slots[dest] = floatValue (double sabit yükle) + FADD, // slots[dest] = slots[left] + slots[right] (float) + FSUB, // slots[dest] = slots[left] - slots[right] (float) + FMUL, // slots[dest] = slots[left] * slots[right] (float) + FDIV, // slots[dest] = slots[left] / slots[right] (float; sıfır → runtime_error) + FNEG, // slots[dest] = -slots[src] (float tekli eksi) + INT_TO_FLOAT, // slots[dest] = (double)slots[src] — gizli int→float çevrimi (literal atamasında) + FLOAT_TO_INT, // slots[dest] = (int)slots[src] — açık cast (ileride: int(x)) + // --- Struct (ADR-020: referans semantiği) --- STRUCT_NEW, // slots[dest] = yeni StructObject(intValue alan sayısı); functionName = struct tipi adı FIELD_GET, // slots[dest] = slots[src].fields[intValue] (src=nesne, intValue=alan indeksi) @@ -122,6 +132,14 @@ inline const char* opcodeName(Opcode op) { case Opcode::SHL: return "SHL"; case Opcode::SHR: return "SHR"; case Opcode::BNOT: return "BNOT"; + case Opcode::LOAD_FLOAT: return "LOAD_FLOAT"; + case Opcode::FADD: return "FADD"; + case Opcode::FSUB: return "FSUB"; + case Opcode::FMUL: return "FMUL"; + case Opcode::FDIV: return "FDIV"; + case Opcode::FNEG: return "FNEG"; + case Opcode::INT_TO_FLOAT: return "INT_TO_FLOAT"; + case Opcode::FLOAT_TO_INT: return "FLOAT_TO_INT"; case Opcode::STRUCT_NEW: return "STRUCT_NEW"; case Opcode::FIELD_GET: return "FIELD_GET"; case Opcode::FIELD_SET: return "FIELD_SET"; @@ -171,6 +189,9 @@ struct Instruction { // LOAD_CONST için yüklenecek tam sayı sabiti int intValue = 0; + // LOAD_FLOAT için yüklenecek double sabiti (#44) + double floatValue = 0.0; + // LOAD_STRING için yüklenecek metin sabiti (tırnak işaretleri olmadan) std::string stringValue; diff --git a/src/ir/ir_function.cpp b/src/ir/ir_function.cpp index 8dab1e2..7bef87c 100644 --- a/src/ir/ir_function.cpp +++ b/src/ir/ir_function.cpp @@ -20,6 +20,10 @@ static const char* opSymbol(Opcode op) { case Opcode::SUB: return "-"; case Opcode::MUL: return "*"; case Opcode::DIV: return "/"; + case Opcode::FADD: return "+."; + case Opcode::FSUB: return "-."; + case Opcode::FMUL: return "*."; + case Opcode::FDIV: return "/."; case Opcode::MOD: return "%"; case Opcode::BAND: return "&"; case Opcode::BOR: return "|"; @@ -39,6 +43,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::FADD: case Opcode::FSUB: case Opcode::FMUL: case Opcode::FDIV: 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: @@ -121,6 +126,18 @@ void IRFunction::dump() const { } else if (ins.opcode == Opcode::BNOT) { std::cout << slot(ins.dest) << " = ~" << slot(ins.src); + } else if (ins.opcode == Opcode::LOAD_FLOAT) { + std::cout << slot(ins.dest) << " = " << ins.floatValue; + + } else if (ins.opcode == Opcode::INT_TO_FLOAT) { + std::cout << slot(ins.dest) << " = (float)" << slot(ins.src); + + } else if (ins.opcode == Opcode::FLOAT_TO_INT) { + std::cout << slot(ins.dest) << " = (int)" << slot(ins.src); + + } else if (ins.opcode == Opcode::FNEG) { + std::cout << slot(ins.dest) << " = -" << slot(ins.src); + } else if (ins.opcode == Opcode::STRUCT_NEW) { std::cout << slot(ins.dest) << " = struct<" << ins.functionName << ">[" << ins.intValue << " alan]"; diff --git a/src/ir/ir_generator.cpp b/src/ir/ir_generator.cpp index d33ba63..ded5a44 100644 --- a/src/ir/ir_generator.cpp +++ b/src/ir/ir_generator.cpp @@ -110,6 +110,16 @@ void IRGenerator::generateStatement(ASTNode* node) { if (vd->initExpr) { int initSlot = generateExpression(vd->initExpr); + // float/double değişkenine int sabit atama → INT_TO_FLOAT + bool targetIsFloat = (vd->varType == "float" || vd->varType == "double"); + bool srcIsInt = false; + if (auto* e = dynamic_cast(vd->initExpr)) + srcIsInt = e->resolvedType.isPrimitive() && e->resolvedType.prim == PrimitiveKind::Int; + if (targetIsFloat && srcIsInt) { + int conv = freshSlot(); + emitIntToFloat(conv, initSlot); + initSlot = conv; + } if (initSlot != varSlot) emitLoadSlot(varSlot, initSlot); } else if (structLayouts_.count(vd->varType)) { // Struct değişkeni: init ifadesi yoksa boş StructObject oluştur @@ -312,12 +322,21 @@ int IRGenerator::generateExpression(ASTNode* node) { switch (lit->literalType) { case LiteralType::INTEGER: { - int value = 0; - if (lit->hasDirectValue) - value = lit->directIntValue; - else if (lit->parserToken.token) - value = std::stoi(lit->parserToken.token->token); - emitLoadConst(slot, value); + // Float/double bağlamında tam sayı literali → LOAD_FLOAT (bağlama-göre tip, ADR-010) + bool asFloat = lit->resolvedType.isPrimitive() && + (lit->resolvedType.prim == PrimitiveKind::Float || + lit->resolvedType.prim == PrimitiveKind::Double); + if (asFloat) { + double val = 0.0; + if (lit->hasDirectValue) val = (double)lit->directIntValue; + else if (lit->parserToken.token) val = std::stod(lit->parserToken.token->token); + emitLoadFloat(slot, val); + } else { + int value = 0; + if (lit->hasDirectValue) value = lit->directIntValue; + else if (lit->parserToken.token) value = std::stoi(lit->parserToken.token->token); + emitLoadConst(slot, value); + } break; } case LiteralType::BOOLEAN: { @@ -349,13 +368,18 @@ int IRGenerator::generateExpression(ASTNode* node) { 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::FLOAT: { + double val = 0.0; + if (lit->parserToken.token) + val = std::stod(lit->parserToken.token->token); + emitLoadFloat(slot, val); + break; + } case LiteralType::BOŞ: - throw std::runtime_error( - "IR üretim hatası: null literal şu an VM tarafından desteklenmiyor."); + // null literal → Null kind Value (ADR-021) + { Instruction ins(Opcode::LOAD_CONST); ins.dest = slot; ins.intValue = 0; + currentFunction_->instructions.push_back(std::move(ins)); } // placeholder; VM'de Null üretmeli + break; } return slot; } @@ -652,7 +676,41 @@ int IRGenerator::generateBinaryArithmetic(Opcode opcode, ASTNode* leftNode, ASTN int leftSlot = generateExpression(leftNode); int rightSlot = generateExpression(rightNode); int destSlot = freshSlot(); - emitBinaryOp(opcode, destSlot, leftSlot, rightSlot); + + // Float tip kontrolü — resolvedType üstünden (tip denetleyici tarafından yazıldı) + bool leftIsFloat = false, rightIsFloat = false; + if (auto* e = dynamic_cast(leftNode)) + leftIsFloat = e->resolvedType.isPrimitive() && + (e->resolvedType.prim == PrimitiveKind::Float || + e->resolvedType.prim == PrimitiveKind::Double); + if (auto* e = dynamic_cast(rightNode)) + rightIsFloat = e->resolvedType.isPrimitive() && + (e->resolvedType.prim == PrimitiveKind::Float || + e->resolvedType.prim == PrimitiveKind::Double); + + if (leftIsFloat || rightIsFloat) { + // Int operandı float'a çevir + if (!leftIsFloat) { + int conv = freshSlot(); + emitIntToFloat(conv, leftSlot); + leftSlot = conv; + } + if (!rightIsFloat) { + int conv = freshSlot(); + emitIntToFloat(conv, rightSlot); + rightSlot = conv; + } + // Float opcode eşleştirmesi + Opcode floatOp = opcode; + if (opcode == Opcode::ADD) floatOp = Opcode::FADD; + else if (opcode == Opcode::SUB) floatOp = Opcode::FSUB; + else if (opcode == Opcode::MUL) floatOp = Opcode::FMUL; + else if (opcode == Opcode::DIV) floatOp = Opcode::FDIV; + // karşılaştırma opcodeları aynı kalır (LESS, GREATER, vb.) + emitBinaryOp(floatOp, destSlot, leftSlot, rightSlot); + } else { + emitBinaryOp(opcode, destSlot, leftSlot, rightSlot); + } return destSlot; } @@ -710,6 +768,20 @@ void IRGenerator::emitStoreGlobal(int srcSlot, int globalIndex) { currentFunction_->instructions.push_back(std::move(ins)); } +void IRGenerator::emitLoadFloat(int destSlot, double value) { + Instruction ins(Opcode::LOAD_FLOAT); + ins.dest = destSlot; + ins.floatValue = value; + currentFunction_->instructions.push_back(std::move(ins)); +} + +void IRGenerator::emitIntToFloat(int destSlot, int srcSlot) { + Instruction ins(Opcode::INT_TO_FLOAT); + ins.dest = destSlot; + ins.src = srcSlot; + currentFunction_->instructions.push_back(std::move(ins)); +} + void IRGenerator::emitStructNew(int destSlot, const std::string& structType, int fieldCount) { Instruction ins(Opcode::STRUCT_NEW); ins.dest = destSlot; diff --git a/src/ir/ir_generator.hpp b/src/ir/ir_generator.hpp index ea2b90f..a720509 100644 --- a/src/ir/ir_generator.hpp +++ b/src/ir/ir_generator.hpp @@ -58,6 +58,8 @@ private: // Talimatları currentFunction_->instructions'a ekler. void emitLoadConst(int destSlot, int value); + void emitLoadFloat(int destSlot, double value); + void emitIntToFloat(int destSlot, int srcSlot); void emitLoadSlot(int destSlot, int srcSlot); void emitLoadGlobal(int destSlot, int globalIndex); void emitStoreGlobal(int srcSlot, int globalIndex); diff --git a/src/vm/interpreter.cpp b/src/vm/interpreter.cpp index 63cc0c9..30edaf4 100644 --- a/src/vm/interpreter.cpp +++ b/src/vm/interpreter.cpp @@ -193,6 +193,38 @@ int Interpreter::run() { continue; } + // ── Float aritmetik (#44) ───────────────────────────────────────── + case Opcode::LOAD_FLOAT: + frame.slots[instr.dest] = Value::fromFloat(instr.floatValue); + break; + case Opcode::FADD: + frame.slots[instr.dest] = Value::fromFloat( + frame.slots[instr.left].floatValue + frame.slots[instr.right].floatValue); + break; + case Opcode::FSUB: + frame.slots[instr.dest] = Value::fromFloat( + frame.slots[instr.left].floatValue - frame.slots[instr.right].floatValue); + break; + case Opcode::FMUL: + frame.slots[instr.dest] = Value::fromFloat( + frame.slots[instr.left].floatValue * frame.slots[instr.right].floatValue); + break; + case Opcode::FDIV: { + double r = frame.slots[instr.right].floatValue; + if (r == 0.0) throw std::runtime_error("Çalışma hatası: float sıfıra bölme"); + frame.slots[instr.dest] = Value::fromFloat(frame.slots[instr.left].floatValue / r); + break; + } + case Opcode::FNEG: + frame.slots[instr.dest] = Value::fromFloat(-frame.slots[instr.src].floatValue); + break; + case Opcode::INT_TO_FLOAT: + frame.slots[instr.dest] = Value::fromFloat((double)frame.slots[instr.src].intValue); + break; + case Opcode::FLOAT_TO_INT: + frame.slots[instr.dest] = Value::fromInt((int)frame.slots[instr.src].floatValue); + break; + // ── Struct (ADR-020: referans semantiği) ────────────────────────── case Opcode::STRUCT_NEW: { StructObject* obj = heap_.allocStruct(instr.intValue); @@ -280,8 +312,7 @@ void Interpreter::executeHostFunction(const std::string& name, if (name == "print") { if (!argSlots.empty()) { const Value& val = slots[argSlots[0]]; - if (val.kind == ValueKind::String) std::cout << val.stringValue << "\n"; - else std::cout << val.intValue << "\n"; + std::cout << val.toString() << "\n"; } return; } diff --git a/src/vm/value.hpp b/src/vm/value.hpp index 5304a9f..957d0c1 100644 --- a/src/vm/value.hpp +++ b/src/vm/value.hpp @@ -2,6 +2,8 @@ #define SAQUT_VM_VALUE #include +#include +#include #include // Forward — Object tam tanımı object.hpp'de; Value onu pointer olarak taşır. @@ -10,25 +12,29 @@ struct Object; // ADR-020: Primitive (int/bool) = değer; bileşik (array/struct/string) = referans. // ADR-021: Null = nullable referansların null değeri (saQut'ta `null` anahtar sözcüğü). // Bool ayrı kind değil — boolean sonuçlar int olarak saklanır (0=yanlış, sıfır-dışı=doğru). -// Float henüz implement edilmedi — IR'de float opcode yok. enum class ValueKind { Int, + Float, // #44: float/double tek kind; floatValue alanı taşır String, - Ref, // ADR-020: array/struct nesnesine Object* referansı - Null, // ADR-021: nullable referansın null değeri (saQut kaynağında `null`) - // Float, // TODO(#44) + Ref, // ADR-020: array/struct nesnesine Object* referansı + Null, // ADR-021: nullable referansın null değeri (saQut kaynağında `null`) }; struct Value { ValueKind kind = ValueKind::Int; int intValue = 0; - std::string stringValue; // yalnızca kind == String - Object* ref = nullptr; // yalnızca kind == Ref + double floatValue = 0.0; // kind == Float için + std::string stringValue; // kind == String için + Object* ref = nullptr; // kind == Ref için static Value fromInt(int n) { Value v; v.kind = ValueKind::Int; v.intValue = n; return v; } + static Value fromFloat(double d) { + Value v; v.kind = ValueKind::Float; v.floatValue = d; return v; + } + static Value fromString(std::string s) { Value v; v.kind = ValueKind::String; v.stringValue = std::move(s); return v; } @@ -41,10 +47,10 @@ struct Value { Value v; v.kind = ValueKind::Null; return v; } - // JIF_FALSE: int 0 / boş string / null = yanlış; Ref her zaman doğru bool isTruthy() const { switch (kind) { case ValueKind::Int: return intValue != 0; + case ValueKind::Float: return floatValue != 0.0; case ValueKind::String: return !stringValue.empty(); case ValueKind::Ref: return ref != nullptr; case ValueKind::Null: return false; @@ -55,8 +61,18 @@ struct Value { std::string toString() const { switch (kind) { case ValueKind::Int: return std::to_string(intValue); + case ValueKind::Float: { + // Tam sayıysa "3.0", değilse "3.14" gibi — gereksiz sıfırları kırp + std::ostringstream oss; + oss << std::setprecision(10) << floatValue; + std::string s = oss.str(); + // Nokta yoksa ".0" ekle (saQut float değerleri her zaman nokta içerir) + if (s.find('.') == std::string::npos && s.find('e') == std::string::npos) + s += ".0"; + return s; + } case ValueKind::String: return stringValue; - case ValueKind::Ref: return ""; + case ValueKind::Ref: return ""; case ValueKind::Null: return "null"; } return "?"; @@ -65,8 +81,9 @@ struct Value { std::string typeName() const { switch (kind) { case ValueKind::Int: return "int"; + case ValueKind::Float: return "float"; case ValueKind::String: return "string"; - case ValueKind::Ref: return "array"; + case ValueKind::Ref: return "ref"; case ValueKind::Null: return "null"; } return "?"; diff --git a/tests/golden/float/basic.expected b/tests/golden/float/basic.expected new file mode 100644 index 0000000..3e6cb18 --- /dev/null +++ b/tests/golden/float/basic.expected @@ -0,0 +1,5 @@ +5.14 +6.28 +1.14 +1.57 +1.0 diff --git a/tests/golden/float/basic.sqt b/tests/golden/float/basic.sqt new file mode 100644 index 0000000..9952426 --- /dev/null +++ b/tests/golden/float/basic.sqt @@ -0,0 +1,11 @@ +int main() { + float x = 3.14; + float y = 2.0; + print(x + y); + print(x * y); + print(x - y); + print(x / y); + float z = 1; + print(z); + return 0; +}