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.
This commit is contained in:
saqut 2026-06-16 00:47:50 +03:00
parent b0219411c1
commit 290dcab5ac
14 changed files with 83 additions and 33 deletions

View File

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

View File

@ -31,6 +31,7 @@
#include <string>
#include <vector>
#include "core/location.hpp"
#include "core/type.hpp"
#include "parser/token.hpp"
#include "tools.hpp"
@ -247,6 +248,47 @@ protected:
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
// ============================================================================

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) : "?"));
if (Left) obj.addRaw("left", Left->toJson(depth + 1));
if (Right) obj.addRaw("right", Right->toJson(depth + 1));
obj.addRaw("resolvedType", resolvedTypeJson());
obj.addRaw("location", loc.toJson());
return obj.str();
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3,11 +3,16 @@
#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:
Token* lexerToken = nullptr;
ParserToken parserToken;
// TODO(faz-2): isim çözümlemede sembol tablosundaki tanıma bağlanır.
Symbol* resolvedSymbol = nullptr;
IdentifierNode();
void log(int indent = 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) {
ss << ",\n" << in << " \"isFloat\": true";
}
ss << ",\n" << in << " \"resolvedType\": " << resolvedTypeJson();
ss << ",\n" << in << " \"location\": " << loc.toJson() << "\n"
<< in << "}";
return ss.str();

View File

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

View File

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

View File

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