A Tour of the Cool Support Code
介绍了写Cool
编译器过程中我们可以使用的数据结构。
本文是我阅读这篇指导的笔记,会随着我做作业的进度慢慢地更新。
Introduction
数据结构包括如下:
- 链表
linked list
- 字符串表
string table
- 符号表
symbol table
- 其他暂时还没在作业中用到的东西。
阅读原文应该与源码配合,源码在include
和assignment
文件夹中的对应作业下。
Lists
list.h
中实现了简单的链表。
- 成员:
T *head
和List<T>* tail
,可以看出这是一个递归的定义。 hd()
返回首元素tl()
返回尾元素list_map()
对list
中的每个元素执行一个操作,该操作由调用方给出。list_print()
打印整个链表list_length()
链表长度
链表使用的例子可以在字符串表和符号表的实现中找到。
String Tables
由于有大量的字符串需要管理,并且很多都是重复的,为了保证高效,就有了string table
。
注意:
String Tables
包括String Table
、Int Table
以及Id Table
3个。Entry
:表中的元素称作词条Entry
,存放string
和每个字符串唯一的整数标识index
,即<index,string>
键值对。- 相同的字符串在一个表中只会存在一个,但不同表,如
String Table
和Int Table
,中可能存在一样的字符串。 Symbol
:指向Entry
的指针。
Entry
API
class Entry {
protected:
char *str; // 字符串 value
int len; // 长度
int index; // 标识,即key
public:
Entry(char *s, int l, int i);
int equal_string(char *s, int len) const; // 判断s与str是否相等
bool equal_index(int ind) const; // 判断index是否与ind相等
ostream& print(ostream& s) const; // 输出
char *get_string() const; // 返回字符串
int get_len() const;// 返回长度
};
函数具体实现在stringtab.cc
文件中。
string table 中的3种Entry
有3种词条,分别存放不同类型的数据:
- 真的字符串,如
string fghij = "abcde"
中的"abcde"
- 变量名的字符串表示,如
string fghij = "abcde"
中的fghij
。 - 数字的字符串表示,如
int abc = 123;
中的123
。
上面3种即对应:
// stringtab.h
class StringEntry : public Entry {
public:
void code_def(ostream& str, int stringclasstag);
void code_ref(ostream& str);
StringEntry(char *s, int l, int i);
};
class IdEntry : public Entry {
public:
IdEntry(char *s, int l, int i);
};
class IntEntry: public Entry {
public:
void code_def(ostream& str, int intclasstag);
void code_ref(ostream& str);
IntEntry(char *s, int l, int i);
};
Tables
有了上面的几个Entry
,就可以实现对应的Table
了。
首先是一个父table
,指明各个子table
中可用的方法:
// stringtab.h
template <class Elem>
class StringTable
{
protected:
List<Elem> *tbl; // 用List存放
int index; // the current index
public:
StringTable(): tbl((List<Elem> *) NULL), index(0) { }
// 添加元素,返回新词条的指针
Elem *add_string(char *s, int maxchars);// 加入s的前缀,长度maxchars
Elem *add_string(char *s); // 加入s
Elem *add_int(int i); // 在表中加入代表数字i的字符串
// iterator.
int first(); // 返回第一个Entry的index
int more(int i); // 检查是否
int next(int i); // 返回下一个Entry的index
// 查找,返回目标的指针
Elem *lookup(int index); // 查找index对应的Entry
Elem *lookup_string(char *s); // 查找string对应的Entry
void print(); // 打印整个table
};
然后3
个table
继承上面的父类:
// stringtab.h
class IdTable : public StringTable<IdEntry> { }; // 变量名 identifier table
class StrTable : public StringTable<StringEntry> // string table
{
public:
void code_string_table(ostream&, int classtag);
};
class IntTable : public StringTable<IntEntry> // int table
{
public:
void code_string_table(ostream&, int classtag);
};
函数方法具体的实现在stringtab_function.h
中。
上面所说的内容是PA2中需要用到的。
Utilities
在utilities.h
中定义了一些可能用到的辅助函数。
Abstract Syntax Trees
在语法分析完成时,程序中就有了一棵AST
,可以查看cool-tree.aps
进一步理解AST
,这个文件是aps
语言编写的,大致了解一下内容就可以了。
phyla 和 constructors
cool-tree.aps
中可以看到一些phylum
类型的变量和对应的构造函数,可以用这些构造函数生成AST
的节点。
在cool.y
中有使用构造函数的例子如下:
class_($2,$4,$6,stringtable.add_string(curr_filename))
这样就创建了一个class_
节点。
AST Lists
有一些数组类型的phylum
,含有若干个变量,如:
phylum Classes = LIST[Class_];
对于列表类型的变量, aps
自动提供了如下函数(以Class
举例):
分别表示:
- 构造空的列表
Classes
- 构造仅有一个元素的列表
Classes
- 在尾部添加元素
- 返回第
index
个元素 - 返回列表长度
这些方法是实现语法树的基础,通过这些方法我们可以方便地模拟文法,进而实现语法树。
所有内容都可以在cool-tree.cc
中找到实现。
除此之外,比较重要的是6.5中的构造函数列表,我们会使用列表中的函数构造节点。
上面所说的是PA3要用到的。