saqut-compiler/MISSION-FAZ3.md

12 KiB
Raw Blame History

MISSION — Faz 3: Semantik Analiz (Gitea #72)

Bu dosya kendi kendine yeter. "MISSION-FAZ3'ü oku ve yap" denince doğrudan uygula. Bitince DOĞRULAMA bölümünü çalıştır, #72'ye tamamlanma yorumu düş, bu dosyayı sil veya MISSION-FAZ4.md ile değiştir. Co-Authored-By: Claude Opus 4.8 trailer'ı unutma.


0) HEDEF & BAŞARI KRİTERLERİ (#72)

İki modül: TypeChecker (her ExpressionNode'a tip ata) + StructuralValidator (kontrol akışı kuralları).

Bitti tanımı:

  1. build/saqut check file:examples/fibonacci.sqt → 0 hata 0 uyarı; her expression node'unun resolvedType alanı JSON'da dolu.
  2. examples/semantic/ test fixture'ları doğru hata/uyarı üretiyor.
  3. -Wall -Wextra temiz. Faz 0/1/2 testleri hâlâ geçer.

1) DÖNÜŞÜM KURALLARI (ADR-010 özeti)

Durum Sonuç Kod
float x = 1 Geçerli — literal 1.0 sayılır
double x = 1 Geçerli
float x = intVar Uyarı — örtük genişletme, veri kaybı yok W004
double x = intVar Uyarı W004
double x = floatVar Uyarı W004
int x = 1.5 Hata — literal'den veri kaybı E003
int x = floatVar Hata — daraltma E003
float x = doubleVar Hata — daraltma E003
int x = intVar Geçerli

Kural özeti:

  • Literal bağlama-göre tiplenir: hedef tip daha geniş veya aynıysa uyarısız geçer.
  • Değişken→değişken: genişletme (widening) = W004, daraltma (narrowing) = E003.
  • Aynı tip = her zaman geçerli.

Yardımcı fonksiyon (type_checker içinde):

enum class ConvResult { Ok, Warn_W004, Error_E003 };
ConvResult checkAssignCompat(const Type& target, const Type& src, bool srcIsLiteral);

2) KATALOG GÜNCELLEMESİ

src/diagnostic/diagnostic.hpp → W004 ekle:

{"W004", DiagLevel::Warning, "Örtük sayısal genişletme (widening)"},

3) DOSYA DOSYA PLAN

Yeni dizin: src/semantic/. CMake GLOB_RECURSE → yeni .cpp'ler otomatik alınır ama build.sh ile derle (cmake yeniden config eder).


3.1 src/semantic/type_checker.hpp

#ifndef SAQUT_SEMANTIC_TYPE_CHECKER
#define SAQUT_SEMANTIC_TYPE_CHECKER

#include "symbol/symbol_table.hpp"
#include "diagnostic/diagnostic_engine.hpp"
#include "parser/ast_node.hpp"
#include "core/type.hpp"

class TypeChecker {
public:
    TypeChecker(SymbolTable& table, DiagnosticEngine& diag)
        : table_(table), diag_(diag) {}

    void check(ASTNode* program);

private:
    // İfadeyi gez, resolvedType ata, tipi döndür.
    // expectedType: bağlam tipi (literal genişletme için); boş = bilinmiyor.
    Type checkExpr(ASTNode* node, const Type& expected = Type::error());

    // Deyimi gez (statement'lar tip döndürmez).
    void checkStmt(ASTNode* node);

    // Fonksiyon gövdesini gez; dönüş tipi bağlamını stack'te tut.
    void checkFunction(ASTNode* fnNode);

    // Atama uyumunu denetle; uyarı/hata raporla. true = geçerli.
    bool checkAssign(const Type& target, const Type& src,
                     bool srcIsLiteral, const SourceLocation& loc,
                     const std::string& context);

    SymbolTable&      table_;
    DiagnosticEngine& diag_;

    // Aktif fonksiyonun beklenen dönüş tipi (iç içe fonksiyon yok, stack
    // yerine tek değer yeterli).
    Type currentReturnType_;
    bool inFunction_ = false;
};

#endif // SAQUT_SEMANTIC_TYPE_CHECKER

3.2 src/semantic/type_checker.cpp

check(program):

  • Program children'ı gez: FunctionDecl → checkFunction, VariableDecl → checkStmt, StructDecl → atla.

checkFunction(fnNode):

  • inFunction_ = true; currentReturnType_ = typeFromReturnType(fn->returnType);
  • checkStmt(body) (body = children[0])
  • inFunction_ = false;

