設計雜感
最近在設計一個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雜混在一起,增加不必要的耦合性,我不是很喜歡這種風格。
今天準備再花一些時間,精化這個設計.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.