feat(faz0): tip sistemi + tanılama motoru (Type + DiagnosticEngine)
Faz 0 (#69) — frontend'in iki temel veri yapısı: - src/core/type.hpp: Type (Primitive/Array/Struct/Function/Error), equals() (katı yapısal, gizli dönüşüm yok — ADR-010), toString(), toJson() (cam ilkesi), factory'ler ve fromName() yardımcısı. - src/diagnostic/diagnostic.hpp: DiagLevel, Diagnostic (veri birincil), hata kataloğu (E001-E010, W001-W003) + jsonEscape + makeDiagnostic. - src/diagnostic/diagnostic_engine.hpp: toplar (ilk hatada durmaz, ADR-013), printAll() (insan-okur, sıralı) + toJson() (makine-okur). - tests/: çerçevesiz birim testleri + run.sh. Header-only (ADR-003). -Wall -Wextra temiz. Henüz pipeline'a bağlı değil; Faz 2/3 (sembol tablosu, tip denetleyici) bunları tüketecek. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
0c2c97acb0
commit
f607f43cc8
|
|
@ -0,0 +1,250 @@
|
|||
// ============================================================================
|
||||
// saQut Compiler — Tip Sistemi (Type System)
|
||||
// ============================================================================
|
||||
//
|
||||
// DİZİN: src/core/type.hpp
|
||||
// KATMAN: Katman 0 — Tüm analiz katmanları tarafından kullanılır
|
||||
// BAĞIMLI: Yok (sadece <string>, <vector>, <memory>)
|
||||
// KULLANAN: Sembol tablosu (Faz 2), tip denetleyici (Faz 3), optimizasyon (Faz 4)
|
||||
//
|
||||
// AMAÇ:
|
||||
// Kaynak koddaki her ifadenin/sembolün veri tipini temsil eder. Derleyicinin
|
||||
// "bu değer ne?" sorusuna verdiği yapısal cevaptır. Tip, makine-okur (toJson)
|
||||
// ve insan-okur (toString) olarak dışa açıktır — "veri birincil, metin bir
|
||||
// görünümdür" ilkesine uyar (bkz. readme → Tasarım felsefesi).
|
||||
//
|
||||
// TİP TÜRLERİ (TypeKind):
|
||||
// Primitive : int, float, double, char, string, bool, void
|
||||
// Array : eleman tipi taşır (örn. int[])
|
||||
// Struct : struct adı taşır (örn. struct Point)
|
||||
// Function : dönüş tipi + parametre tipleri taşır
|
||||
// Error : hatalı/çözümlenememiş tip — ardışık sahte hataları bastırmak için
|
||||
// (tip denetleyici, operandı Error olan ifadede yeni hata üretmez)
|
||||
//
|
||||
// NOT (kasıtlı sadelik): Gizli tip dönüşümü YOKTUR (ADR-010). equals() yapısal
|
||||
// ve katıdır; "int, float'a uyar mı?" gibi kurallar tip denetleyicinin işidir,
|
||||
// bu dosyanın değil. Tamsayı literalinin bağlama-göre tiplenmesi de (ADR-010)
|
||||
// Faz 3'te ele alınır.
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
#ifndef SAQUT_CORE_TYPE
|
||||
#define SAQUT_CORE_TYPE
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
// ============================================================================
|
||||
// Enum'lar
|
||||
// ============================================================================
|
||||
|
||||
enum class PrimitiveKind { Int, Float, Double, Char, String, Bool, Void };
|
||||
|
||||
enum class TypeKind { Primitive, Array, Struct, Function, Error };
|
||||
|
||||
// ============================================================================
|
||||
// Type — Bir veri tipi
|
||||
// ============================================================================
|
||||
//
|
||||
// KULLANIM:
|
||||
// Type a = Type::Int(); // int
|
||||
// Type b = Type::array(Type::Int()); // int[]
|
||||
// Type c = Type::function(Type::Int(), {Type::Int(), Type::Int()}); // fn(int,int)->int
|
||||
// Type d = Type::structType("Point"); // struct Point
|
||||
// Type e = Type::error(); // <error>
|
||||
//
|
||||
// a.equals(Type::Int()); // true
|
||||
// a.equals(b); // false
|
||||
// a.toString(); // "int"
|
||||
// b.toString(); // "int[]"
|
||||
// c.toJson(); // {"kind":"function",...}
|
||||
//
|
||||
// İç içe tipler (array elemanı, fonksiyon dönüşü) shared_ptr ile tutulur:
|
||||
// Type değer-semantiğiyle kopyalanabilir kalır ama özyinelemeli olabilir.
|
||||
// ============================================================================
|
||||
|
||||
struct Type {
|
||||
TypeKind kind = TypeKind::Error;
|
||||
|
||||
PrimitiveKind prim = PrimitiveKind::Void; // kind == Primitive
|
||||
std::shared_ptr<Type> elementType; // kind == Array
|
||||
std::shared_ptr<Type> returnType; // kind == Function
|
||||
std::vector<Type> paramTypes; // kind == Function
|
||||
std::string structName; // kind == Struct
|
||||
|
||||
// ------------------------------------------------------------------ //
|
||||
// Factory'ler
|
||||
// ------------------------------------------------------------------ //
|
||||
static Type primitive(PrimitiveKind p) {
|
||||
Type t;
|
||||
t.kind = TypeKind::Primitive;
|
||||
t.prim = p;
|
||||
return t;
|
||||
}
|
||||
static Type Int() { return primitive(PrimitiveKind::Int); }
|
||||
static Type Float() { return primitive(PrimitiveKind::Float); }
|
||||
static Type Double() { return primitive(PrimitiveKind::Double); }
|
||||
static Type Char() { return primitive(PrimitiveKind::Char); }
|
||||
static Type String() { return primitive(PrimitiveKind::String); }
|
||||
static Type Bool() { return primitive(PrimitiveKind::Bool); }
|
||||
static Type Void() { return primitive(PrimitiveKind::Void); }
|
||||
|
||||
static Type array(Type elem) {
|
||||
Type t;
|
||||
t.kind = TypeKind::Array;
|
||||
t.elementType = std::make_shared<Type>(std::move(elem));
|
||||
return t;
|
||||
}
|
||||
static Type function(Type ret, std::vector<Type> params) {
|
||||
Type t;
|
||||
t.kind = TypeKind::Function;
|
||||
t.returnType = std::make_shared<Type>(std::move(ret));
|
||||
t.paramTypes = std::move(params);
|
||||
return t;
|
||||
}
|
||||
static Type structType(std::string name) {
|
||||
Type t;
|
||||
t.kind = TypeKind::Struct;
|
||||
t.structName = std::move(name);
|
||||
return t;
|
||||
}
|
||||
static Type error() {
|
||||
return Type{}; // varsayılan = Error
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ //
|
||||
// Yüklemler (predicates)
|
||||
// ------------------------------------------------------------------ //
|
||||
bool isError() const { return kind == TypeKind::Error; }
|
||||
bool isPrimitive() const { return kind == TypeKind::Primitive; }
|
||||
bool isArray() const { return kind == TypeKind::Array; }
|
||||
bool isStruct() const { return kind == TypeKind::Struct; }
|
||||
bool isFunction() const { return kind == TypeKind::Function; }
|
||||
bool isVoid() const { return kind == TypeKind::Primitive && prim == PrimitiveKind::Void; }
|
||||
|
||||
// Aritmetik/karşılaştırma operatörlerine uygun sayısal tip mi?
|
||||
bool isNumeric() const {
|
||||
return kind == TypeKind::Primitive &&
|
||||
(prim == PrimitiveKind::Int ||
|
||||
prim == PrimitiveKind::Float ||
|
||||
prim == PrimitiveKind::Double);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ //
|
||||
// equals — Yapısal eşitlik (katı; gizli dönüşüm yok, ADR-010)
|
||||
// ------------------------------------------------------------------ //
|
||||
bool equals(const Type& o) const {
|
||||
if (kind != o.kind) return false;
|
||||
switch (kind) {
|
||||
case TypeKind::Primitive:
|
||||
return prim == o.prim;
|
||||
case TypeKind::Array:
|
||||
return elementType && o.elementType &&
|
||||
elementType->equals(*o.elementType);
|
||||
case TypeKind::Struct:
|
||||
return structName == o.structName;
|
||||
case TypeKind::Function: {
|
||||
if (!returnType || !o.returnType) return false;
|
||||
if (!returnType->equals(*o.returnType)) return false;
|
||||
if (paramTypes.size() != o.paramTypes.size()) return false;
|
||||
for (size_t i = 0; i < paramTypes.size(); ++i)
|
||||
if (!paramTypes[i].equals(o.paramTypes[i])) return false;
|
||||
return true;
|
||||
}
|
||||
case TypeKind::Error:
|
||||
// Error == Error: ardışık sahte hataların bastırılması tip
|
||||
// denetleyicinin sorumluluğundadır (operandı Error ise hata üretme).
|
||||
return true;
|
||||
}
|
||||
return false; // erişilemez (tüm enum değerleri kapsandı)
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ //
|
||||
// İsim yardımcıları
|
||||
// ------------------------------------------------------------------ //
|
||||
static const char* primName(PrimitiveKind p) {
|
||||
switch (p) {
|
||||
case PrimitiveKind::Int: return "int";
|
||||
case PrimitiveKind::Float: return "float";
|
||||
case PrimitiveKind::Double: return "double";
|
||||
case PrimitiveKind::Char: return "char";
|
||||
case PrimitiveKind::String: return "string";
|
||||
case PrimitiveKind::Bool: return "bool";
|
||||
case PrimitiveKind::Void: return "void";
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
|
||||
// Bir tip adından (parser tipleri string olarak tutar) primitif Type üretir.
|
||||
// Bilinen primitif değilse Error döner — bilinmeyen tip adının teşhisi
|
||||
// (E007) çağıranın (Faz 2/3) işidir; bu fonksiyon sessizce Error verir.
|
||||
static Type fromName(const std::string& n) {
|
||||
if (n == "int") return Int();
|
||||
if (n == "float") return Float();
|
||||
if (n == "double") return Double();
|
||||
if (n == "char") return Char();
|
||||
if (n == "string") return String();
|
||||
if (n == "bool") return Bool();
|
||||
if (n == "void") return Void();
|
||||
return error();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ //
|
||||
// toString — İnsan-okur ("int", "int[]", "fn(int,int)->int")
|
||||
// ------------------------------------------------------------------ //
|
||||
std::string toString() const {
|
||||
switch (kind) {
|
||||
case TypeKind::Primitive:
|
||||
return primName(prim);
|
||||
case TypeKind::Array:
|
||||
return (elementType ? elementType->toString() : "<?>") + "[]";
|
||||
case TypeKind::Struct:
|
||||
return "struct " + structName;
|
||||
case TypeKind::Function: {
|
||||
std::string s = "fn(";
|
||||
for (size_t i = 0; i < paramTypes.size(); ++i) {
|
||||
if (i) s += ",";
|
||||
s += paramTypes[i].toString();
|
||||
}
|
||||
s += ")->";
|
||||
s += returnType ? returnType->toString() : "<?>";
|
||||
return s;
|
||||
}
|
||||
case TypeKind::Error:
|
||||
return "<error>";
|
||||
}
|
||||
return "<?>";
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ //
|
||||
// toJson — Makine-okur (cam ilkesi: her tip dışarıdan sorgulanabilir)
|
||||
// ------------------------------------------------------------------ //
|
||||
std::string toJson() const {
|
||||
switch (kind) {
|
||||
case TypeKind::Primitive:
|
||||
return std::string("{\"kind\":\"primitive\",\"name\":\"") + primName(prim) + "\"}";
|
||||
case TypeKind::Array:
|
||||
return std::string("{\"kind\":\"array\",\"element\":") +
|
||||
(elementType ? elementType->toJson() : "null") + "}";
|
||||
case TypeKind::Struct:
|
||||
return "{\"kind\":\"struct\",\"name\":\"" + structName + "\"}";
|
||||
case TypeKind::Function: {
|
||||
std::string s = "{\"kind\":\"function\",\"returns\":";
|
||||
s += returnType ? returnType->toJson() : "null";
|
||||
s += ",\"params\":[";
|
||||
for (size_t i = 0; i < paramTypes.size(); ++i) {
|
||||
if (i) s += ",";
|
||||
s += paramTypes[i].toJson();
|
||||
}
|
||||
s += "]}";
|
||||
return s;
|
||||
}
|
||||
case TypeKind::Error:
|
||||
return "{\"kind\":\"error\"}";
|
||||
}
|
||||
return "null";
|
||||
}
|
||||
};
|
||||
|
||||
#endif // SAQUT_CORE_TYPE
|
||||
|
|
@ -0,0 +1,178 @@
|
|||
// ============================================================================
|
||||
// saQut Compiler — Tanılama (Diagnostic) Veri Yapıları + Hata Kataloğu
|
||||
// ============================================================================
|
||||
//
|
||||
// DİZİN: src/diagnostic/diagnostic.hpp
|
||||
// KATMAN: Katman 0 — Tüm analiz katmanları tarafından kullanılır
|
||||
// BAĞIMLI: src/core/location.hpp
|
||||
// KULLANAN: DiagnosticEngine, sembol toplayıcı (Faz 2), tip denetleyici (Faz 3)
|
||||
//
|
||||
// AMAÇ:
|
||||
// Derleme sırasında bulunan hata/uyarıları YAPISAL veri olarak temsil eder.
|
||||
// "Veri birincil, insan-okur metin bir görünümdür" (readme → Tasarım felsefesi):
|
||||
// bir Diagnostic; seviye + kod + konum + mesaj taşır; ekrana basılan satır
|
||||
// bunun yalnızca bir render'ıdır. Bu sayede aynı tanı LSP, AI veya `saqut
|
||||
// explain` tarafından da tüketilebilir.
|
||||
//
|
||||
// HATA KATALOĞU (baştan sabitlenir — yeni kodlar buraya eklenir):
|
||||
// E001 Tanımsız değişken/isim (declare-before-use ihlali dâhil) Faz 2/3
|
||||
// E002 Aynı scope'ta çift tanım Faz 2
|
||||
// E003 Tip uyuşmazlığı (gizli dönüşüm yok, ADR-010) Faz 3
|
||||
// E004 Döngü/switch dışı break/continue Faz 3
|
||||
// E005 Fonksiyon dışı return Faz 3
|
||||
// E006 Return tipi imzaya uymuyor Faz 3
|
||||
// E007 Tanımsız tip (bilinmeyen tip adı) Faz 2/3
|
||||
// E008 Fonksiyon çağrısı argüman sayısı/tipi uyuşmuyor Faz 3
|
||||
// E009 Array boyutu sabit değil / geçersiz Faz 3
|
||||
// E010 Özyinelemeli/döngüsel struct (by-value çevrim → sonsuz boyut) Faz 2/3
|
||||
// W001 Kullanılmayan değişken Faz 4
|
||||
// W002 Sıfıra bölme (sabit folding) Faz 4
|
||||
// W003 Erişilemez (ölü) kod Faz 4
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
#ifndef SAQUT_DIAGNOSTIC_DIAGNOSTIC
|
||||
#define SAQUT_DIAGNOSTIC_DIAGNOSTIC
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "core/location.hpp"
|
||||
|
||||
// ============================================================================
|
||||
// DiagLevel — Tanı seviyesi
|
||||
// ============================================================================
|
||||
|
||||
enum class DiagLevel { Error, Warning, Note, Hint };
|
||||
|
||||
inline const char* diagLevelName(DiagLevel l) {
|
||||
switch (l) {
|
||||
case DiagLevel::Error: return "error";
|
||||
case DiagLevel::Warning: return "warning";
|
||||
case DiagLevel::Note: return "note";
|
||||
case DiagLevel::Hint: return "hint";
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
|
||||
// İnsan-okur çıktı için Türkçe karşılık
|
||||
inline const char* diagLevelNameTr(DiagLevel l) {
|
||||
switch (l) {
|
||||
case DiagLevel::Error: return "hata";
|
||||
case DiagLevel::Warning: return "uyarı";
|
||||
case DiagLevel::Note: return "not";
|
||||
case DiagLevel::Hint: return "ipucu";
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
|
||||
// JSON string kaçışı (mesaj/ipucu tırnak veya satır sonu içerebilir)
|
||||
inline std::string jsonEscape(const std::string& s) {
|
||||
std::string out;
|
||||
out.reserve(s.size() + 8);
|
||||
for (char c : s) {
|
||||
switch (c) {
|
||||
case '"': out += "\\\""; break;
|
||||
case '\\': out += "\\\\"; break;
|
||||
case '\n': out += "\\n"; break;
|
||||
case '\r': out += "\\r"; break;
|
||||
case '\t': out += "\\t"; break;
|
||||
default: out += c; break;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Diagnostic — Tek bir tanı (hata/uyarı/not/ipucu)
|
||||
// ============================================================================
|
||||
//
|
||||
// KULLANIM:
|
||||
// Diagnostic d{DiagLevel::Error, "E003", loc, "int'e string atanamaz"};
|
||||
// d.hint = "açık dönüşüm gerekiyor";
|
||||
// std::cout << d.toJson();
|
||||
// ============================================================================
|
||||
|
||||
struct Diagnostic {
|
||||
DiagLevel level = DiagLevel::Error;
|
||||
std::string code; // "E003" (katalog kodu; boş olabilir)
|
||||
SourceLocation loc; // hatanın kaynak koddaki yeri
|
||||
std::string message; // bağlama özel açıklama
|
||||
std::string hint; // opsiyonel "şunu dene" önerisi
|
||||
|
||||
std::string toJson() const {
|
||||
std::string s = "{";
|
||||
s += "\"level\":\""; s += diagLevelName(level); s += "\",";
|
||||
s += "\"code\":\""; s += jsonEscape(code); s += "\",";
|
||||
s += "\"location\":"; s += loc.toJson(); s += ",";
|
||||
s += "\"message\":\""; s += jsonEscape(message); s += "\"";
|
||||
if (!hint.empty()) {
|
||||
s += ",\"hint\":\""; s += jsonEscape(hint); s += "\"";
|
||||
}
|
||||
s += "}";
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Hata Kataloğu — kod → (seviye, kanonik başlık)
|
||||
// ============================================================================
|
||||
//
|
||||
// Bağlama özel mesaj report sırasında verilir; buradaki başlık, kodun GENEL
|
||||
// anlamıdır (ileride `saqut explain E003` bunu kullanabilir, #107/#98).
|
||||
// ============================================================================
|
||||
|
||||
struct DiagInfo {
|
||||
const char* code;
|
||||
DiagLevel level;
|
||||
const char* title;
|
||||
};
|
||||
|
||||
inline const std::vector<DiagInfo>& diagnosticCatalog() {
|
||||
static const std::vector<DiagInfo> catalog = {
|
||||
{"E001", DiagLevel::Error, "Tanımsız değişken/isim"},
|
||||
{"E002", DiagLevel::Error, "Aynı scope'ta çift tanım"},
|
||||
{"E003", DiagLevel::Error, "Tip uyuşmazlığı"},
|
||||
{"E004", DiagLevel::Error, "Döngü/switch dışı break/continue"},
|
||||
{"E005", DiagLevel::Error, "Fonksiyon dışı return"},
|
||||
{"E006", DiagLevel::Error, "Return tipi imzaya uymuyor"},
|
||||
{"E007", DiagLevel::Error, "Tanımsız tip"},
|
||||
{"E008", DiagLevel::Error, "Fonksiyon çağrısı argümanı uyuşmuyor"},
|
||||
{"E009", DiagLevel::Error, "Array boyutu sabit değil / geçersiz"},
|
||||
{"E010", DiagLevel::Error, "Özyinelemeli/döngüsel struct tanımı"},
|
||||
{"W001", DiagLevel::Warning, "Kullanılmayan değişken"},
|
||||
{"W002", DiagLevel::Warning, "Sıfıra bölme (sabit ifade)"},
|
||||
{"W003", DiagLevel::Warning, "Erişilemez (ölü) kod"},
|
||||
};
|
||||
return catalog;
|
||||
}
|
||||
|
||||
// Kod kataloğda var mı? (yoksa nullptr)
|
||||
inline const DiagInfo* findDiag(const std::string& code) {
|
||||
for (const auto& d : diagnosticCatalog())
|
||||
if (code == d.code) return &d;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Bir koddan Diagnostic üretir; seviye kataloğdan çözülür (yoksa: E→Error,
|
||||
// W→Warning, diğer→Note). Bağlama özel mesajı çağıran verir.
|
||||
inline Diagnostic makeDiagnostic(const std::string& code,
|
||||
const SourceLocation& loc,
|
||||
const std::string& message,
|
||||
const std::string& hint = "") {
|
||||
DiagLevel level = DiagLevel::Note;
|
||||
if (const DiagInfo* info = findDiag(code)) {
|
||||
level = info->level;
|
||||
} else if (!code.empty()) {
|
||||
if (code[0] == 'E') level = DiagLevel::Error;
|
||||
else if (code[0] == 'W') level = DiagLevel::Warning;
|
||||
}
|
||||
Diagnostic d;
|
||||
d.level = level;
|
||||
d.code = code;
|
||||
d.loc = loc;
|
||||
d.message = message;
|
||||
d.hint = hint;
|
||||
return d;
|
||||
}
|
||||
|
||||
#endif // SAQUT_DIAGNOSTIC_DIAGNOSTIC
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
// ============================================================================
|
||||
// saQut Compiler — Tanılama Motoru (DiagnosticEngine)
|
||||
// ============================================================================
|
||||
//
|
||||
// DİZİN: src/diagnostic/diagnostic_engine.hpp
|
||||
// KATMAN: Katman 0 — Tüm analiz katmanları tarafından kullanılır
|
||||
// BAĞIMLI: src/diagnostic/diagnostic.hpp
|
||||
// KULLANAN: sembol toplayıcı (Faz 2), tip denetleyici (Faz 3), pipeline (main)
|
||||
//
|
||||
// AMAÇ:
|
||||
// Derleme boyunca üretilen tüm Diagnostic'leri EKLENME SIRASIYLA biriktirir.
|
||||
// İlk hatada DURMAZ (ADR-013): bütün hatalar toplanır, faz sonunda topluca
|
||||
// raporlanır; durdurma kararını pipeline verir (hasErrors()).
|
||||
//
|
||||
// İki çıktı yüzü vardır — aynı veriden:
|
||||
// printAll() → insan-okur (terminal)
|
||||
// toJson() → makine-okur (LSP / AI / araçlar)
|
||||
//
|
||||
// ============================================================================
|
||||
|
||||
#ifndef SAQUT_DIAGNOSTIC_ENGINE
|
||||
#define SAQUT_DIAGNOSTIC_ENGINE
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <ostream>
|
||||
#include "diagnostic/diagnostic.hpp"
|
||||
|
||||
// ============================================================================
|
||||
// DiagnosticEngine
|
||||
// ============================================================================
|
||||
//
|
||||
// KULLANIM:
|
||||
// DiagnosticEngine diag;
|
||||
// diag.report(makeDiagnostic("E001", loc, "x tanımsız"));
|
||||
// diag.report(DiagLevel::Warning, "W001", loc2, "y kullanılmıyor");
|
||||
// if (diag.hasErrors()) diag.printAll(std::cerr);
|
||||
// ============================================================================
|
||||
|
||||
class DiagnosticEngine {
|
||||
public:
|
||||
// --- Ekleme ---
|
||||
void report(const Diagnostic& d) {
|
||||
diagnostics_.push_back(d);
|
||||
}
|
||||
|
||||
// Kolaylık: koddan üret + ekle (seviye kataloğdan çözülür)
|
||||
void report(const std::string& code,
|
||||
const SourceLocation& loc,
|
||||
const std::string& message,
|
||||
const std::string& hint = "") {
|
||||
diagnostics_.push_back(makeDiagnostic(code, loc, message, hint));
|
||||
}
|
||||
|
||||
// Kolaylık: seviyeyi açıkça vererek
|
||||
void report(DiagLevel level,
|
||||
const std::string& code,
|
||||
const SourceLocation& loc,
|
||||
const std::string& message,
|
||||
const std::string& hint = "") {
|
||||
Diagnostic d;
|
||||
d.level = level; d.code = code; d.loc = loc; d.message = message; d.hint = hint;
|
||||
diagnostics_.push_back(d);
|
||||
}
|
||||
|
||||
// --- Sorgu ---
|
||||
bool hasErrors() const { return errorCount() > 0; }
|
||||
|
||||
int errorCount() const { return countLevel(DiagLevel::Error); }
|
||||
int warningCount() const { return countLevel(DiagLevel::Warning); }
|
||||
int count() const { return static_cast<int>(diagnostics_.size()); }
|
||||
bool empty() const { return diagnostics_.empty(); }
|
||||
|
||||
const std::vector<Diagnostic>& all() const { return diagnostics_; }
|
||||
|
||||
void clear() { diagnostics_.clear(); }
|
||||
|
||||
// --- İnsan-okur çıktı (ekleme sırasıyla) ---
|
||||
void printAll(std::ostream& os) const {
|
||||
for (const auto& d : diagnostics_) {
|
||||
os << d.loc.toString() << ": "
|
||||
<< diagLevelNameTr(d.level) << " [" << d.code << "]: "
|
||||
<< d.message << "\n";
|
||||
if (!d.hint.empty())
|
||||
os << " ipucu: " << d.hint << "\n";
|
||||
}
|
||||
os << "— " << errorCount() << " hata, " << warningCount() << " uyarı\n";
|
||||
}
|
||||
|
||||
// --- Makine-okur çıktı ---
|
||||
std::string toJson() const {
|
||||
std::string s = "{\"diagnostics\":[";
|
||||
for (size_t i = 0; i < diagnostics_.size(); ++i) {
|
||||
if (i) s += ",";
|
||||
s += diagnostics_[i].toJson();
|
||||
}
|
||||
s += "],\"errorCount\":" + std::to_string(errorCount());
|
||||
s += ",\"warningCount\":" + std::to_string(warningCount());
|
||||
s += "}";
|
||||
return s;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<Diagnostic> diagnostics_;
|
||||
|
||||
int countLevel(DiagLevel level) const {
|
||||
int n = 0;
|
||||
for (const auto& d : diagnostics_)
|
||||
if (d.level == level) ++n;
|
||||
return n;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // SAQUT_DIAGNOSTIC_ENGINE
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
#!/usr/bin/env bash
|
||||
# saQut birim testleri — çerçevesiz (assert tabanlı), tek komutla.
|
||||
# Kullanım: bash tests/run.sh
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
CXX="${CXX:-g++}"
|
||||
FLAGS=(-std=c++20 -Wall -Wextra -I"$ROOT/src")
|
||||
|
||||
for t in test_type test_diagnostic; do
|
||||
echo "=== $t ==="
|
||||
"$CXX" "${FLAGS[@]}" "$ROOT/tests/$t.cpp" -o "/tmp/saqut_$t"
|
||||
"/tmp/saqut_$t"
|
||||
done
|
||||
|
||||
echo "=== TUM TESTLER GECTI ==="
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
// Faz 0 — DiagnosticEngine birim testleri (çerçevesiz; assert + çıktı).
|
||||
// Koşmak için: tests/run.sh
|
||||
#include "diagnostic/diagnostic_engine.hpp"
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
|
||||
int main() {
|
||||
DiagnosticEngine diag;
|
||||
SourceLocation l1{"test.sqt", 3, 5, 40};
|
||||
SourceLocation l2{"test.sqt", 7, 9, 88};
|
||||
SourceLocation l3{"test.sqt", 12, 1, 150};
|
||||
|
||||
// Üç tanı — ekleme sırası korunmalı (ADR-013: ilk hatada durma, topla)
|
||||
diag.report("E001", l1, "x tanımsız");
|
||||
diag.report("E003", l2, "int'e string atanamaz", "açık dönüşüm gerekiyor");
|
||||
diag.report("W001", l3, "y kullanılmıyor");
|
||||
|
||||
assert(diag.count() == 3);
|
||||
assert(diag.hasErrors());
|
||||
assert(diag.errorCount() == 2);
|
||||
assert(diag.warningCount() == 1);
|
||||
|
||||
// Sıra korunmuş mu?
|
||||
assert(diag.all()[0].code == "E001");
|
||||
assert(diag.all()[1].code == "E003");
|
||||
assert(diag.all()[2].code == "W001");
|
||||
|
||||
// Seviye kataloğdan çözülmüş mü?
|
||||
assert(diag.all()[0].level == DiagLevel::Error);
|
||||
assert(diag.all()[2].level == DiagLevel::Warning);
|
||||
|
||||
// Katalog erişimi
|
||||
assert(findDiag("E010") != nullptr);
|
||||
assert(findDiag("E999") == nullptr);
|
||||
|
||||
std::cout << "--- printAll ---\n";
|
||||
diag.printAll(std::cout);
|
||||
std::cout << "--- toJson ---\n";
|
||||
std::cout << diag.toJson() << "\n";
|
||||
|
||||
std::cout << "test_diagnostic: TUM TESTLER GECTI\n";
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
// Faz 0 — Type birim testleri (çerçevesiz; assert + çıktı).
|
||||
// Koşmak için: tests/run.sh (veya g++ -std=c++20 -Wall -Wextra -Isrc ...)
|
||||
#include "core/type.hpp"
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
|
||||
int main() {
|
||||
Type i = Type::Int();
|
||||
Type i2 = Type::Int();
|
||||
Type f = Type::Float();
|
||||
Type arrI = Type::array(Type::Int());
|
||||
Type arrI2 = Type::array(Type::Int());
|
||||
Type arrF = Type::array(Type::Float());
|
||||
Type fn = Type::function(Type::Int(), {Type::Int(), Type::Int()});
|
||||
Type st = Type::structType("Point");
|
||||
Type err = Type::error();
|
||||
|
||||
// equals — yapısal, katı (gizli dönüşüm yok)
|
||||
assert(i.equals(i2));
|
||||
assert(!i.equals(f));
|
||||
assert(arrI.equals(arrI2));
|
||||
assert(!arrI.equals(arrF));
|
||||
assert(!arrI.equals(i));
|
||||
assert(fn.equals(Type::function(Type::Int(), {Type::Int(), Type::Int()})));
|
||||
assert(!fn.equals(Type::function(Type::Int(), {Type::Int()})));
|
||||
assert(st.equals(Type::structType("Point")));
|
||||
assert(!st.equals(Type::structType("Vec")));
|
||||
assert(err.equals(Type::error()));
|
||||
|
||||
// yüklemler
|
||||
assert(i.isNumeric() && !st.isNumeric());
|
||||
assert(Type::Void().isVoid());
|
||||
assert(err.isError());
|
||||
|
||||
// toString
|
||||
assert(i.toString() == "int");
|
||||
assert(arrI.toString() == "int[]");
|
||||
assert(fn.toString() == "fn(int,int)->int");
|
||||
assert(st.toString() == "struct Point");
|
||||
assert(err.toString() == "<error>");
|
||||
|
||||
// fromName
|
||||
assert(Type::fromName("int").equals(Type::Int()));
|
||||
assert(Type::fromName("bool").equals(Type::Bool()));
|
||||
assert(Type::fromName("bogus").isError());
|
||||
|
||||
std::cout << "test_type: TUM TESTLER GECTI\n";
|
||||
return 0;
|
||||
}
|
||||
Loading…
Reference in New Issue