checkStmt(node) — switch(node->kind):

  • Block: her child → checkStmt.
  • VariableDecl: initExpr varsa checkExpr(initExpr, typeFromName(vd->varType)); sonucu target tiple checkAssign ile denetle.
  • ExpressionStatement: checkExpr(es->expression).
  • ReturnStatement: value varsa checkExpr(value, currentReturnType_); sonucu checkAssign(currentReturnType_, valueType, isLiteral, loc, "return"). Değer yok ve returnType != void → E006.
  • IfStatement: condition checkExpr (bool beklenir, ama şimdilik herhangi sayısal tip de geçerli — E003 değil, TODO Faz3+); then/else checkStmt.
  • WhileStatement / DoWhileStatement: condition checkExpr; body checkStmt.
  • ForStatement: init checkStmt; condition checkExpr; update checkExpr; body checkStmt.
  • Break / Continue / ExpressionStatement: içerik checkExpr.
  • ReturnStatement: yukarıda.

checkExpr(node, expected) — switch(node->kind) → Type döndürür:

  • Literal(INTEGER):
    • expected sayısal ve daha geniş veya aynıysa → expected tipini döndür (literal 1.0 olarak tiplenir, uyarısız).
    • expected int veya error → Type::Int().
    • expected float literal'e uymuyor (ör. expected=int, literal=FLOAT) → E003.
  • Literal(FLOAT): expected double ise double döndür (kayıpsız); expected int → E003; aksi → Type::Float().
  • Literal(BOOLEAN): Type::Bool().
  • Literal(STRING): Type::String().
  • Literal(BOŞ): Type::error() (null — Faz 3+ kapsam dışı).
  • Identifier: id->resolvedSymbol ? id->resolvedSymbol->type : Type::error(). Type::error() ise E001 zaten Faz 2'de raporlandı, sessiz geç.
  • BinaryExpression:
    • Atama operatörü (EQUAL, +=, -=, vb.):
      • left = checkExpr(left), right = checkExpr(right, leftType).
      • checkAssign(leftType, rightType, isLiteralRight, loc, "atama").
      • resolvedType = leftType.
    • Aritmetik (+, -, *, /, %):
      • Her iki operandı checkExpr et.
      • İkisi de sayısal ve aynı tip → sonuç o tip.
      • İkisi sayısal farklı tip → checkAssign mantığıyla uyar/hata; sonuç daha geniş tip (veya E003 durumunda error).
      • Sayısal değil → E003, Type::error().
    • Karşılaştırma (==, !=, <, <=, >, >=):
      • Operandları checkExpr; uyumlu sayısal veya aynı tip → Type::Bool().
      • Uyumsuz → E003, Type::Bool() (hata yayılmasın, bool olarak devam).
    • Mantıksal (&&, ||):
      • Her iki operand bool beklenir; değilse E003.
      • Sonuç Type::Bool().
    • Unary prefix (-, +, !):
      • Left = nullptr; Right = operand.
      • -/+: sayısal beklenir; !: bool beklenir.
    • resolvedType'a sonucu ata.
  • Call:
    • callee = checkExpr(callee) → Function tipi beklenir.
    • Argüman sayısı imzayla eşleşmeli → E008.
    • Her argüman: checkExpr(arg, paramType[i])checkAssign(paramType, argType, ...).
    • resolvedType = callee tipi's returnType.
  • Postfix (++/--): operand sayısal → sonuç aynı tip; değilse E003.
  • MemberAccess: Type::error() + TODO (struct alan çözümü Faz 3+).
  • IndexExpression: TODO; Type::error().

checkAssign(target, src, srcIsLiteral, loc, ctx):

  • target veya src = error → return true (sessiz geç, hata zaten raporlandı).
  • target == src → return true.
  • Genişletme (widening):
    • int → float: srcIsLiteral → geçerli (uyarısız); değil → W004.
    • int → double, float → double: aynı kural.
    • srcIsLiteral ve kayıpsız → return true.
    • srcIsLiteral değil → W004 raporla, return true (yine de geçerli).
  • Daraltma (narrowing):
    • float → int, double → int, double → floatE003, return false.
  • Tamamen farklı tipler (int ↔ string, vb.) → E003, return false.

srcIsLiteral: node->kind == ASTKind::Literal ise true.


3.3 src/semantic/structural_validator.hpp

#ifndef SAQUT_SEMANTIC_STRUCTURAL_VALIDATOR
#define SAQUT_SEMANTIC_STRUCTURAL_VALIDATOR

#include "diagnostic/diagnostic_engine.hpp"
#include "parser/ast_node.hpp"

