符号表(Symbol Tables)是编译器和解释器中的一个关键组件,用于存储代码中的所有符号及其对应的类型和属性。符号表在语法分析和语义分析阶段起着至关重要的作用,帮助编译器或解释器理解代码中的变量、函数、类型等符号的定义和使用。本文将详细介绍符号表的实现和使用,包括解题思路、示例代码和性能分析。
1. 符号表的基本概念
符号表是一个存储符号及其属性的数据结构。每个符号可以是一个变量、函数、类型等。符号表通常包含符号的名称、类型、作用域、存储位置等信息。
2. 符号表的常规实现
2.1 符号类(Symbol Class)
符号类是符号表中的基本单元,用于存储符号的名称和类型。我们可以定义一个基类 Symbol
,并为不同的符号类型(如变量、内置类型)定义继承自 Symbol
的子类。
#pragma once
#include <string>
#include <memory>
class Symbol;
typedef std::shared_ptr<Symbol> SymbolPtr;
typedef Symbol* const SymbolRawPtr;
class Symbol {
protected:
std::string symbolName;
SymbolPtr symbolType;
public:
virtual SymbolRawPtr getType() = 0;
std::string getName() const { return symbolName; }
};
2.2 内置类型符号(BuiltinTypeSymbol)
内置类型符号表示预定义的类型,如 INTEGER
和 REAL
。这些类型在符号表中作为常量存在。
#pragma once
#include "Symbol/Symbol.h"
class BuiltinTypeSymbol : public Symbol {
public:
BuiltinTypeSymbol(std::string sName) : symbolName(sName) {}
SymbolRawPtr getType() override { return this; }
};
2.3 变量符号(VarSymbol)
变量符号表示代码中的变量,存储变量的名称和类型。
#pragma once
#include "Symbol/Symbol.h"
class VarSymbol : public Symbol {
public:
VarSymbol(std::string varName, SymbolPtr varType) : symbolName(varName), symbolType(varType) {}
SymbolRawPtr getType() override { return symbolType; }
};
2.4 符号表类(SymbolTable)
符号表类负责管理所有符号,提供定义和查找符号的方法。
#pragma once
#include <map>
#include <string>
#include "Symbol/Symbol.h"
class SymbolTable {
private:
std::map<std::string, SymbolPtr> symbols;
public:
SymbolTable() {
define(std::make_shared<BuiltinTypeSymbol>("INTEGER"));
define(std::make_shared<BuiltinTypeSymbol>("REAL"));
}
void define(SymbolPtr symbolPtr) {
symbols[symbolPtr->getName()] = symbolPtr;
}
SymbolPtr lookup(std::string symbolName) {
if (symbols.find(symbolName) == symbols.end()) {
throw "Symbol not found";
}
return symbols[symbolName];
}
std::map<std::string, SymbolPtr> getAll() const {
return symbols;
}
};
3. 符号表的使用
3.1 符号表构建器(SymbolTableBuilder)
符号表构建器用于遍历抽象语法树(AST),并在符号表中定义符号。它继承自 NodeVisitor
,为每种节点类型实现 visit
方法。
#pragma once
#include "NodeVisitor/NodeVisitor.h"
#include "ASTNodes/ProgramNode.h"
#include "Symbol/SymbolTable.h"
class SymbolTableBuilder : public NodeVisitor {
private:
SymbolTable symTable;
public:
SymbolTableBuilder() {}
SymbolTable getSymbolTable() const { return symTable; }
void visit(ProgramNode* node) override {
// 处理程序节点
}
void visit(VarDeclNode* node) override {
auto typeName = std::get<std::string>(node->getType()->getTokenPtr()->getValue());
auto typeSymbol = symTable.lookup(typeName);
std::string varName = std::get<std::string>(node->getVar()->getTokenPtr()->getValue());
symTable.define(std::make_shared<VarSymbol>(varName, typeSymbol));
}
// 其他节点的 visit 方法
};
4. 示例代码
4.1 构建符号表
int main() {
std::string text = R"(
PROGRAM Part8;
VAR
number : INTEGER;
a, b : INTEGER;
y : REAL;
BEGIN {Part8}
number := 2;
a := number ;
b := 10 * a + 10 * number DIV 4;
y := 20 / 7 + 3.14
END. {Part8}
)";
ASTParser parser(text);
SymbolTableBuilder symtableBuilder;
symtableBuilder.visit(parser->parse());
for (const auto& pair : symtableBuilder.getSymbolTable().getAll()) {
std::cout << pair.first << " : " << pair.second->getType()->getName() << std::endl;
}
return 0;
}
4.2 输出结果
INTEGER : INTEGER
REAL : REAL
a : INTEGER
b : INTEGER
number : INTEGER
y : REAL
5. 性能分析
符号表的性能主要取决于查找和插入操作的复杂度。使用哈希表实现的符号表,查找和插入操作的平均复杂度为 O(1)。在最坏情况下,哈希表可能会退化为链表,复杂度为 O(n)。为了优化性能,可以使用平衡二叉搜索树或跳表等数据结构。
6. 符号表的不同实现方式及特点
符号表(Symbol Table)是编译器和解释器中的一个关键组件,用于存储代码中的所有符号及其对应的类型和属性。符号表在语法分析和语义分析阶段起着至关重要的作用,帮助编译器或解释器理解代码中的变量、函数、类型等符号的定义和使用。本文将详细介绍符号表的不同实现方式及其特点,包括它们的解题思路、示例代码和性能分析。
1 无序数组实现(Unordered Array Implementation)
1.1 实现方式
仅维护一个数组,数组中的每个元素是一个键值对,键是符号的名称,值是符号的属性。
1.2 性能
-
搜索操作:在最坏情况下,时间复杂度为 O(n)。
-
插入操作:在最坏情况下,时间复杂度为 O(1)。
-
删除操作:在最坏情况下,时间复杂度为 O(n)。
1.3 示例代码
#include <vector>
#include <string>
#include <iostream>
struct Symbol {
std::string name;
std::string type;
};
class SymbolTable {
private:
std::vector<Symbol> symbols;
public:
void insert(const std::string& name, const std::string& type) {
symbols.push_back({name, type});
}
std::string lookup(const std::string& name) {
for (const auto& symbol : symbols) {
if (symbol.name == name) {
return symbol.type;