feat(faz1): AST'i Expression/Statement olarak ayır + analiz alanları (#70)

- ast_node.hpp: ExpressionNode (resolvedType + isConstant/foldedValue TODO)
  ve StatementNode (isReachable) ara taban sınıfları; core/type.hpp dahil.
- İfade düğümleri (Literal, Identifier, Binary, Postfix, Call, Member,
  Index) → ExpressionNode; toJson'a resolvedType (çözülmemişse null).
- Deyim düğümleri (Block/If/While/For/DoWhile/Return/Break/Continue/
  ExpressionStatement + VariableDecl) → StatementNode; toJson'a isReachable.
- IdentifierNode'a Symbol* resolvedSymbol (TODO faz-2, ileri bildirim).
- Faz 0 düzeltmesi: diagnostic.hpp kendi jsonEscape'ini kaldırdı, tools.hpp'
  dekini kullanıyor (ODR çakışması önlendi).

Doğrulama: -Wall -Wextra temiz; saqut ast fibonacci.sqt geçerli JSON
(48 resolvedType:null, 20 isReachable:true); parser-stress regresyon temiz;
Faz 0 birim testleri geçiyor.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
saqut 2026-06-16 00:47:50 +03:00
parent 0b4c04cfb6
commit f4f57dd435
14 changed files with 83 additions and 33 deletions

View File

@ -37,6 +37,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "core/location.hpp" #include "core/location.hpp"
#include "tools.hpp" // jsonEscape — TEK tanım (tools.hpp); çakışmayı önler
// ============================================================================ // ============================================================================
// DiagLevel — Tanı seviyesi // DiagLevel — Tanı seviyesi
@ -65,22 +66,7 @@ inline const char* diagLevelNameTr(DiagLevel l) {
return "?"; return "?";
} }
// JSON string kaçışı (mesaj/ipucu tırnak veya satır sonu içerebilir) // NOT: jsonEscape() tools.hpp'de tanımlıdır (tek tanım — ODR çakışması olmaz).
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) // Diagnostic — Tek bir tanı (hata/uyarı/not/ipucu)

View File

@ -31,6 +31,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "core/location.hpp" #include "core/location.hpp"
#include "core/type.hpp"
#include "parser/token.hpp" #include "parser/token.hpp"
#include "tools.hpp" #include "tools.hpp"
@ -247,6 +248,47 @@ protected:
std::vector<ASTNode*> children; std::vector<ASTNode*> children;
}; };
// ============================================================================
// ExpressionNode — Değer Üreten Düğümlerin Tabanı (Faz 1, ADR-012)
// ============================================================================
//
// Bir DEĞER üreten her düğüm (Literal, Identifier, BinaryExpression, Call,
// Postfix, MemberAccess, IndexExpression) buradan türer. Bir ifadenin bir
// TİPİ vardır; analiz/optimizasyon alanları burada toplanır.
//
class ExpressionNode : public ASTNode {
public:
// TODO(faz-3): tip denetleyici doldurur. Şimdilik Error = "henüz çözülmedi".
Type resolvedType;
// TODO(faz-4): sabit katlama (constant folding) bayrağı.
bool isConstant = false;
// TODO(faz-4): foldedValue — katlanmış sabit değer (temsil Faz 4'te netleşir).
// resolvedType'ın JSON karşılığı (henüz çözülmemişse null gösterilir).
std::string resolvedTypeJson() const {
return resolvedType.isError() ? std::string("null") : resolvedType.toJson();
}
};
// ============================================================================
// StatementNode — Eylem/Kontrol Akışı Yürüten Düğümlerin Tabanı (Faz 1)
// ============================================================================
//
// Değer üretmeyen, bir iş/kontrol akışı yürüten her düğüm (Block, If, For,
// While, DoWhile, Return, Break, Continue, ExpressionStatement ve şimdilik
// VariableDecl) buradan türer. Tipi yoktur; akış-analizi alanları taşır.
//
// TODO(faz-1 gözden geçirme): VariableDecl/FunctionDecl/StructDecl'in tam
// sınıflandırması provizyonel — VariableDecl burada (blok içinde erişilebilirliğe
// tabi), Function/StructDecl doğrudan ASTNode altında kaldı.
//
class StatementNode : public ASTNode {
public:
// TODO(faz-3/4): erişilebilirlik (dead-code) analizi günceller.
bool isReachable = true;
};
// ============================================================================ // ============================================================================
// childrenToJson — Düğümün çocuklarını JSON array olarak yaz // childrenToJson — Düğümün çocuklarını JSON array olarak yaz
// ============================================================================ // ============================================================================