class StructuralValidator {
public:
    StructuralValidator(DiagnosticEngine& diag) : diag_(diag) {}
    void validate(ASTNode* program);

private:
    void walkDecl(ASTNode* node);
    void walkStmt(ASTNode* node);

    DiagnosticEngine& diag_;
    int  loopDepth_  = 0;
    bool inFunction_ = false;
};

#endif // SAQUT_SEMANTIC_STRUCTURAL_VALIDATOR

3.4 src/semantic/structural_validator.cpp

validate(program): her top-level child → walkDecl.

walkDecl:

  • FunctionDecl: inFunction_=true, walkStmt(body), inFunction_=false.
  • VariableDecl / StructDecl: atla.

walkStmt(node) — switch:

  • Block: her child walkStmt.
  • IfStatement: then/else walkStmt.
  • WhileStatement / DoWhileStatement: loopDepth_++; walkStmt(body); loopDepth_--;
  • ForStatement: loopDepth_++; walkStmt(init?); walkStmt(body); loopDepth_--;
  • BreakStatement: loopDepth_ == 0 → E004.
  • ContinueStatement: loopDepth_ == 0 → E004.
  • ReturnStatement: !inFunction_ → E005. (Return tip uyumu TypeChecker'ın işi; burada sadece yapısal kural.)
  • ExpressionStatement / VariableDecl: walkStmt çocukları için yinele.
  • Diğer: atla.

4) CLI — saqut check KOMUTU

4.1 src/cli/commands/check.hpp

inline int cmdCheck(const CliArgs& args) {
    // tokenize → parse → symbolCollect → typeCheck → structValidate
    // Tüm hatalar DiagnosticEngine'de toplanır.
    // Çıktı: JSON (--compact destekli)
    // {"file":"...","diagnostics":{...}}
    // exit code: hasErrors() ? 1 : 0
}

4.2 src/cli/cli.hpp veya main.cpp

check komutu kaydet; parseArgs tanımlı komutlar listesine ekle.


5) TEST FIXTURE'LARI — examples/semantic/

Dosya İçerik Beklenen
widening.sqt float x = 1; (OK) + float y = someInt; W004
narrowing.sqt int x = 1.5; E003
bad_return.sqt int foo() { return 1.5; } E003
break_outside.sqt top-level break; E004
return_outside.sqt top-level return 0; E005
bad_args.sqt fibonacci(1, 2) (1 parametre bekliyor) E008

6) DOĞRULAMA KOMUTLARI

bash scripts/build.sh

# Başarı: 0 hata 0 uyarı
build/saqut check file:examples/fibonacci.sqt

# Her expression tipli mi?
build/saqut ast file:examples/fibonacci.sqt | python3 -c "
import json,sys
def walk(n):
    if 'resolvedType' in n and n['resolvedType'] is None:
        print('UNTIPPED:', n.get('kind'), n.get('name',''))
    for v in n.values():
        if isinstance(v, dict): walk(v)
        elif isinstance(v, list): [walk(i) for i in v if isinstance(i, dict)]
walk(json.load(sys.stdin))"

# Hata fixture'ları
build/saqut check file:examples/semantic/widening.sqt      # W004
build/saqut check file:examples/semantic/narrowing.sqt     # E003
build/saqut check file:examples/semantic/break_outside.sqt # E004
build/saqut check file:examples/semantic/bad_args.sqt      # E008

# Regresyon
bash tests/run.sh
build/saqut ast file:examples/fibonacci.sqt | python3 -m json.tool >/dev/null && echo AST-OK

7) HAFIZA İPUÇLARI

  • ASTKind::BinaryExpressionBinaryExpressionNode{Left, Right, Operator(TokenType)}
  • Atama operatörleri: EQUAL, PLUS_EQUAL, MINUS_EQUAL, STAR_EQUAL, SLASH_EQUAL, ...
  • ASTKind::CallCallExpressionNode{callee, arguments}
  • ASTKind::LiteralLiteralNode{literalType(INTEGER/FLOAT/...), parserToken}
  • ASTKind::IdentifierIdentifierNode{resolvedSymbol} (Faz 2'de set edildi)
  • FunctionDeclNode{name, returnType(string), params, children=[body]}
  • Type::fromName(str) primitifi çözer; Type::function(ret, params) fonksiyon tipi
  • diag_.report("E003", loc, "mesaj") → katalogdan seviye otomatik çözülür
  • resolvedTypeExpressionNode'un alanı (ast_node.hpp)
  • Literal'ın literal tipi: ((LiteralNode*)node)->literalType
  • İki sayısal tipin genişliği: int < float < double (basit sıralama yeterli)