309 lines
12 KiB
C++
309 lines
12 KiB
C++
// ============================================================================
|
||
// saQut — Sabit Katlama (Constant Folding) Pass (ADR-009)
|
||
//
|
||
// BinaryExpression(Literal, OP, Literal) → Literal
|
||
// Sadece tam sayı sabitleri katlanır; kayan nokta şimdilik dışarıda.
|
||
// Derleme zamanı sıfıra bölme: W002 uyarısı verilir, katlama yapılmaz.
|
||
// ============================================================================
|
||
|
||
#ifndef SAQUT_OPT_CONSTANT_FOLDING
|
||
#define SAQUT_OPT_CONSTANT_FOLDING
|
||
|
||
#include <string>
|
||
#include "opt/optimization_pass.hpp"
|
||
#include "parser/nodes/binary_expr.hpp"
|
||
#include "parser/nodes/literal.hpp"
|
||
#include "parser/nodes/identifier.hpp"
|
||
#include "parser/nodes/statements.hpp"
|
||
#include "parser/nodes/expressions.hpp"
|
||
#include "parser/nodes/declarations.hpp"
|
||
#include "parser/nodes/program.hpp"
|
||
#include "diagnostic/diagnostic_engine.hpp"
|
||
|
||
class ConstantFoldingPass : public OptimizationPass {
|
||
public:
|
||
explicit ConstantFoldingPass(DiagnosticEngine& diag) : diag_(diag) {}
|
||
|
||
bool run(ASTNode* root, SymbolTable*) override {
|
||
changed_ = false;
|
||
fold(root);
|
||
return changed_;
|
||
}
|
||
|
||
const std::string& name() const override {
|
||
static std::string n = "constant-folding";
|
||
return n;
|
||
}
|
||
|
||
private:
|
||
DiagnosticEngine& diag_;
|
||
bool changed_ = false;
|
||
|
||
// ── Literal değer okuma ──────────────────────────────────────────────────
|
||
static bool isIntLit(ASTNode* node) {
|
||
auto* lit = dynamic_cast<LiteralNode*>(node);
|
||
return lit && lit->literalType == LiteralType::INTEGER;
|
||
}
|
||
|
||
static int getIntVal(LiteralNode* lit) {
|
||
if (lit->hasDirectValue) return lit->directIntValue;
|
||
if (lit->parserToken.token) return std::stoi(lit->parserToken.token->token);
|
||
return 0;
|
||
}
|
||
|
||
// ── Operatör hesaplama ───────────────────────────────────────────────────
|
||
static bool canFoldOp(TokenType op) {
|
||
switch (op) {
|
||
case TokenType::PLUS:
|
||
case TokenType::MINUS:
|
||
case TokenType::STAR:
|
||
case TokenType::SLASH:
|
||
case TokenType::PERCENT:
|
||
case TokenType::EQUAL_EQUAL:
|
||
case TokenType::BANG_EQUAL:
|
||
case TokenType::LESS:
|
||
case TokenType::GREATER:
|
||
case TokenType::LESS_EQUAL:
|
||
case TokenType::GREATER_EQUAL:
|
||
case TokenType::AMPERSAND_AMPERSAND:
|
||
case TokenType::PIPE_PIPE:
|
||
return true;
|
||
default:
|
||
return false;
|
||
}
|
||
}
|
||
|
||
static int computeOp(TokenType op, int l, int r) {
|
||
switch (op) {
|
||
case TokenType::PLUS: return l + r;
|
||
case TokenType::MINUS: return l - r;
|
||
case TokenType::STAR: return l * r;
|
||
case TokenType::SLASH: return l / r;
|
||
case TokenType::PERCENT: return l % r;
|
||
case TokenType::EQUAL_EQUAL: return l == r ? 1 : 0;
|
||
case TokenType::BANG_EQUAL: return l != r ? 1 : 0;
|
||
case TokenType::LESS: return l < r ? 1 : 0;
|
||
case TokenType::GREATER: return l > r ? 1 : 0;
|
||
case TokenType::LESS_EQUAL: return l <= r ? 1 : 0;
|
||
case TokenType::GREATER_EQUAL: return l >= r ? 1 : 0;
|
||
case TokenType::AMPERSAND_AMPERSAND: return (l && r) ? 1 : 0;
|
||
case TokenType::PIPE_PIPE: return (l || r) ? 1 : 0;
|
||
default: return 0;
|
||
}
|
||
}
|
||
|
||
// ── Sentetik literal oluştur ─────────────────────────────────────────────
|
||
static LiteralNode* makeFoldedLit(int value, const SourceLocation& loc, const Type& type) {
|
||
auto* lit = new LiteralNode();
|
||
lit->loc = loc;
|
||
lit->literalType = LiteralType::INTEGER;
|
||
lit->hasDirectValue = true;
|
||
lit->directIntValue = value;
|
||
lit->resolvedType = type;
|
||
lit->isConstant = true;
|
||
return lit;
|
||
}
|
||
|
||
// ── Ana ziyaretçi: bottom-up, pointer-by-reference ───────────────────────
|
||
// Dönüş değeri: aynı node veya yerine geçen yeni node.
|
||
// Folding yapıldıysa orijinal node silinir, yeni node döndürülür.
|
||
ASTNode* fold(ASTNode* node) {
|
||
if (!node) return nullptr;
|
||
|
||
switch (node->kind) {
|
||
|
||
// ── İfadeler ────────────────────────────────────────────────────────
|
||
case ASTKind::BinaryExpression: {
|
||
auto* bin = static_cast<BinaryExpressionNode*>(node);
|
||
|
||
// Alt ağaçları önce kat
|
||
ASTNode* newLeft = fold(bin->Left);
|
||
if (newLeft != bin->Left) { bin->Left = newLeft; if (newLeft) newLeft->parent = bin; }
|
||
|
||
ASTNode* newRight = fold(bin->Right);
|
||
if (newRight != bin->Right) { bin->Right = newRight; if (newRight) newRight->parent = bin; }
|
||
|
||
// İki taraf da tam sayı sabiti ve operatör kesilebilir mi?
|
||
if (!isIntLit(bin->Left) || !isIntLit(bin->Right)) return bin;
|
||
if (!canFoldOp(bin->Operator)) return bin;
|
||
|
||
auto* llit = static_cast<LiteralNode*>(bin->Left);
|
||
auto* rlit = static_cast<LiteralNode*>(bin->Right);
|
||
int lv = getIntVal(llit);
|
||
int rv = getIntVal(rlit);
|
||
|
||
// W002: Derleme zamanı sıfıra bölme
|
||
if ((bin->Operator == TokenType::SLASH || bin->Operator == TokenType::PERCENT) && rv == 0) {
|
||
Diagnostic d;
|
||
d.level = DiagLevel::Warning;
|
||
d.code = "W002";
|
||
d.loc = bin->loc;
|
||
d.message = "Derleme zamanı sıfıra bölme — katlama yapılmadı";
|
||
diag_.report(d);
|
||
return bin; // fold etme
|
||
}
|
||
|
||
int result = computeOp(bin->Operator, lv, rv);
|
||
LiteralNode* lit = makeFoldedLit(result, bin->loc, bin->resolvedType);
|
||
|
||
delete bin->Left;
|
||
delete bin->Right;
|
||
delete bin;
|
||
changed_ = true;
|
||
return lit;
|
||
}
|
||
|
||
case ASTKind::Postfix: {
|
||
auto* pf = static_cast<PostfixNode*>(node);
|
||
ASTNode* newOp = fold(pf->operand);
|
||
if (newOp != pf->operand) { pf->operand = newOp; if (newOp) newOp->parent = pf; }
|
||
return pf;
|
||
}
|
||
|
||
case ASTKind::Call: {
|
||
auto* call = static_cast<CallExpressionNode*>(node);
|
||
if (call->callee) {
|
||
ASTNode* nc = fold(call->callee);
|
||
if (nc != call->callee) { call->callee = nc; if (nc) nc->parent = call; }
|
||
}
|
||
for (auto*& arg : call->arguments) {
|
||
ASTNode* na = fold(arg);
|
||
if (na != arg) { arg = na; if (na) na->parent = call; }
|
||
}
|
||
return call;
|
||
}
|
||
|
||
case ASTKind::MemberAccess: {
|
||
auto* ma = static_cast<MemberAccessNode*>(node);
|
||
ASTNode* no = fold(ma->object);
|
||
if (no != ma->object) { ma->object = no; if (no) no->parent = ma; }
|
||
return ma;
|
||
}
|
||
|
||
case ASTKind::IndexExpression: {
|
||
auto* ix = static_cast<IndexExpressionNode*>(node);
|
||
ASTNode* no = fold(ix->object);
|
||
if (no != ix->object) { ix->object = no; if (no) no->parent = ix; }
|
||
ASTNode* ni = fold(ix->index);
|
||
if (ni != ix->index) { ix->index = ni; if (ni) ni->parent = ix; }
|
||
return ix;
|
||
}
|
||
|
||
// Literal ve Identifier değişmez
|
||
case ASTKind::Literal:
|
||
case ASTKind::Identifier:
|
||
return node;
|
||
|
||
// ── Deyimler (çocuklara iner) ────────────────────────────────────────
|
||
case ASTKind::ExpressionStatement: {
|
||
auto* es = static_cast<ExpressionStatementNode*>(node);
|
||
if (es->expression) {
|
||
ASTNode* ne = fold(es->expression);
|
||
if (ne != es->expression) { es->expression = ne; if (ne) ne->parent = es; }
|
||
}
|
||
return es;
|
||
}
|
||
|
||
case ASTKind::ReturnStatement: {
|
||
auto* rs = static_cast<ReturnStatementNode*>(node);
|
||
if (rs->value) {
|
||
ASTNode* nv = fold(rs->value);
|
||
if (nv != rs->value) { rs->value = nv; if (nv) nv->parent = rs; }
|
||
}
|
||
return rs;
|
||
}
|
||
|
||
case ASTKind::VariableDecl: {
|
||
auto* vd = static_cast<VariableDeclNode*>(node);
|
||
if (vd->initExpr) {
|
||
ASTNode* ni = fold(vd->initExpr);
|
||
if (ni != vd->initExpr) { vd->initExpr = ni; if (ni) ni->parent = vd; }
|
||
}
|
||
for (auto*& ch : vd->getChildren()) {
|
||
ASTNode* nc = fold(ch);
|
||
if (nc != ch) { ch = nc; if (nc) nc->parent = vd; }
|
||
}
|
||
return vd;
|
||
}
|
||
|
||
case ASTKind::IfStatement: {
|
||
auto* ifs = static_cast<IfStatementNode*>(node);
|
||
if (ifs->condition) {
|
||
ASTNode* nc = fold(ifs->condition);
|
||
if (nc != ifs->condition) { ifs->condition = nc; if (nc) nc->parent = ifs; }
|
||
}
|
||
if (ifs->thenBranch) fold(ifs->thenBranch);
|
||
if (ifs->elseBranch) fold(ifs->elseBranch);
|
||
return ifs;
|
||
}
|
||
|
||
case ASTKind::WhileStatement: {
|
||
auto* ws = static_cast<WhileStatementNode*>(node);
|
||
if (ws->condition) {
|
||
ASTNode* nc = fold(ws->condition);
|
||
if (nc != ws->condition) { ws->condition = nc; if (nc) nc->parent = ws; }
|
||
}
|
||
if (ws->body) fold(ws->body);
|
||
return ws;
|
||
}
|
||
|
||
case ASTKind::ForStatement: {
|
||
auto* fs = static_cast<ForStatementNode*>(node);
|
||
if (fs->init) { ASTNode* n = fold(fs->init); if (n != fs->init) { fs->init = n; if (n) n->parent = fs; } }
|
||
if (fs->condition) { ASTNode* n = fold(fs->condition); if (n != fs->condition) { fs->condition = n; if (n) n->parent = fs; } }
|
||
if (fs->update) { ASTNode* n = fold(fs->update); if (n != fs->update) { fs->update = n; if (n) n->parent = fs; } }
|
||
if (fs->body) fold(fs->body);
|
||
return fs;
|
||
}
|
||
|
||
case ASTKind::DoWhileStatement: {
|
||
auto* dw = static_cast<DoWhileStatementNode*>(node);
|
||
if (dw->body) fold(dw->body);
|
||
if (dw->condition) {
|
||
ASTNode* nc = fold(dw->condition);
|
||
if (nc != dw->condition) { dw->condition = nc; if (nc) nc->parent = dw; }
|
||
}
|
||
return dw;
|
||
}
|
||
|
||
// ── Blok ve üst düzey ────────────────────────────────────────────────
|
||
case ASTKind::Block: {
|
||
auto* blk = static_cast<BlockNode*>(node);
|
||
for (auto*& ch : blk->getChildren()) {
|
||
ASTNode* nc = fold(ch);
|
||
if (nc != ch) { ch = nc; if (nc) nc->parent = blk; }
|
||
}
|
||
return blk;
|
||
}
|
||
|
||
case ASTKind::FunctionDecl: {
|
||
auto* fn = static_cast<FunctionDeclNode*>(node);
|
||
for (auto*& ch : fn->getChildren()) {
|
||
ASTNode* nc = fold(ch);
|
||
if (nc != ch) { ch = nc; if (nc) nc->parent = fn; }
|
||
}
|
||
return fn;
|
||
}
|
||
|
||
case ASTKind::Program: {
|
||
auto* prog = static_cast<ProgramNode*>(node);
|
||
for (auto*& ch : prog->getChildren()) {
|
||
ASTNode* nc = fold(ch);
|
||
if (nc != ch) { ch = nc; if (nc) nc->parent = prog; }
|
||
}
|
||
return prog;
|
||
}
|
||
|
||
default:
|
||
// Bilinmeyen veya işlenmeyen node — olduğu gibi bırak
|
||
for (auto*& ch : node->getChildren()) {
|
||
ASTNode* nc = fold(ch);
|
||
if (nc != ch) { ch = nc; if (nc) nc->parent = node; }
|
||
}
|
||
return node;
|
||
}
|
||
}
|
||
};
|
||
|
||
#endif // SAQUT_OPT_CONSTANT_FOLDING
|