fix(ast): 规范AST输出
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <iosfwd>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -66,5 +67,7 @@ struct CompUnit {
|
|||||||
|
|
||||||
// 调试打印
|
// 调试打印
|
||||||
void PrintAST(const CompUnit& cu);
|
void PrintAST(const CompUnit& cu);
|
||||||
|
// 导出 AST 为 Graphviz DOT。
|
||||||
|
void PrintASTDot(const CompUnit& cu, std::ostream& os);
|
||||||
|
|
||||||
} // namespace ast
|
} // namespace ast
|
||||||
|
|||||||
@@ -3,70 +3,194 @@
|
|||||||
#include "ast/AstNodes.h"
|
#include "ast/AstNodes.h"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <ostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace ast {
|
namespace ast {
|
||||||
|
|
||||||
static void PrintExpr(const Expr* expr);
|
|
||||||
|
|
||||||
static void PrintIndent(int depth) {
|
static void PrintIndent(int depth) {
|
||||||
for (int i = 0; i < depth; ++i) std::cout << " ";
|
for (int i = 0; i < depth; ++i) std::cout << " ";
|
||||||
}
|
}
|
||||||
|
|
||||||
static void PrintExpr(const Expr* expr) {
|
static void PrintLine(int depth, const std::string& text) {
|
||||||
|
PrintIndent(depth);
|
||||||
|
std::cout << text << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DumpExpr(const Expr* expr, int depth) {
|
||||||
|
if (!expr) {
|
||||||
|
PrintLine(depth, "NullExpr");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (auto num = dynamic_cast<const NumberExpr*>(expr)) {
|
if (auto num = dynamic_cast<const NumberExpr*>(expr)) {
|
||||||
std::cout << num->value;
|
PrintLine(depth, "NumberExpr value=" + std::to_string(num->value));
|
||||||
} else if (auto var = dynamic_cast<const VarExpr*>(expr)) {
|
return;
|
||||||
std::cout << var->name;
|
}
|
||||||
} else if (auto bin = dynamic_cast<const BinaryExpr*>(expr)) {
|
if (auto var = dynamic_cast<const VarExpr*>(expr)) {
|
||||||
std::cout << "(";
|
PrintLine(depth, "VarExpr name=" + var->name);
|
||||||
PrintExpr(bin->lhs.get());
|
return;
|
||||||
|
}
|
||||||
|
if (auto bin = dynamic_cast<const BinaryExpr*>(expr)) {
|
||||||
const char* op = "?";
|
const char* op = "?";
|
||||||
switch (bin->op) {
|
switch (bin->op) {
|
||||||
case BinaryOp::Add:
|
case BinaryOp::Add:
|
||||||
op = "+";
|
op = "Add";
|
||||||
break;
|
break;
|
||||||
case BinaryOp::Sub:
|
case BinaryOp::Sub:
|
||||||
op = "-";
|
op = "Sub";
|
||||||
break;
|
break;
|
||||||
case BinaryOp::Mul:
|
case BinaryOp::Mul:
|
||||||
op = "*";
|
op = "Mul";
|
||||||
break;
|
break;
|
||||||
case BinaryOp::Div:
|
case BinaryOp::Div:
|
||||||
op = "/";
|
op = "Div";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
std::cout << " " << op << " ";
|
PrintLine(depth, std::string("BinaryExpr op=") + op);
|
||||||
PrintExpr(bin->rhs.get());
|
DumpExpr(bin->lhs.get(), depth + 1);
|
||||||
std::cout << ")";
|
DumpExpr(bin->rhs.get(), depth + 1);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PrintLine(depth, "UnknownExpr");
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrintAST(const CompUnit& cu) {
|
void PrintAST(const CompUnit& cu) {
|
||||||
if (!cu.func) return;
|
PrintLine(0, "CompUnit");
|
||||||
std::cout << "func " << cu.func->name << " () {\n";
|
if (!cu.func) {
|
||||||
const auto& body = cu.func->body;
|
PrintLine(1, "<empty>");
|
||||||
if (!body) {
|
|
||||||
std::cout << "}\n";
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PrintLine(1, "FuncDef name=" + cu.func->name);
|
||||||
|
const auto& body = cu.func->body;
|
||||||
|
if (!body) {
|
||||||
|
PrintLine(2, "Block <null>");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PrintLine(2, "Block");
|
||||||
for (const auto& decl : body->varDecls) {
|
for (const auto& decl : body->varDecls) {
|
||||||
PrintIndent(1);
|
PrintLine(3, "VarDecl name=" + decl->name);
|
||||||
std::cout << "var " << decl->name;
|
|
||||||
if (decl->init) {
|
if (decl->init) {
|
||||||
std::cout << " = ";
|
PrintLine(4, "Init");
|
||||||
PrintExpr(decl->init.get());
|
DumpExpr(decl->init.get(), 5);
|
||||||
}
|
}
|
||||||
std::cout << ";\n";
|
|
||||||
}
|
}
|
||||||
for (const auto& stmt : body->stmts) {
|
for (const auto& stmt : body->stmts) {
|
||||||
if (auto ret = dynamic_cast<ReturnStmt*>(stmt.get())) {
|
if (auto ret = dynamic_cast<ReturnStmt*>(stmt.get())) {
|
||||||
PrintIndent(1);
|
PrintLine(3, "ReturnStmt");
|
||||||
std::cout << "return ";
|
DumpExpr(ret->value.get(), 4);
|
||||||
PrintExpr(ret->value.get());
|
} else {
|
||||||
std::cout << ";\n";
|
PrintLine(3, "UnknownStmt");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::cout << "}\n";
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
std::string EscapeDotLabel(const std::string& s) {
|
||||||
|
std::string out;
|
||||||
|
out.reserve(s.size());
|
||||||
|
for (char c : s) {
|
||||||
|
if (c == '\\' || c == '"') out.push_back('\\');
|
||||||
|
out.push_back(c);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
class DotWriter {
|
||||||
|
public:
|
||||||
|
explicit DotWriter(std::ostream& os) : os_(os) {}
|
||||||
|
|
||||||
|
int AddNode(const std::string& label) {
|
||||||
|
int id = next_id_++;
|
||||||
|
os_ << " n" << id << " [label=\"" << EscapeDotLabel(label) << "\"];\n";
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddEdge(int from, int to) { os_ << " n" << from << " -> n" << to << ";\n"; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::ostream& os_;
|
||||||
|
int next_id_ = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
int EmitExpr(const Expr* expr, DotWriter& dot) {
|
||||||
|
if (!expr) return dot.AddNode("NullExpr");
|
||||||
|
|
||||||
|
if (auto num = dynamic_cast<const NumberExpr*>(expr)) {
|
||||||
|
return dot.AddNode("Number(" + std::to_string(num->value) + ")");
|
||||||
|
}
|
||||||
|
if (auto var = dynamic_cast<const VarExpr*>(expr)) {
|
||||||
|
return dot.AddNode("Var(" + var->name + ")");
|
||||||
|
}
|
||||||
|
if (auto bin = dynamic_cast<const BinaryExpr*>(expr)) {
|
||||||
|
const char* op = "?";
|
||||||
|
switch (bin->op) {
|
||||||
|
case BinaryOp::Add:
|
||||||
|
op = "Add";
|
||||||
|
break;
|
||||||
|
case BinaryOp::Sub:
|
||||||
|
op = "Sub";
|
||||||
|
break;
|
||||||
|
case BinaryOp::Mul:
|
||||||
|
op = "Mul";
|
||||||
|
break;
|
||||||
|
case BinaryOp::Div:
|
||||||
|
op = "Div";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
int id = dot.AddNode(std::string("Binary(") + op + ")");
|
||||||
|
int lhs = EmitExpr(bin->lhs.get(), dot);
|
||||||
|
int rhs = EmitExpr(bin->rhs.get(), dot);
|
||||||
|
dot.AddEdge(id, lhs);
|
||||||
|
dot.AddEdge(id, rhs);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
return dot.AddNode("UnknownExpr");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void PrintASTDot(const CompUnit& cu, std::ostream& os) {
|
||||||
|
os << "digraph AST {\n";
|
||||||
|
os << " rankdir=TB;\n";
|
||||||
|
os << " node [shape=box, fontname=\"Courier\"];\n";
|
||||||
|
|
||||||
|
DotWriter dot(os);
|
||||||
|
int cu_id = dot.AddNode("CompUnit");
|
||||||
|
if (cu.func) {
|
||||||
|
int func_id = dot.AddNode("FuncDef(" + cu.func->name + ")");
|
||||||
|
dot.AddEdge(cu_id, func_id);
|
||||||
|
if (cu.func->body) {
|
||||||
|
int block_id = dot.AddNode("Block");
|
||||||
|
dot.AddEdge(func_id, block_id);
|
||||||
|
|
||||||
|
for (const auto& decl : cu.func->body->varDecls) {
|
||||||
|
int decl_id = dot.AddNode("VarDecl(" + decl->name + ")");
|
||||||
|
dot.AddEdge(block_id, decl_id);
|
||||||
|
if (decl->init) {
|
||||||
|
int init_id = EmitExpr(decl->init.get(), dot);
|
||||||
|
dot.AddEdge(decl_id, init_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& stmt : cu.func->body->stmts) {
|
||||||
|
if (auto ret = dynamic_cast<ReturnStmt*>(stmt.get())) {
|
||||||
|
int ret_id = dot.AddNode("Return");
|
||||||
|
dot.AddEdge(block_id, ret_id);
|
||||||
|
int value_id = EmitExpr(ret->value.get(), dot);
|
||||||
|
dot.AddEdge(ret_id, value_id);
|
||||||
|
} else {
|
||||||
|
int stmt_id = dot.AddNode("Stmt");
|
||||||
|
dot.AddEdge(block_id, stmt_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
os << "}\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ast
|
} // namespace ast
|
||||||
|
|||||||
11
src/main.cpp
11
src/main.cpp
@@ -1,5 +1,7 @@
|
|||||||
#include <exception>
|
#include <exception>
|
||||||
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
#include "frontend/AntlrDriver.h"
|
#include "frontend/AntlrDriver.h"
|
||||||
#include "frontend/AstBuilder.h"
|
#include "frontend/AstBuilder.h"
|
||||||
@@ -20,8 +22,15 @@ int main(int argc, char** argv) {
|
|||||||
auto antlr = ParseFileWithAntlr(opts.input);
|
auto antlr = ParseFileWithAntlr(opts.input);
|
||||||
auto ast = BuildAst(antlr.tree);
|
auto ast = BuildAst(antlr.tree);
|
||||||
ast::PrintAST(*ast); // 调试 AST
|
ast::PrintAST(*ast); // 调试 AST
|
||||||
|
if (!opts.ast_dot_output.empty()) {
|
||||||
|
std::ofstream dot_out(opts.ast_dot_output);
|
||||||
|
if (!dot_out) {
|
||||||
|
throw std::runtime_error("无法写入 AST DOT 文件: " + opts.ast_dot_output);
|
||||||
|
}
|
||||||
|
ast::PrintASTDot(*ast, dot_out);
|
||||||
|
}
|
||||||
ast = RunSema(std::move(ast));
|
ast = RunSema(std::move(ast));
|
||||||
// IR 生成:当前使用 IRGenDriver.cpp 里的最小参考实现,
|
// IR 生成:当前使用 IRGenDriver.cpp 里的最小实现,
|
||||||
// 同学们可以在 irgen/ 目录下按提示自行完善或替换实现。
|
// 同学们可以在 irgen/ 目录下按提示自行完善或替换实现。
|
||||||
auto module = GenerateIR(*ast);
|
auto module = GenerateIR(*ast);
|
||||||
|
|
||||||
|
|||||||
@@ -5,15 +5,15 @@
|
|||||||
|
|
||||||
#include "utils/CLI.h"
|
#include "utils/CLI.h"
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
#include <string>
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
CLIOptions ParseCLI(int argc, char** argv) {
|
CLIOptions ParseCLI(int argc, char** argv) {
|
||||||
CLIOptions opt;
|
CLIOptions opt;
|
||||||
|
|
||||||
if (argc <= 1) {
|
if (argc <= 1) {
|
||||||
throw std::runtime_error("用法: compiler [--help] <input.sy>");
|
throw std::runtime_error("用法: compiler [--help] [--ast-dot <file.dot>] <input.sy>");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 1; i < argc; ++i) {
|
for (int i = 1; i < argc; ++i) {
|
||||||
@@ -23,6 +23,14 @@ CLIOptions ParseCLI(int argc, char** argv) {
|
|||||||
return opt;
|
return opt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (std::strcmp(arg, "--ast-dot") == 0) {
|
||||||
|
if (i + 1 >= argc) {
|
||||||
|
throw std::runtime_error("参数错误: --ast-dot 需要一个输出路径");
|
||||||
|
}
|
||||||
|
opt.ast_dot_output = argv[++i];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (arg[0] == '-') {
|
if (arg[0] == '-') {
|
||||||
throw std::runtime_error(std::string("未知参数: ") + arg +
|
throw std::runtime_error(std::string("未知参数: ") + arg +
|
||||||
"(使用 --help 查看用法)");
|
"(使用 --help 查看用法)");
|
||||||
@@ -35,7 +43,7 @@ CLIOptions ParseCLI(int argc, char** argv) {
|
|||||||
opt.input = arg;
|
opt.input = arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opt.input.empty()) {
|
if (opt.input.empty() && !opt.show_help) {
|
||||||
throw std::runtime_error("缺少输入文件:请提供 <input.sy>(使用 --help 查看用法)");
|
throw std::runtime_error("缺少输入文件:请提供 <input.sy>(使用 --help 查看用法)");
|
||||||
}
|
}
|
||||||
return opt;
|
return opt;
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
// 简易命令行解析:仅支持输入文件路径。
|
// 简易命令行解析:支持帮助、输入文件和 AST DOT 导出。
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
struct CLIOptions {
|
struct CLIOptions {
|
||||||
std::string input;
|
std::string input;
|
||||||
|
std::string ast_dot_output;
|
||||||
bool show_help = false;
|
bool show_help = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -10,10 +10,11 @@ void PrintHelp(std::ostream& os) {
|
|||||||
os << "SysY Compiler (课程实验最小可运行示例)\n"
|
os << "SysY Compiler (课程实验最小可运行示例)\n"
|
||||||
<< "\n"
|
<< "\n"
|
||||||
<< "用法:\n"
|
<< "用法:\n"
|
||||||
<< " compiler [--help] <input.sy>\n"
|
<< " compiler [--help] [--ast-dot <file.dot>] <input.sy>\n"
|
||||||
<< "\n"
|
<< "\n"
|
||||||
<< "选项:\n"
|
<< "选项:\n"
|
||||||
<< " -h, --help 打印帮助信息并退出\n"
|
<< " -h, --help 打印帮助信息并退出\n"
|
||||||
|
<< " --ast-dot <path> 导出 AST Graphviz DOT 到指定文件\n"
|
||||||
<< "\n"
|
<< "\n"
|
||||||
<< "说明:\n"
|
<< "说明:\n"
|
||||||
<< " - 当前默认将 IR 输出到标准输出,可使用重定向写入文件:\n"
|
<< " - 当前默认将 IR 输出到标准输出,可使用重定向写入文件:\n"
|
||||||
|
|||||||
Reference in New Issue
Block a user