最近在设计一个Identifier的Resolver,通俗来说,这个Resolver要完成如下功能:
1。对语句,表达式中出现的ID进行解析,判断该ID是否存在相应的定义。
2。对存在定义的ID,要校验引用处对该ID的用法是否与其定义处的语意相兼容。
比如说,对一个浮点变量执行移位操作,在语法上是合法的,但是在语意上则通常不允许了。
这件事情说起来并不太复杂,主要涉及到的工作有两件:
1。在对源文件进行语法parse的过程中,建立一个符号表,用于存放ID名称及其相应定义
信息的映射关系。
2。对源文件完成parse,建立起语法树以后,对语法树中的元素进行遍历,对其中涉及到ID
引用的地方执行ID Resolve的操作。
对工作1,符号表的建立来说,主要工作就是为这个符号表设计一个能够满足自己的任务需求的数据
结构,我给出的设计大致如下:
class ident; // 用于描述ID的名称,在引用ID的地方对应会创建一个ident的对象
class idDecl; // 用于记录ID的定义信息,在定义ID的地方会创建一个相应的idDecl对象
class funcIDDecl : idDecl { // 函数ID定义
};
class varIDDecl : idDecl { // 变量ID定义
};
class constIDDecl : idDecl { // 常量ID定义
};
map< ident *, idDecl *> idTable; // ID符号表,完成从ID名称到ID定义信息的映射
通过一个map,将ID的名称与ID的定义建立起映射关系,一方面便于查找,另一方面,map的内部实现
是基于红黑树的,查找效率是 O(logN),速度较快,可以适应频繁的ID检索需求。
对工作2,主要的任务有两部分, 1)。如何遍历语法树,找出涉及ID引用的元素;2)。找到这些ID元
素以后,具体执行哪些ID Resolve的相关操作。
因为最近Visitor模式使用得比较多,手比较熟,而且语法树中涉及到ID引用的元素数目也比较多,比如
表达式,常规语句等都涉及到了ID引用,所以我的初始设计决定设计一个Visitor类对语法树中的元素
进行遍历,在这个Visitor类中,为涉及到ID引用的元素分别设计相应的Visit方法,然后在这些语法元素
对应的类中通过统一的accept接口来调用Visitor,从而完成Visitor类与具体语法元素的结合,大致的设
计如下:
class expr {
public:
void accept( visitorBase& v )
{
v.visitExpr( *this );
}
ident * getID()
{
return id_;
}
private:
ident * id_;
};
class branchStmt {
public:
void accept( visitorBase& v )
{
v.visitBranchStmt( *this );
}
ident * getID()
{
return id_;
}
private:
ident * id_;
};
class ident {
public:
void accept( visitorBase& v )
{
v.visitID( *this );
}
};
...
class idResolver : public visitorBase {
public:
void visitExpr( expr& expr );
{
expr.getID()->accept( *this );
...
}
void visitBranchStmt( branchStmt& stmt )
{
stmt.getID()->accept( *this );
}
void visitID( ident& id );
...
};
但就是这个设计,给自己带来了一些麻烦。
问题是这样的,在不同的上下文,对ID的语意要求是不一样的,比如,在函数调用语句中,只允许出现
函数定义ID,而在表达式计算中则允许出现变量ID,常量ID,以及函数定义ID,此外,还有一些别的场景
对ID语意有不同的要求。而在我初始的Visitor设计中,对ID的resolve操作,全是通过一个visit方法
visitID()来完成的,而visitID的实际内容需要依据ID引用出现的上下文场景有所区别。由于是标准的
visit方法,我不想通过增加一个标识上下文场景类型的标志变量来告诉visitID()方法执行不同动作,
因为个人感觉那样破坏了visitor模式的统一性,代码美感不好,而且容易增加日后维护工作的思考
负担。当下我给出的解决办法是在idResolver类内部定义了一个成员变量,用于记录当前被visit的语法
元素的上下文类型,在visitID中根据这个成员变量来决定执行不同的操作。坦率地讲,自己也并不喜欢
这个方案,因为这个方案有两个潜在的问题:1)。破坏了各个visit方法的独立性,让visitor具有了内部
状态,使得不同的visit方法之间存在了状态依赖,对某个visit方法的调整存在影响到其他visit方法的
可能,是一个潜在在破窗。 2)。由于引入了内部状态,而且这个状态是一个对象级别的变量,这就破坏了
visitID()方法的可重入性,如果visitID()会嵌套调用自身的话,就会带来一些麻烦。
上周同事建议自己将id Resolve的操作不要集中在单一的Visitor类里,而是打散了,分布到各个元素类
里,比如,为branchStmt,expr,ident类增加一个叫作idResolve()的方法。但即便是这个法子,也同样
不能回避ID引用依赖于上下文场景的问题。而且id Resolve这个工作相对独立,放在元素类自身中,会让
其code与元素类的其他code杂混在一起,增加不必要的耦合性,我不是很喜欢这种风格。
今天准备再花一些时间,精化这个设计.
1。对语句,表达式中出现的ID进行解析,判断该ID是否存在相应的定义。
2。对存在定义的ID,要校验引用处对该ID的用法是否与其定义处的语意相兼容。
比如说,对一个浮点变量执行移位操作,在语法上是合法的,但是在语意上则通常不允许了。
这件事情说起来并不太复杂,主要涉及到的工作有两件:
1。在对源文件进行语法parse的过程中,建立一个符号表,用于存放ID名称及其相应定义
信息的映射关系。
2。对源文件完成parse,建立起语法树以后,对语法树中的元素进行遍历,对其中涉及到ID
引用的地方执行ID Resolve的操作。
对工作1,符号表的建立来说,主要工作就是为这个符号表设计一个能够满足自己的任务需求的数据
结构,我给出的设计大致如下:
class ident; // 用于描述ID的名称,在引用ID的地方对应会创建一个ident的对象
class idDecl; // 用于记录ID的定义信息,在定义ID的地方会创建一个相应的idDecl对象
class funcIDDecl : idDecl { // 函数ID定义
};
class varIDDecl : idDecl { // 变量ID定义
};
class constIDDecl : idDecl { // 常量ID定义
};
map< ident *, idDecl *> idTable; // ID符号表,完成从ID名称到ID定义信息的映射
通过一个map,将ID的名称与ID的定义建立起映射关系,一方面便于查找,另一方面,map的内部实现
是基于红黑树的,查找效率是 O(logN),速度较快,可以适应频繁的ID检索需求。
对工作2,主要的任务有两部分, 1)。如何遍历语法树,找出涉及ID引用的元素;2)。找到这些ID元
素以后,具体执行哪些ID Resolve的相关操作。
因为最近Visitor模式使用得比较多,手比较熟,而且语法树中涉及到ID引用的元素数目也比较多,比如
表达式,常规语句等都涉及到了ID引用,所以我的初始设计决定设计一个Visitor类对语法树中的元素
进行遍历,在这个Visitor类中,为涉及到ID引用的元素分别设计相应的Visit方法,然后在这些语法元素
对应的类中通过统一的accept接口来调用Visitor,从而完成Visitor类与具体语法元素的结合,大致的设
计如下:
class expr {
public:
void accept( visitorBase& v )
{
v.visitExpr( *this );
}
ident * getID()
{
return id_;
}
private:
ident * id_;
};
class branchStmt {
public:
void accept( visitorBase& v )
{
v.visitBranchStmt( *this );
}
ident * getID()
{
return id_;
}
private:
ident * id_;
};
class ident {
public:
void accept( visitorBase& v )
{
v.visitID( *this );
}
};
...
class idResolver : public visitorBase {
public:
void visitExpr( expr& expr );
{
expr.getID()->accept( *this );
...
}
void visitBranchStmt( branchStmt& stmt )
{
stmt.getID()->accept( *this );
}
void visitID( ident& id );
...
};
但就是这个设计,给自己带来了一些麻烦。
问题是这样的,在不同的上下文,对ID的语意要求是不一样的,比如,在函数调用语句中,只允许出现
函数定义ID,而在表达式计算中则允许出现变量ID,常量ID,以及函数定义ID,此外,还有一些别的场景
对ID语意有不同的要求。而在我初始的Visitor设计中,对ID的resolve操作,全是通过一个visit方法
visitID()来完成的,而visitID的实际内容需要依据ID引用出现的上下文场景有所区别。由于是标准的
visit方法,我不想通过增加一个标识上下文场景类型的标志变量来告诉visitID()方法执行不同动作,
因为个人感觉那样破坏了visitor模式的统一性,代码美感不好,而且容易增加日后维护工作的思考
负担。当下我给出的解决办法是在idResolver类内部定义了一个成员变量,用于记录当前被visit的语法
元素的上下文类型,在visitID中根据这个成员变量来决定执行不同的操作。坦率地讲,自己也并不喜欢
这个方案,因为这个方案有两个潜在的问题:1)。破坏了各个visit方法的独立性,让visitor具有了内部
状态,使得不同的visit方法之间存在了状态依赖,对某个visit方法的调整存在影响到其他visit方法的
可能,是一个潜在在破窗。 2)。由于引入了内部状态,而且这个状态是一个对象级别的变量,这就破坏了
visitID()方法的可重入性,如果visitID()会嵌套调用自身的话,就会带来一些麻烦。
上周同事建议自己将id Resolve的操作不要集中在单一的Visitor类里,而是打散了,分布到各个元素类
里,比如,为branchStmt,expr,ident类增加一个叫作idResolve()的方法。但即便是这个法子,也同样
不能回避ID引用依赖于上下文场景的问题。而且id Resolve这个工作相对独立,放在元素类自身中,会让
其code与元素类的其他code杂混在一起,增加不必要的耦合性,我不是很喜欢这种风格。
今天准备再花一些时间,精化这个设计.