feat(float): float/double aritmetik runtime (#44)
- value.hpp: ValueKind::Float + floatValue alanı; toString() noktalı gösterim - instruction.hpp: LOAD_FLOAT, FADD, FSUB, FMUL, FDIV, FNEG, INT_TO_FLOAT, FLOAT_TO_INT - instruction.hpp: Instruction.floatValue alanı (double) - ir_generator: LOAD_FLOAT emit; int→float çevrimi (bağlama-göre literal, INT_TO_FLOAT); generateBinaryArithmetic float-aware (FADD/FSUB/FMUL/FDIV seçimi) - interpreter: LOAD_FLOAT/FADD/FSUB/FMUL/FDIV/FNEG/INT_TO_FLOAT/FLOAT_TO_INT case'leri - print: val.toString() ile float değerleri "3.14" formatında yazdırır - ir_function: LOAD_FLOAT/FNEG/INT_TO_FLOAT/FLOAT_TO_INT dump; FADD/FSUB/FMUL/FDIV sembol - golden test: tests/golden/float/basic.sqt (22 test geçiyor) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
435c8bcb96
commit
996868efeb
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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]";
|
||||
|
||||
|
|
|
|||
|
|
@ -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<ExpressionNode*>(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<ExpressionNode*>(leftNode))
|
||||
leftIsFloat = e->resolvedType.isPrimitive() &&
|
||||
(e->resolvedType.prim == PrimitiveKind::Float ||
|
||||
e->resolvedType.prim == PrimitiveKind::Double);
|
||||
if (auto* e = dynamic_cast<ExpressionNode*>(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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
#define SAQUT_VM_VALUE
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <stdexcept>
|
||||
|
||||
// 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 "<array>";
|
||||
case ValueKind::Ref: return "<ref>";
|
||||
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 "?";
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
5.14
|
||||
6.28
|
||||
1.14
|
||||
1.57
|
||||
1.0
|
||||
|
|
@ -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;
|
||||
}
|
||||
Loading…
Reference in New Issue