View File

@ -18,6 +18,7 @@ std::string BinaryExpressionNode::toJson(int depth) {
obj.add("operator", std::string(OPERATOR_MAP_REV.count(Operator) ? OPERATOR_MAP_REV.at(Operator) : "?")); obj.add("operator", std::string(OPERATOR_MAP_REV.count(Operator) ? OPERATOR_MAP_REV.at(Operator) : "?"));
if (Left) obj.addRaw("left", Left->toJson(depth + 1)); if (Left) obj.addRaw("left", Left->toJson(depth + 1));
if (Right) obj.addRaw("right", Right->toJson(depth + 1)); if (Right) obj.addRaw("right", Right->toJson(depth + 1));
obj.addRaw("resolvedType", resolvedTypeJson());
obj.addRaw("location", loc.toJson()); obj.addRaw("location", loc.toJson());
return obj.str(); return obj.str();
} }

View File

@ -3,7 +3,7 @@
#include "parser/ast_node.hpp" #include "parser/ast_node.hpp"
class BinaryExpressionNode : public ASTNode { class BinaryExpressionNode : public ExpressionNode {
public: public:
TokenType Operator; TokenType Operator;
ASTNode* Left = nullptr; ASTNode* Left = nullptr;

View File

@ -30,6 +30,7 @@ std::string VariableDeclNode::toJson(int depth) {
obj.add("kind", "VariableDecl"); obj.add("kind", "VariableDecl");
obj.add("name", name); obj.add("name", name);
obj.add("varType", varType); obj.add("varType", varType);
obj.add("isReachable", isReachable);
if (initExpr) obj.addRaw("init", initExpr->toJson(depth + 1)); if (initExpr) obj.addRaw("init", initExpr->toJson(depth + 1));
obj.addRaw("location", loc.toJson()); obj.addRaw("location", loc.toJson());
return obj.str(); return obj.str();

View File

@ -12,7 +12,7 @@ public:
std::string toJson(int depth = 0) override; std::string toJson(int depth = 0) override;
}; };
class VariableDeclNode : public ASTNode { class VariableDeclNode : public StatementNode {
public: public:
std::string varType; std::string varType;
std::string name; std::string name;

View File

@ -12,6 +12,7 @@ std::string PostfixNode::toJson(int depth) {
obj.add("kind", "Postfix"); obj.add("kind", "Postfix");
obj.add("operator", std::string(OPERATOR_MAP_REV.count(Operator) ? OPERATOR_MAP_REV.at(Operator) : "?")); obj.add("operator", std::string(OPERATOR_MAP_REV.count(Operator) ? OPERATOR_MAP_REV.at(Operator) : "?"));
if (operand) obj.addRaw("operand", operand->toJson(depth + 1)); if (operand) obj.addRaw("operand", operand->toJson(depth + 1));
obj.addRaw("resolvedType", resolvedTypeJson());
obj.addRaw("location", loc.toJson()); obj.addRaw("location", loc.toJson());
return obj.str(); return obj.str();
} }
@ -30,6 +31,7 @@ std::string CallExpressionNode::toJson(int depth) {
obj.addArray("arguments", [&]() { obj.addArray("arguments", [&]() {
for (auto* arg : arguments) obj.addItem(arg->toJson(depth + 2)); for (auto* arg : arguments) obj.addItem(arg->toJson(depth + 2));
}); });
obj.addRaw("resolvedType", resolvedTypeJson());
obj.addRaw("location", loc.toJson()); obj.addRaw("location", loc.toJson());
return obj.str(); return obj.str();
} }
@ -46,6 +48,7 @@ std::string MemberAccessNode::toJson(int depth) {
obj.add("member", member); obj.add("member", member);
obj.add("arrow", arrow); obj.add("arrow", arrow);
if (object) obj.addRaw("object", object->toJson(depth + 1)); if (object) obj.addRaw("object", object->toJson(depth + 1));
obj.addRaw("resolvedType", resolvedTypeJson());
obj.addRaw("location", loc.toJson()); obj.addRaw("location", loc.toJson());
return obj.str(); return obj.str();
} }
@ -62,6 +65,7 @@ std::string IndexExpressionNode::toJson(int depth) {
obj.add("kind", "IndexExpression"); obj.add("kind", "IndexExpression");
if (object) obj.addRaw("object", object->toJson(depth + 1)); if (object) obj.addRaw("object", object->toJson(depth + 1));
if (index) obj.addRaw("index", index->toJson(depth + 1)); if (index) obj.addRaw("index", index->toJson(depth + 1));
obj.addRaw("resolvedType", resolvedTypeJson());
obj.addRaw("location", loc.toJson()); obj.addRaw("location", loc.toJson());
return obj.str(); return obj.str();
} }

