parent
477720eb5e
commit
04a29b2bf9
@ -1,30 +1,213 @@
|
|||||||
// 基于语法树的语义检查与名称绑定。
|
#ifndef SEMANTIC_ANALYSIS_H
|
||||||
#pragma once
|
#define SEMANTIC_ANALYSIS_H
|
||||||
|
|
||||||
|
#include "SymbolTable.h"
|
||||||
|
#include "SysYBaseVisitor.h"
|
||||||
|
#include "SysYParser.h"
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <any>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include "SysYParser.h"
|
// 错误信息结构体
|
||||||
|
struct ErrorMsg {
|
||||||
|
std::string msg;
|
||||||
|
int line;
|
||||||
|
int column;
|
||||||
|
ErrorMsg(std::string m, int l, int c) : msg(std::move(m)), line(l), column(c) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 前向声明
|
||||||
|
namespace antlr4 {
|
||||||
|
class ParserRuleContext;
|
||||||
|
namespace tree {
|
||||||
|
class ParseTree;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 语义/IR生成上下文核心类
|
||||||
|
class IRGenContext {
|
||||||
|
public:
|
||||||
|
// 错误管理
|
||||||
|
void RecordError(const ErrorMsg& err) { errors_.push_back(err); }
|
||||||
|
const std::vector<ErrorMsg>& GetErrors() const { return errors_; }
|
||||||
|
bool HasError() const { return !errors_.empty(); }
|
||||||
|
void ClearErrors() { errors_.clear(); }
|
||||||
|
|
||||||
|
// 类型绑定/查询 - 使用 void* 以兼容测试代码
|
||||||
|
void SetType(void* ctx, SymbolType type) {
|
||||||
|
node_type_map_[ctx] = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
SymbolType GetType(void* ctx) const {
|
||||||
|
auto it = node_type_map_.find(ctx);
|
||||||
|
return it == node_type_map_.end() ? SymbolType::TYPE_UNKNOWN : it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 常量值绑定/查询 - 使用 void* 以兼容测试代码
|
||||||
|
void SetConstVal(void* ctx, const std::any& val) {
|
||||||
|
const_val_map_[ctx] = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::any GetConstVal(void* ctx) const {
|
||||||
|
auto it = const_val_map_.find(ctx);
|
||||||
|
return it == const_val_map_.end() ? std::any() : it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 循环状态管理
|
||||||
|
void EnterLoop() { sym_table_.EnterLoop(); }
|
||||||
|
void ExitLoop() { sym_table_.ExitLoop(); }
|
||||||
|
bool InLoop() const { return sym_table_.InLoop(); }
|
||||||
|
|
||||||
|
// 类型判断工具函数
|
||||||
|
bool IsIntType(const std::any& val) const {
|
||||||
|
return val.type() == typeid(long) || val.type() == typeid(int);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsFloatType(const std::any& val) const {
|
||||||
|
return val.type() == typeid(double) || val.type() == typeid(float);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 当前函数返回类型
|
||||||
|
SymbolType GetCurrentFuncReturnType() const {
|
||||||
|
return current_func_ret_type_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetCurrentFuncReturnType(SymbolType type) {
|
||||||
|
current_func_ret_type_ = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 符号表访问
|
||||||
|
SymbolTable& GetSymbolTable() { return sym_table_; }
|
||||||
|
const SymbolTable& GetSymbolTable() const { return sym_table_; }
|
||||||
|
|
||||||
|
// 作用域管理
|
||||||
|
void EnterScope() { sym_table_.EnterScope(); }
|
||||||
|
void LeaveScope() { sym_table_.LeaveScope(); }
|
||||||
|
size_t GetScopeDepth() const { return sym_table_.GetScopeDepth(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
SymbolTable sym_table_;
|
||||||
|
std::unordered_map<void*, SymbolType> node_type_map_;
|
||||||
|
std::unordered_map<void*, std::any> const_val_map_;
|
||||||
|
std::vector<ErrorMsg> errors_;
|
||||||
|
SymbolType current_func_ret_type_ = SymbolType::TYPE_UNKNOWN;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 与现有 IRGen/主流程保持兼容的语义上下文占位。
|
||||||
class SemanticContext {
|
class SemanticContext {
|
||||||
public:
|
public:
|
||||||
void BindVarUse(SysYParser::VarContext* use,
|
void BindVarUse(const SysYParser::LValueContext* use,
|
||||||
SysYParser::VarDefContext* decl) {
|
SysYParser::VarDefContext* decl) {
|
||||||
var_uses_[use] = decl;
|
var_uses_[use] = decl;
|
||||||
}
|
}
|
||||||
|
|
||||||
SysYParser::VarDefContext* ResolveVarUse(
|
SysYParser::VarDefContext* ResolveVarUse(
|
||||||
const SysYParser::VarContext* use) const {
|
const SysYParser::LValueContext* use) const {
|
||||||
auto it = var_uses_.find(use);
|
auto it = var_uses_.find(use);
|
||||||
return it == var_uses_.end() ? nullptr : it->second;
|
return it == var_uses_.end() ? nullptr : it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unordered_map<const SysYParser::VarContext*,
|
std::unordered_map<const SysYParser::LValueContext*,
|
||||||
SysYParser::VarDefContext*>
|
SysYParser::VarDefContext*>
|
||||||
var_uses_;
|
var_uses_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 目前仅检查:
|
// 错误信息格式化工具函数
|
||||||
// - 变量先声明后使用
|
inline std::string FormatErrMsg(const std::string& msg, int line, int col) {
|
||||||
// - 局部变量不允许重复定义
|
std::ostringstream oss;
|
||||||
|
oss << "[行:" << line << ",列:" << col << "] " << msg;
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 语义分析访问器 - 继承自生成的基类
|
||||||
|
class SemaVisitor : public SysYBaseVisitor {
|
||||||
|
public:
|
||||||
|
explicit SemaVisitor(IRGenContext& ctx) : ir_ctx_(ctx) {}
|
||||||
|
|
||||||
|
// 必须实现的 ANTLR4 接口
|
||||||
|
std::any visit(antlr4::tree::ParseTree* tree) override {
|
||||||
|
if (tree) {
|
||||||
|
return tree->accept(this);
|
||||||
|
}
|
||||||
|
return std::any();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::any visitTerminal(antlr4::tree::TerminalNode* node) override {
|
||||||
|
return std::any();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::any visitErrorNode(antlr4::tree::ErrorNode* node) override {
|
||||||
|
if (node) {
|
||||||
|
int line = node->getSymbol()->getLine();
|
||||||
|
int col = node->getSymbol()->getCharPositionInLine() + 1;
|
||||||
|
ir_ctx_.RecordError(ErrorMsg("语法错误节点", line, col));
|
||||||
|
}
|
||||||
|
return std::any();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 核心访问方法
|
||||||
|
std::any visitCompUnit(SysYParser::CompUnitContext* ctx) override;
|
||||||
|
std::any visitDecl(SysYParser::DeclContext* ctx) override;
|
||||||
|
std::any visitConstDecl(SysYParser::ConstDeclContext* ctx) override;
|
||||||
|
std::any visitBtype(SysYParser::BtypeContext* ctx) override;
|
||||||
|
std::any visitConstDef(SysYParser::ConstDefContext* ctx) override;
|
||||||
|
std::any visitConstInitValue(SysYParser::ConstInitValueContext* ctx) override;
|
||||||
|
std::any visitVarDecl(SysYParser::VarDeclContext* ctx) override;
|
||||||
|
std::any visitVarDef(SysYParser::VarDefContext* ctx) override;
|
||||||
|
std::any visitInitValue(SysYParser::InitValueContext* ctx) override;
|
||||||
|
std::any visitFuncDef(SysYParser::FuncDefContext* ctx) override;
|
||||||
|
std::any visitFuncType(SysYParser::FuncTypeContext* ctx) override;
|
||||||
|
std::any visitFuncFParams(SysYParser::FuncFParamsContext* ctx) override;
|
||||||
|
std::any visitFuncFParam(SysYParser::FuncFParamContext* ctx) override;
|
||||||
|
std::any visitBlockStmt(SysYParser::BlockStmtContext* ctx) override;
|
||||||
|
std::any visitBlockItem(SysYParser::BlockItemContext* ctx) override;
|
||||||
|
std::any visitStmt(SysYParser::StmtContext* ctx) override;
|
||||||
|
std::any visitReturnStmt(SysYParser::ReturnStmtContext* ctx) override;
|
||||||
|
std::any visitExp(SysYParser::ExpContext* ctx) override;
|
||||||
|
std::any visitCond(SysYParser::CondContext* ctx) override;
|
||||||
|
std::any visitLValue(SysYParser::LValueContext* ctx) override;
|
||||||
|
std::any visitPrimaryExp(SysYParser::PrimaryExpContext* ctx) override;
|
||||||
|
std::any visitNumber(SysYParser::NumberContext* ctx) override;
|
||||||
|
std::any visitUnaryExp(SysYParser::UnaryExpContext* ctx) override;
|
||||||
|
std::any visitUnaryOp(SysYParser::UnaryOpContext* ctx) override;
|
||||||
|
std::any visitFuncRParams(SysYParser::FuncRParamsContext* ctx) override;
|
||||||
|
std::any visitMulExp(SysYParser::MulExpContext* ctx) override;
|
||||||
|
std::any visitAddExp(SysYParser::AddExpContext* ctx) override;
|
||||||
|
std::any visitRelExp(SysYParser::RelExpContext* ctx) override;
|
||||||
|
std::any visitEqExp(SysYParser::EqExpContext* ctx) override;
|
||||||
|
std::any visitLAndExp(SysYParser::LAndExpContext* ctx) override;
|
||||||
|
std::any visitLOrExp(SysYParser::LOrExpContext* ctx) override;
|
||||||
|
std::any visitConstExp(SysYParser::ConstExpContext* ctx) override;
|
||||||
|
|
||||||
|
// 通用子节点访问
|
||||||
|
std::any visitChildren(antlr4::tree::ParseTree* node) override {
|
||||||
|
std::any result;
|
||||||
|
if (node) {
|
||||||
|
for (auto* child : node->children) {
|
||||||
|
if (child) {
|
||||||
|
result = child->accept(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取上下文引用
|
||||||
|
IRGenContext& GetContext() { return ir_ctx_; }
|
||||||
|
const IRGenContext& GetContext() const { return ir_ctx_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
IRGenContext& ir_ctx_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 语义分析入口函数
|
||||||
|
void RunSemanticAnalysis(SysYParser::CompUnitContext* ctx, IRGenContext& ir_ctx);
|
||||||
|
|
||||||
|
// 兼容旧流程入口。
|
||||||
SemanticContext RunSema(SysYParser::CompUnitContext& comp_unit);
|
SemanticContext RunSema(SysYParser::CompUnitContext& comp_unit);
|
||||||
|
|
||||||
|
#endif // SEMANTIC_ANALYSIS_H
|
||||||
@ -1,17 +1,201 @@
|
|||||||
// 极简符号表:记录局部变量定义点。
|
#ifndef SYMBOL_TABLE_H
|
||||||
#pragma once
|
#define SYMBOL_TABLE_H
|
||||||
|
|
||||||
|
#include <any>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <stack>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include "SysYParser.h"
|
// 核心类型枚举
|
||||||
|
enum class SymbolType {
|
||||||
|
TYPE_UNKNOWN, // 未知类型
|
||||||
|
TYPE_INT, // 整型
|
||||||
|
TYPE_FLOAT, // 浮点型
|
||||||
|
TYPE_VOID, // 空类型
|
||||||
|
TYPE_ARRAY, // 数组类型
|
||||||
|
TYPE_FUNCTION // 函数类型
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取类型名称字符串
|
||||||
|
inline const char* SymbolTypeToString(SymbolType type) {
|
||||||
|
switch (type) {
|
||||||
|
case SymbolType::TYPE_INT: return "int";
|
||||||
|
case SymbolType::TYPE_FLOAT: return "float";
|
||||||
|
case SymbolType::TYPE_VOID: return "void";
|
||||||
|
case SymbolType::TYPE_ARRAY: return "array";
|
||||||
|
case SymbolType::TYPE_FUNCTION: return "function";
|
||||||
|
default: return "unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 变量信息结构体
|
||||||
|
struct VarInfo {
|
||||||
|
SymbolType type = SymbolType::TYPE_UNKNOWN;
|
||||||
|
bool is_const = false;
|
||||||
|
std::any const_val;
|
||||||
|
std::vector<int> array_dims; // 数组维度,空表示非数组
|
||||||
|
void* decl_ctx = nullptr; // 关联的语法节点
|
||||||
|
|
||||||
|
// 检查是否为数组类型
|
||||||
|
bool IsArray() const { return !array_dims.empty(); }
|
||||||
|
|
||||||
|
// 获取数组元素总数
|
||||||
|
int GetArrayElementCount() const {
|
||||||
|
int count = 1;
|
||||||
|
for (int dim : array_dims) {
|
||||||
|
count *= dim;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 函数信息结构体
|
||||||
|
struct FuncInfo {
|
||||||
|
SymbolType ret_type = SymbolType::TYPE_UNKNOWN;
|
||||||
|
std::string name;
|
||||||
|
std::vector<SymbolType> param_types; // 参数类型列表
|
||||||
|
void* decl_ctx = nullptr; // 关联的语法节点
|
||||||
|
|
||||||
|
// 检查参数匹配
|
||||||
|
bool CheckParams(const std::vector<SymbolType>& actual_params) const {
|
||||||
|
if (actual_params.size() != param_types.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < param_types.size(); ++i) {
|
||||||
|
if (param_types[i] != actual_params[i] &&
|
||||||
|
param_types[i] != SymbolType::TYPE_UNKNOWN &&
|
||||||
|
actual_params[i] != SymbolType::TYPE_UNKNOWN) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 作用域条目结构体
|
||||||
|
struct ScopeEntry {
|
||||||
|
// 变量符号表:符号名 -> (符号信息, 声明节点)
|
||||||
|
std::unordered_map<std::string, std::pair<VarInfo, void*>> var_symbols;
|
||||||
|
|
||||||
|
// 函数符号表:符号名 -> (函数信息, 声明节点)
|
||||||
|
std::unordered_map<std::string, std::pair<FuncInfo, void*>> func_symbols;
|
||||||
|
|
||||||
|
// 清空作用域
|
||||||
|
void Clear() {
|
||||||
|
var_symbols.clear();
|
||||||
|
func_symbols.clear();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 符号表核心类
|
||||||
class SymbolTable {
|
class SymbolTable {
|
||||||
public:
|
public:
|
||||||
void Add(const std::string& name, SysYParser::VarDefContext* decl);
|
// ========== 作用域管理 ==========
|
||||||
bool Contains(const std::string& name) const;
|
|
||||||
SysYParser::VarDefContext* Lookup(const std::string& name) const;
|
// 进入新作用域
|
||||||
|
void EnterScope();
|
||||||
|
|
||||||
|
// 离开当前作用域
|
||||||
|
void LeaveScope();
|
||||||
|
|
||||||
|
// 获取当前作用域深度
|
||||||
|
size_t GetScopeDepth() const { return scopes_.size(); }
|
||||||
|
|
||||||
|
// 检查作用域栈是否为空
|
||||||
|
bool IsEmpty() const { return scopes_.empty(); }
|
||||||
|
|
||||||
|
// ========== 变量符号管理 ==========
|
||||||
|
|
||||||
|
// 检查当前作用域是否包含指定变量
|
||||||
|
bool CurrentScopeHasVar(const std::string& name) const;
|
||||||
|
|
||||||
|
// 绑定变量到当前作用域
|
||||||
|
void BindVar(const std::string& name, const VarInfo& info, void* decl_ctx);
|
||||||
|
|
||||||
|
// 查找变量(从当前作用域向上遍历)
|
||||||
|
bool LookupVar(const std::string& name, VarInfo& out_info, void*& out_decl_ctx) const;
|
||||||
|
|
||||||
|
// 快速查找变量(不获取详细信息)
|
||||||
|
bool HasVar(const std::string& name) const {
|
||||||
|
VarInfo info;
|
||||||
|
void* ctx;
|
||||||
|
return LookupVar(name, info, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 函数符号管理 ==========
|
||||||
|
|
||||||
|
// 检查当前作用域是否包含指定函数
|
||||||
|
bool CurrentScopeHasFunc(const std::string& name) const;
|
||||||
|
|
||||||
|
// 绑定函数到当前作用域
|
||||||
|
void BindFunc(const std::string& name, const FuncInfo& info, void* decl_ctx);
|
||||||
|
|
||||||
|
// 查找函数(从当前作用域向上遍历)
|
||||||
|
bool LookupFunc(const std::string& name, FuncInfo& out_info, void*& out_decl_ctx) const;
|
||||||
|
|
||||||
|
// 快速查找函数(不获取详细信息)
|
||||||
|
bool HasFunc(const std::string& name) const {
|
||||||
|
FuncInfo info;
|
||||||
|
void* ctx;
|
||||||
|
return LookupFunc(name, info, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 循环状态管理 ==========
|
||||||
|
|
||||||
|
// 进入循环
|
||||||
|
void EnterLoop();
|
||||||
|
|
||||||
|
// 离开循环
|
||||||
|
void ExitLoop();
|
||||||
|
|
||||||
|
// 检查是否在循环内
|
||||||
|
bool InLoop() const;
|
||||||
|
|
||||||
|
// 获取循环嵌套深度
|
||||||
|
int GetLoopDepth() const { return loop_depth_; }
|
||||||
|
|
||||||
|
// ========== 辅助功能 ==========
|
||||||
|
|
||||||
|
// 清空所有作用域和状态
|
||||||
|
void Clear();
|
||||||
|
|
||||||
|
// 获取当前作用域中所有变量名
|
||||||
|
std::vector<std::string> GetCurrentScopeVarNames() const;
|
||||||
|
|
||||||
|
// 获取当前作用域中所有函数名
|
||||||
|
std::vector<std::string> GetCurrentScopeFuncNames() const;
|
||||||
|
|
||||||
|
// 调试:打印符号表内容
|
||||||
|
void Dump() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unordered_map<std::string, SysYParser::VarDefContext*> table_;
|
// 作用域栈
|
||||||
|
std::stack<ScopeEntry> scopes_;
|
||||||
|
|
||||||
|
// 循环嵌套深度
|
||||||
|
int loop_depth_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 类型兼容性检查函数
|
||||||
|
inline bool IsTypeCompatible(SymbolType expected, SymbolType actual) {
|
||||||
|
if (expected == SymbolType::TYPE_UNKNOWN || actual == SymbolType::TYPE_UNKNOWN) {
|
||||||
|
return true; // 未知类型视为兼容
|
||||||
|
}
|
||||||
|
|
||||||
|
// 基本类型兼容规则
|
||||||
|
if (expected == actual) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// int 可以隐式转换为 float
|
||||||
|
if (expected == SymbolType::TYPE_FLOAT && actual == SymbolType::TYPE_INT) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SYMBOL_TABLE_H
|
||||||
@ -1,200 +1,224 @@
|
|||||||
#include "sem/Sema.h"
|
#include "sem/Sema.h"
|
||||||
|
|
||||||
#include <any>
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "SysYBaseVisitor.h"
|
|
||||||
#include "sem/SymbolTable.h"
|
|
||||||
#include "utils/Log.h"
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
std::string GetLValueName(SysYParser::LValueContext& lvalue) {
|
SymbolType ParseType(const std::string& text) {
|
||||||
if (!lvalue.ID()) {
|
if (text == "int") {
|
||||||
throw std::runtime_error(FormatError("sema", "非法左值"));
|
return SymbolType::TYPE_INT;
|
||||||
}
|
}
|
||||||
return lvalue.ID()->getText();
|
if (text == "float") {
|
||||||
}
|
return SymbolType::TYPE_FLOAT;
|
||||||
|
}
|
||||||
class SemaVisitor final : public SysYBaseVisitor {
|
if (text == "void") {
|
||||||
public:
|
return SymbolType::TYPE_VOID;
|
||||||
std::any visitCompUnit(SysYParser::CompUnitContext* ctx) override {
|
|
||||||
if (!ctx) {
|
|
||||||
throw std::runtime_error(FormatError("sema", "缺少编译单元"));
|
|
||||||
}
|
|
||||||
auto* func = ctx->funcDef();
|
|
||||||
if (!func || !func->blockStmt()) {
|
|
||||||
throw std::runtime_error(FormatError("sema", "缺少 main 函数定义"));
|
|
||||||
}
|
|
||||||
if (!func->ID() || func->ID()->getText() != "main") {
|
|
||||||
throw std::runtime_error(FormatError("sema", "缺少 main 函数定义"));
|
|
||||||
}
|
|
||||||
func->accept(this);
|
|
||||||
if (!seen_return_) {
|
|
||||||
throw std::runtime_error(
|
|
||||||
FormatError("sema", "main 函数必须包含 return 语句"));
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
return SymbolType::TYPE_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
std::any visitFuncDef(SysYParser::FuncDefContext* ctx) override {
|
} // namespace
|
||||||
if (!ctx || !ctx->blockStmt()) {
|
|
||||||
throw std::runtime_error(FormatError("sema", "缺少 main 函数定义"));
|
std::any SemaVisitor::visitCompUnit(SysYParser::CompUnitContext* ctx) {
|
||||||
}
|
return visitChildren(ctx);
|
||||||
if (!ctx->funcType() || !ctx->funcType()->INT()) {
|
}
|
||||||
throw std::runtime_error(FormatError("sema", "当前仅支持 int main"));
|
|
||||||
}
|
std::any SemaVisitor::visitDecl(SysYParser::DeclContext* ctx) {
|
||||||
const auto& items = ctx->blockStmt()->blockItem();
|
return visitChildren(ctx);
|
||||||
if (items.empty()) {
|
}
|
||||||
throw std::runtime_error(
|
|
||||||
FormatError("sema", "main 函数不能为空,且必须以 return 结束"));
|
std::any SemaVisitor::visitConstDecl(SysYParser::ConstDeclContext* ctx) {
|
||||||
}
|
return visitChildren(ctx);
|
||||||
ctx->blockStmt()->accept(this);
|
}
|
||||||
return {};
|
|
||||||
|
std::any SemaVisitor::visitBtype(SysYParser::BtypeContext* ctx) {
|
||||||
|
return visitChildren(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::any SemaVisitor::visitConstDef(SysYParser::ConstDefContext* ctx) {
|
||||||
|
return visitChildren(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::any SemaVisitor::visitConstInitValue(SysYParser::ConstInitValueContext* ctx) {
|
||||||
|
return visitChildren(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::any SemaVisitor::visitVarDecl(SysYParser::VarDeclContext* ctx) {
|
||||||
|
return visitChildren(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::any SemaVisitor::visitVarDef(SysYParser::VarDefContext* ctx) {
|
||||||
|
return visitChildren(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::any SemaVisitor::visitInitValue(SysYParser::InitValueContext* ctx) {
|
||||||
|
return visitChildren(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::any SemaVisitor::visitFuncDef(SysYParser::FuncDefContext* ctx) {
|
||||||
|
SymbolType ret_type = SymbolType::TYPE_UNKNOWN;
|
||||||
|
if (ctx && ctx->funcType()) {
|
||||||
|
ret_type = ParseType(ctx->funcType()->getText());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::any visitBlockStmt(SysYParser::BlockStmtContext* ctx) override {
|
ir_ctx_.SetCurrentFuncReturnType(ret_type);
|
||||||
if (!ctx) {
|
return visitChildren(ctx);
|
||||||
throw std::runtime_error(FormatError("sema", "缺少语句块"));
|
}
|
||||||
}
|
|
||||||
const auto& items = ctx->blockItem();
|
std::any SemaVisitor::visitFuncType(SysYParser::FuncTypeContext* ctx) {
|
||||||
for (size_t i = 0; i < items.size(); ++i) {
|
return visitChildren(ctx);
|
||||||
auto* item = items[i];
|
}
|
||||||
if (!item) {
|
|
||||||
continue;
|
std::any SemaVisitor::visitFuncFParams(SysYParser::FuncFParamsContext* ctx) {
|
||||||
}
|
return visitChildren(ctx);
|
||||||
if (seen_return_) {
|
}
|
||||||
throw std::runtime_error(
|
|
||||||
FormatError("sema", "return 必须是 main 函数中的最后一条语句"));
|
std::any SemaVisitor::visitFuncFParam(SysYParser::FuncFParamContext* ctx) {
|
||||||
}
|
return visitChildren(ctx);
|
||||||
current_item_index_ = i;
|
}
|
||||||
total_items_ = items.size();
|
|
||||||
item->accept(this);
|
std::any SemaVisitor::visitBlockStmt(SysYParser::BlockStmtContext* ctx) {
|
||||||
}
|
ir_ctx_.EnterScope();
|
||||||
|
std::any result = visitChildren(ctx);
|
||||||
|
ir_ctx_.LeaveScope();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::any SemaVisitor::visitBlockItem(SysYParser::BlockItemContext* ctx) {
|
||||||
|
return visitChildren(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::any SemaVisitor::visitStmt(SysYParser::StmtContext* ctx) {
|
||||||
|
if (!ctx) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::any visitBlockItem(SysYParser::BlockItemContext* ctx) override {
|
if (ctx->WHILE()) {
|
||||||
if (!ctx) {
|
ir_ctx_.EnterLoop();
|
||||||
throw std::runtime_error(FormatError("sema", "暂不支持的语句或声明"));
|
std::any result = visitChildren(ctx);
|
||||||
}
|
ir_ctx_.ExitLoop();
|
||||||
if (ctx->decl()) {
|
return result;
|
||||||
ctx->decl()->accept(this);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
if (ctx->stmt()) {
|
|
||||||
ctx->stmt()->accept(this);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
throw std::runtime_error(FormatError("sema", "暂不支持的语句或声明"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::any visitDecl(SysYParser::DeclContext* ctx) override {
|
if (ctx->BREAK() && !ir_ctx_.InLoop()) {
|
||||||
if (!ctx) {
|
ir_ctx_.RecordError(
|
||||||
throw std::runtime_error(FormatError("sema", "非法变量声明"));
|
ErrorMsg("break 只能出现在循环语句中", ctx->getStart()->getLine(),
|
||||||
}
|
ctx->getStart()->getCharPositionInLine() + 1));
|
||||||
if (!ctx->btype() || !ctx->btype()->INT()) {
|
|
||||||
throw std::runtime_error(FormatError("sema", "当前仅支持局部 int 变量声明"));
|
|
||||||
}
|
|
||||||
auto* var_def = ctx->varDef();
|
|
||||||
if (!var_def || !var_def->lValue()) {
|
|
||||||
throw std::runtime_error(FormatError("sema", "非法变量声明"));
|
|
||||||
}
|
|
||||||
const std::string name = GetLValueName(*var_def->lValue());
|
|
||||||
if (table_.Contains(name)) {
|
|
||||||
throw std::runtime_error(FormatError("sema", "重复定义变量: " + name));
|
|
||||||
}
|
|
||||||
if (auto* init = var_def->initValue()) {
|
|
||||||
if (!init->exp()) {
|
|
||||||
throw std::runtime_error(FormatError("sema", "当前不支持聚合初始化"));
|
|
||||||
}
|
|
||||||
init->exp()->accept(this);
|
|
||||||
}
|
|
||||||
table_.Add(name, var_def);
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::any visitStmt(SysYParser::StmtContext* ctx) override {
|
if (ctx->CONTINUE() && !ir_ctx_.InLoop()) {
|
||||||
if (!ctx || !ctx->returnStmt()) {
|
ir_ctx_.RecordError(
|
||||||
throw std::runtime_error(FormatError("sema", "暂不支持的语句或声明"));
|
ErrorMsg("continue 只能出现在循环语句中", ctx->getStart()->getLine(),
|
||||||
}
|
ctx->getStart()->getCharPositionInLine() + 1));
|
||||||
ctx->returnStmt()->accept(this);
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::any visitReturnStmt(SysYParser::ReturnStmtContext* ctx) override {
|
return visitChildren(ctx);
|
||||||
if (!ctx || !ctx->exp()) {
|
}
|
||||||
throw std::runtime_error(FormatError("sema", "return 缺少表达式"));
|
|
||||||
}
|
|
||||||
ctx->exp()->accept(this);
|
|
||||||
seen_return_ = true;
|
|
||||||
if (current_item_index_ + 1 != total_items_) {
|
|
||||||
throw std::runtime_error(
|
|
||||||
FormatError("sema", "return 必须是 main 函数中的最后一条语句"));
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::any visitParenExp(SysYParser::ParenExpContext* ctx) override {
|
std::any SemaVisitor::visitReturnStmt(SysYParser::ReturnStmtContext* ctx) {
|
||||||
if (!ctx || !ctx->exp()) {
|
if (!ctx) {
|
||||||
throw std::runtime_error(FormatError("sema", "非法括号表达式"));
|
|
||||||
}
|
|
||||||
ctx->exp()->accept(this);
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::any visitVarExp(SysYParser::VarExpContext* ctx) override {
|
if (ctx->exp() && ir_ctx_.GetCurrentFuncReturnType() == SymbolType::TYPE_VOID) {
|
||||||
if (!ctx || !ctx->var()) {
|
ir_ctx_.RecordError(
|
||||||
throw std::runtime_error(FormatError("sema", "非法变量表达式"));
|
ErrorMsg("void 函数不应返回表达式", ctx->getStart()->getLine(),
|
||||||
}
|
ctx->getStart()->getCharPositionInLine() + 1));
|
||||||
ctx->var()->accept(this);
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::any visitNumberExp(SysYParser::NumberExpContext* ctx) override {
|
if (!ctx->exp() &&
|
||||||
if (!ctx || !ctx->number() || !ctx->number()->ILITERAL()) {
|
ir_ctx_.GetCurrentFuncReturnType() != SymbolType::TYPE_VOID &&
|
||||||
throw std::runtime_error(FormatError("sema", "当前仅支持整数字面量"));
|
ir_ctx_.GetCurrentFuncReturnType() != SymbolType::TYPE_UNKNOWN) {
|
||||||
}
|
ir_ctx_.RecordError(
|
||||||
return {};
|
ErrorMsg("非 void 函数 return 必须带表达式", ctx->getStart()->getLine(),
|
||||||
|
ctx->getStart()->getCharPositionInLine() + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::any visitAdditiveExp(SysYParser::AdditiveExpContext* ctx) override {
|
return visitChildren(ctx);
|
||||||
if (!ctx || !ctx->exp(0) || !ctx->exp(1)) {
|
}
|
||||||
throw std::runtime_error(FormatError("sema", "暂不支持的表达式形式"));
|
|
||||||
}
|
std::any SemaVisitor::visitExp(SysYParser::ExpContext* ctx) {
|
||||||
ctx->exp(0)->accept(this);
|
return visitChildren(ctx);
|
||||||
ctx->exp(1)->accept(this);
|
}
|
||||||
|
|
||||||
|
std::any SemaVisitor::visitCond(SysYParser::CondContext* ctx) {
|
||||||
|
return visitChildren(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::any SemaVisitor::visitLValue(SysYParser::LValueContext* ctx) {
|
||||||
|
return visitChildren(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::any SemaVisitor::visitPrimaryExp(SysYParser::PrimaryExpContext* ctx) {
|
||||||
|
return visitChildren(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::any SemaVisitor::visitNumber(SysYParser::NumberContext* ctx) {
|
||||||
|
if (!ctx) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::any visitVar(SysYParser::VarContext* ctx) override {
|
if (ctx->ILITERAL()) {
|
||||||
if (!ctx || !ctx->ID()) {
|
ir_ctx_.SetType(ctx, SymbolType::TYPE_INT);
|
||||||
throw std::runtime_error(FormatError("sema", "非法变量引用"));
|
ir_ctx_.SetConstVal(ctx, std::any(0L));
|
||||||
}
|
} else if (ctx->FLITERAL()) {
|
||||||
const std::string name = ctx->ID()->getText();
|
ir_ctx_.SetType(ctx, SymbolType::TYPE_FLOAT);
|
||||||
auto* decl = table_.Lookup(name);
|
ir_ctx_.SetConstVal(ctx, std::any(0.0));
|
||||||
if (!decl) {
|
|
||||||
throw std::runtime_error(FormatError("sema", "使用了未定义的变量: " + name));
|
|
||||||
}
|
|
||||||
sema_.BindVarUse(ctx, decl);
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SemanticContext TakeSemanticContext() { return std::move(sema_); }
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
std::any SemaVisitor::visitUnaryExp(SysYParser::UnaryExpContext* ctx) {
|
||||||
SymbolTable table_;
|
return visitChildren(ctx);
|
||||||
SemanticContext sema_;
|
}
|
||||||
bool seen_return_ = false;
|
|
||||||
size_t current_item_index_ = 0;
|
|
||||||
size_t total_items_ = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace
|
std::any SemaVisitor::visitUnaryOp(SysYParser::UnaryOpContext* ctx) {
|
||||||
|
return visitChildren(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::any SemaVisitor::visitFuncRParams(SysYParser::FuncRParamsContext* ctx) {
|
||||||
|
return visitChildren(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::any SemaVisitor::visitMulExp(SysYParser::MulExpContext* ctx) {
|
||||||
|
return visitChildren(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::any SemaVisitor::visitAddExp(SysYParser::AddExpContext* ctx) {
|
||||||
|
return visitChildren(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::any SemaVisitor::visitRelExp(SysYParser::RelExpContext* ctx) {
|
||||||
|
return visitChildren(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::any SemaVisitor::visitEqExp(SysYParser::EqExpContext* ctx) {
|
||||||
|
return visitChildren(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::any SemaVisitor::visitLAndExp(SysYParser::LAndExpContext* ctx) {
|
||||||
|
return visitChildren(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::any SemaVisitor::visitLOrExp(SysYParser::LOrExpContext* ctx) {
|
||||||
|
return visitChildren(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::any SemaVisitor::visitConstExp(SysYParser::ConstExpContext* ctx) {
|
||||||
|
return visitChildren(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RunSemanticAnalysis(SysYParser::CompUnitContext* ctx, IRGenContext& ir_ctx) {
|
||||||
|
if (!ctx) {
|
||||||
|
throw std::invalid_argument("CompUnitContext is null");
|
||||||
|
}
|
||||||
|
SemaVisitor visitor(ir_ctx);
|
||||||
|
visitor.visit(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
SemanticContext RunSema(SysYParser::CompUnitContext& comp_unit) {
|
SemanticContext RunSema(SysYParser::CompUnitContext& comp_unit) {
|
||||||
SemaVisitor visitor;
|
IRGenContext ctx;
|
||||||
comp_unit.accept(&visitor);
|
RunSemanticAnalysis(&comp_unit, ctx);
|
||||||
return visitor.TakeSemanticContext();
|
return SemanticContext();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,17 +1,164 @@
|
|||||||
// 维护局部变量声明的注册与查找。
|
#include "../../include/sem/SymbolTable.h"
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
#include "sem/SymbolTable.h"
|
// 进入新作用域
|
||||||
|
void SymbolTable::EnterScope() {
|
||||||
|
scopes_.push(ScopeEntry());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 离开当前作用域
|
||||||
|
void SymbolTable::LeaveScope() {
|
||||||
|
if (scopes_.empty()) {
|
||||||
|
throw std::runtime_error("SymbolTable Error: 作用域栈为空,无法退出");
|
||||||
|
}
|
||||||
|
scopes_.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绑定变量到当前作用域
|
||||||
|
void SymbolTable::BindVar(const std::string& name, const VarInfo& info, void* decl_ctx) {
|
||||||
|
if (CurrentScopeHasVar(name)) {
|
||||||
|
throw std::runtime_error("变量'" + name + "'在当前作用域重复定义");
|
||||||
|
}
|
||||||
|
scopes_.top().var_symbols[name] = {info, decl_ctx};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绑定函数到当前作用域
|
||||||
|
void SymbolTable::BindFunc(const std::string& name, const FuncInfo& info, void* decl_ctx) {
|
||||||
|
if (CurrentScopeHasFunc(name)) {
|
||||||
|
throw std::runtime_error("函数'" + name + "'在当前作用域重复定义");
|
||||||
|
}
|
||||||
|
scopes_.top().func_symbols[name] = {info, decl_ctx};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找变量(从当前作用域向上遍历)
|
||||||
|
bool SymbolTable::LookupVar(const std::string& name, VarInfo& out_info, void*& out_decl_ctx) const {
|
||||||
|
if (scopes_.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto temp_stack = scopes_;
|
||||||
|
while (!temp_stack.empty()) {
|
||||||
|
auto& scope = temp_stack.top();
|
||||||
|
auto it = scope.var_symbols.find(name);
|
||||||
|
if (it != scope.var_symbols.end()) {
|
||||||
|
out_info = it->second.first;
|
||||||
|
out_decl_ctx = it->second.second;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
temp_stack.pop();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找函数(从当前作用域向上遍历,通常函数在全局作用域)
|
||||||
|
bool SymbolTable::LookupFunc(const std::string& name, FuncInfo& out_info, void*& out_decl_ctx) const {
|
||||||
|
if (scopes_.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto temp_stack = scopes_;
|
||||||
|
while (!temp_stack.empty()) {
|
||||||
|
auto& scope = temp_stack.top();
|
||||||
|
auto it = scope.func_symbols.find(name);
|
||||||
|
if (it != scope.func_symbols.end()) {
|
||||||
|
out_info = it->second.first;
|
||||||
|
out_decl_ctx = it->second.second;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
temp_stack.pop();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void SymbolTable::Add(const std::string& name,
|
// 检查当前作用域是否包含指定变量
|
||||||
SysYParser::VarDefContext* decl) {
|
bool SymbolTable::CurrentScopeHasVar(const std::string& name) const {
|
||||||
table_[name] = decl;
|
if (scopes_.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return scopes_.top().var_symbols.count(name) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SymbolTable::Contains(const std::string& name) const {
|
// 检查当前作用域是否包含指定函数
|
||||||
return table_.find(name) != table_.end();
|
bool SymbolTable::CurrentScopeHasFunc(const std::string& name) const {
|
||||||
|
if (scopes_.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return scopes_.top().func_symbols.count(name) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
SysYParser::VarDefContext* SymbolTable::Lookup(const std::string& name) const {
|
// 进入循环
|
||||||
auto it = table_.find(name);
|
void SymbolTable::EnterLoop() {
|
||||||
return it == table_.end() ? nullptr : it->second;
|
loop_depth_++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 离开循环
|
||||||
|
void SymbolTable::ExitLoop() {
|
||||||
|
if (loop_depth_ > 0) loop_depth_--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否在循环内
|
||||||
|
bool SymbolTable::InLoop() const {
|
||||||
|
return loop_depth_ > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空所有作用域和状态
|
||||||
|
void SymbolTable::Clear() {
|
||||||
|
while (!scopes_.empty()) {
|
||||||
|
scopes_.pop();
|
||||||
|
}
|
||||||
|
loop_depth_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取当前作用域中所有变量名
|
||||||
|
std::vector<std::string> SymbolTable::GetCurrentScopeVarNames() const {
|
||||||
|
std::vector<std::string> names;
|
||||||
|
if (!scopes_.empty()) {
|
||||||
|
for (const auto& pair : scopes_.top().var_symbols) {
|
||||||
|
names.push_back(pair.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取当前作用域中所有函数名
|
||||||
|
std::vector<std::string> SymbolTable::GetCurrentScopeFuncNames() const {
|
||||||
|
std::vector<std::string> names;
|
||||||
|
if (!scopes_.empty()) {
|
||||||
|
for (const auto& pair : scopes_.top().func_symbols) {
|
||||||
|
names.push_back(pair.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调试:打印符号表内容
|
||||||
|
void SymbolTable::Dump() const {
|
||||||
|
std::cout << "符号表内容 (作用域深度: " << scopes_.size() << "):\n";
|
||||||
|
int scope_idx = 0;
|
||||||
|
auto temp_stack = scopes_;
|
||||||
|
|
||||||
|
while (!temp_stack.empty()) {
|
||||||
|
std::cout << "\n作用域 " << scope_idx++ << ":\n";
|
||||||
|
auto& scope = temp_stack.top();
|
||||||
|
|
||||||
|
std::cout << " 变量:\n";
|
||||||
|
for (const auto& var_pair : scope.var_symbols) {
|
||||||
|
const VarInfo& info = var_pair.second.first;
|
||||||
|
std::cout << " " << var_pair.first << ": "
|
||||||
|
<< SymbolTypeToString(info.type)
|
||||||
|
<< (info.is_const ? " (const)" : "")
|
||||||
|
<< (info.IsArray() ? " [数组]" : "")
|
||||||
|
<< "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << " 函数:\n";
|
||||||
|
for (const auto& func_pair : scope.func_symbols) {
|
||||||
|
const FuncInfo& info = func_pair.second.first;
|
||||||
|
std::cout << " " << func_pair.first << ": "
|
||||||
|
<< SymbolTypeToString(info.ret_type) << " ("
|
||||||
|
<< info.param_types.size() << " 个参数)\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
temp_stack.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in new issue