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:
abdussamedulutas 2026-06-16 00:31:44 +03:00
parent 63ad3e2dcf
commit f3358473ac
6 changed files with 650 additions and 0 deletions

250
src/core/type.hpp Normal file
View File

@ -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

View File

@ -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

View File

@ -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

16
tests/run.sh Executable file
View File

@ -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 ==="

43
tests/test_diagnostic.cpp Normal file
View File

@ -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", "ı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;
}

49
tests/test_type.cpp Normal file
View File

@ -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;
}