View File

@ -3,7 +3,7 @@
#include "parser/ast_node.hpp" #include "parser/ast_node.hpp"
class PostfixNode : public ASTNode { class PostfixNode : public ExpressionNode {
public: public:
ASTNode* operand = nullptr; ASTNode* operand = nullptr;
TokenType Operator; TokenType Operator;
@ -12,7 +12,7 @@ public:
std::string toJson(int depth = 0) override; std::string toJson(int depth = 0) override;
}; };
class CallExpressionNode : public ASTNode { class CallExpressionNode : public ExpressionNode {
public: public:
ASTNode* callee = nullptr; ASTNode* callee = nullptr;
std::vector<ASTNode*> arguments; std::vector<ASTNode*> arguments;
@ -21,7 +21,7 @@ public:
std::string toJson(int depth = 0) override; std::string toJson(int depth = 0) override;
}; };
class MemberAccessNode : public ASTNode { class MemberAccessNode : public ExpressionNode {
public: public:
ASTNode* object = nullptr; ASTNode* object = nullptr;
std::string member; std::string member;
@ -31,7 +31,7 @@ public:
std::string toJson(int depth = 0) override; std::string toJson(int depth = 0) override;
}; };
class IndexExpressionNode : public ASTNode { class IndexExpressionNode : public ExpressionNode {
public: public:
ASTNode* object = nullptr; ASTNode* object = nullptr;
ASTNode* index = nullptr; ASTNode* index = nullptr;

View File

@ -17,6 +17,7 @@ std::string IdentifierNode::toJson(int depth) {
ss << "{\n" ss << "{\n"
<< in << " \"kind\": \"Identifier\",\n" << in << " \"kind\": \"Identifier\",\n"
<< in << " \"name\": \"" << jsonEscape(name) << "\",\n" << in << " \"name\": \"" << jsonEscape(name) << "\",\n"
<< in << " \"resolvedType\": " << resolvedTypeJson() << ",\n"
<< in << " \"location\": " << loc.toJson() << "\n" << in << " \"location\": " << loc.toJson() << "\n"
<< in << "}"; << in << "}";
return ss.str(); return ss.str();

View File

@ -3,11 +3,16 @@
#include "parser/ast_node.hpp" #include "parser/ast_node.hpp"
class IdentifierNode : public ASTNode { struct Symbol; // TODO(faz-2): sembol tablosu (Symbol) tanımlandığında bağlanacak
class IdentifierNode : public ExpressionNode {
public: public:
Token* lexerToken = nullptr; Token* lexerToken = nullptr;
ParserToken parserToken; ParserToken parserToken;
// TODO(faz-2): isim çözümlemede sembol tablosundaki tanıma bağlanır.
Symbol* resolvedSymbol = nullptr;
IdentifierNode(); IdentifierNode();
void log(int indent = 0) override; void log(int indent = 0) override;
std::string toJson(int depth = 0) override; std::string toJson(int depth = 0) override;

View File

@ -28,6 +28,7 @@ std::string LiteralNode::toJson(int depth) {
if (literalType == LiteralType::FLOAT) { if (literalType == LiteralType::FLOAT) {
ss << ",\n" << in << " \"isFloat\": true"; ss << ",\n" << in << " \"isFloat\": true";
} }
ss << ",\n" << in << " \"resolvedType\": " << resolvedTypeJson();
ss << ",\n" << in << " \"location\": " << loc.toJson() << "\n" ss << ",\n" << in << " \"location\": " << loc.toJson() << "\n"
<< in << "}"; << in << "}";
return ss.str(); return ss.str();

View File

@ -3,7 +3,7 @@
#include "parser/ast_node.hpp" #include "parser/ast_node.hpp"
class LiteralNode : public ASTNode { class LiteralNode : public ExpressionNode {
public: public:
Token* lexerToken = nullptr; Token* lexerToken = nullptr;
ParserToken parserToken; ParserToken parserToken;

View File

@ -13,6 +13,7 @@ std::string BlockNode::toJson(int depth) {
obj.addArray("children", [&]() { obj.addArray("children", [&]() {
for (auto* child : children) obj.addItem(child->toJson(depth + 2)); for (auto* child : children) obj.addItem(child->toJson(depth + 2));
}); });
obj.add("isReachable", isReachable);
obj.addRaw("location", loc.toJson()); obj.addRaw("location", loc.toJson());
return obj.str(); return obj.str();
} }
@ -31,6 +32,7 @@ std::string IfStatementNode::toJson(int depth) {
if (condition) obj.addRaw("condition", condition->toJson(depth + 1)); if (condition) obj.addRaw("condition", condition->toJson(depth + 1));
if (thenBranch) obj.addRaw("then", thenBranch->toJson(depth + 1)); if (thenBranch) obj.addRaw("then", thenBranch->toJson(depth + 1));
if (elseBranch) obj.addRaw("else", elseBranch->toJson(depth + 1)); if (elseBranch) obj.addRaw("else", elseBranch->toJson(depth + 1));
obj.add("isReachable", isReachable);
obj.addRaw("location", loc.toJson()); obj.addRaw("location", loc.toJson());
return obj.str(); return obj.str();
} }
@ -47,6 +49,7 @@ std::string WhileStatementNode::toJson(int depth) {
obj.add("kind", "WhileStatement"); obj.add("kind", "WhileStatement");
if (condition) obj.addRaw("condition", condition->toJson(depth + 1)); if (condition) obj.addRaw("condition", condition->toJson(depth + 1));
if (body) obj.addRaw("body", body->toJson(depth + 1)); if (body) obj.addRaw("body", body->toJson(depth + 1));
obj.add("isReachable", isReachable);
obj.addRaw("location", loc.toJson()); obj.addRaw("location", loc.toJson());
return obj.str(); return obj.str();
} }
@ -67,6 +70,7 @@ std::string ForStatementNode::toJson(int depth) {
if (condition) obj.addRaw("condition", condition->toJson(depth + 1)); if (condition) obj.addRaw("condition", condition->toJson(depth + 1));
if (update) obj.addRaw("update", update->toJson(depth + 1)); if (update) obj.addRaw("update", update->toJson(depth + 1));
if (body) obj.addRaw("body", body->toJson(depth + 1)); if (body) obj.addRaw("body", body->toJson(depth + 1));
obj.add("isReachable", isReachable);
obj.addRaw("location", loc.toJson()); obj.addRaw("location", loc.toJson());
return obj.str(); return obj.str();
} }
@ -83,6 +87,7 @@ std::string DoWhileStatementNode::toJson(int depth) {
obj.add("kind", "DoWhileStatement"); obj.add("kind", "DoWhileStatement");
if (condition) obj.addRaw("condition", condition->toJson(depth + 1)); if (condition) obj.addRaw("condition", condition->toJson(depth + 1));
if (body) obj.addRaw("body", body->toJson(depth + 1)); if (body) obj.addRaw("body", body->toJson(depth + 1));
obj.add("isReachable", isReachable);
obj.addRaw("location", loc.toJson()); obj.addRaw("location", loc.toJson());
return obj.str(); return obj.str();
} }
@ -97,6 +102,7 @@ std::string ReturnStatementNode::toJson(int depth) {
JsonObject obj(depth); JsonObject obj(depth);
obj.add("kind", "ReturnStatement"); obj.add("kind", "ReturnStatement");
if (value) obj.addRaw("value", value->toJson(depth + 1)); if (value) obj.addRaw("value", value->toJson(depth + 1));
obj.add("isReachable", isReachable);
obj.addRaw("location", loc.toJson()); obj.addRaw("location", loc.toJson());
return obj.str(); return obj.str();
} }
@ -109,6 +115,7 @@ void BreakStatementNode::log(int indent) {
std::string BreakStatementNode::toJson(int depth) { std::string BreakStatementNode::toJson(int depth) {
JsonObject obj(depth); JsonObject obj(depth);
obj.add("kind", "BreakStatement"); obj.add("kind", "BreakStatement");
obj.add("isReachable", isReachable);
obj.addRaw("location", loc.toJson()); obj.addRaw("location", loc.toJson());
return obj.str(); return obj.str();
} }
@ -121,6 +128,7 @@ void ContinueStatementNode::log(int indent) {
std::string ContinueStatementNode::toJson(int depth) { std::string ContinueStatementNode::toJson(int depth) {
JsonObject obj(depth); JsonObject obj(depth);
obj.add("kind", "ContinueStatement"); obj.add("kind", "ContinueStatement");
obj.add("isReachable", isReachable);
obj.addRaw("location", loc.toJson()); obj.addRaw("location", loc.toJson());
return obj.str(); return obj.str();
} }
@ -135,6 +143,7 @@ std::string ExpressionStatementNode::toJson(int depth) {
JsonObject obj(depth); JsonObject obj(depth);
obj.add("kind", "ExpressionStatement"); obj.add("kind", "ExpressionStatement");
if (expression) obj.addRaw("expression", expression->toJson(depth + 1)); if (expression) obj.addRaw("expression", expression->toJson(depth + 1));
obj.add("isReachable", isReachable);
obj.addRaw("location", loc.toJson()); obj.addRaw("location", loc.toJson());
return obj.str(); return obj.str();
} }

View File

@ -3,14 +3,14 @@
#include "parser/ast_node.hpp" #include "parser/ast_node.hpp"
class BlockNode : public ASTNode { class BlockNode : public StatementNode {
public: public:
BlockNode(); BlockNode();
void log(int indent = 0) override; void log(int indent = 0) override;
std::string toJson(int depth = 0) override; std::string toJson(int depth = 0) override;
}; };
class IfStatementNode : public ASTNode { class IfStatementNode : public StatementNode {
public: public:
ASTNode* condition = nullptr; ASTNode* condition = nullptr;
ASTNode* thenBranch = nullptr; ASTNode* thenBranch = nullptr;
@ -20,7 +20,7 @@ public:
std::string toJson(int depth = 0) override; std::string toJson(int depth = 0) override;
}; };
class WhileStatementNode : public ASTNode { class WhileStatementNode : public StatementNode {
public: public:
ASTNode* condition = nullptr; ASTNode* condition = nullptr;
ASTNode* body = nullptr; ASTNode* body = nullptr;
@ -29,7 +29,7 @@ public:
std::string toJson(int depth = 0) override; std::string toJson(int depth = 0) override;
}; };
class ForStatementNode : public ASTNode { class ForStatementNode : public StatementNode {
public: public:
ASTNode* init = nullptr; ASTNode* init = nullptr;
ASTNode* condition = nullptr; ASTNode* condition = nullptr;
@ -40,7 +40,7 @@ public:
std::string toJson(int depth = 0) override; std::string toJson(int depth = 0) override;
}; };
class DoWhileStatementNode : public ASTNode { class DoWhileStatementNode : public StatementNode {
public: public:
ASTNode* condition = nullptr; ASTNode* condition = nullptr;
ASTNode* body = nullptr; ASTNode* body = nullptr;
@ -49,7 +49,7 @@ public:
std::string toJson(int depth = 0) override; std::string toJson(int depth = 0) override;
}; };
class ReturnStatementNode : public ASTNode { class ReturnStatementNode : public StatementNode {
public: public:
ASTNode* value = nullptr; ASTNode* value = nullptr;
ReturnStatementNode(); ReturnStatementNode();
@ -57,21 +57,21 @@ public:
std::string toJson(int depth = 0) override; std::string toJson(int depth = 0) override;
}; };
class BreakStatementNode : public ASTNode { class BreakStatementNode : public StatementNode {
public: public:
BreakStatementNode(); BreakStatementNode();
void log(int indent = 0) override; void log(int indent = 0) override;
std::string toJson(int depth = 0) override; std::string toJson(int depth = 0) override;
}; };
class ContinueStatementNode : public ASTNode { class ContinueStatementNode : public StatementNode {
public: public:
ContinueStatementNode(); ContinueStatementNode();
void log(int indent = 0) override; void log(int indent = 0) override;
std::string toJson(int depth = 0) override; std::string toJson(int depth = 0) override;
}; };
class ExpressionStatementNode : public ASTNode { class ExpressionStatementNode : public StatementNode {
public: public:
ASTNode* expression = nullptr; ASTNode* expression = nullptr;
ExpressionStatementNode(); ExpressionStatementNode();