自己寫的編譯器一般幾個符號表比較合適?
我有兩個想法。一是利用的棧存放標識符號,兩個棧,一個符號表棧,一個塊索引棧,符號表棧包含所有標識符。二是利用vector 創建 變數,常量,函數,結構,數組這5個動態數組存放標識符號?
naive。等到你開始支持namespace、static method一樣的東西,你就知道所有的東西都得用樹來和scope組織。但是光用樹和scope還不夠,當你支持using namespace的時候你就知道麻煩了——這也是為什麼80年代設計出來的C++的using namespace的語義這麼垃圾,大概是因為以前內存太少了,沒法使用複雜的組織方法。
因此符號表只需要一個,然後每個語法樹的節點都可以決定自己要不要創建scope,然後scope可以掛到符號表的一個節點上,也可以是匿名的,scope還是反向引用的。每個scope上面還要掛著一個額外的為using namespace優化的數據結構,這麼弄下來就差不多了,所有的語言的特性都包括在裡面了。
Symbol Table可以用鏈表、動態數組、hash、map等結構。
Symbol Table的設計和Syntax Checking、Type checking、Property Access等都有關係。設計上的選擇是多樣的,比如可以把常量和變數放在兩張表,在語義分析的過程中加一個判斷,來決定操作哪張表;也可以放在一起,加上額外的toeken(其他的token包括type等信息)區分它們。拋開複雜功能不談,最方便的理解方式是實現小型語言的解釋器,操作Env(Environment)的過程中自然就是在和Symbol Table打交道:對Identifier的創建、查找、刪除,Scope的嵌套、屏蔽等等,都反映在Env的創建、變化以及指向、包含等關係上
比如下面就用了3個表。# Oberon.py
# Env
class environment:
def __init__(self, variables=None, procedures=None, functions=None):
if variables is None:
variables = {}
if procedures is None:
procedures = {}
if functions is None:
functions = {}
self.variables = variables
self.procedures = procedures
self.functions = functions
# Varibles另外用類包裝
class variable:
def __init__(self, type, value):
self.type = type
self.value = value
def __repr__(self):
return repr(self.__dict__)
# Lisp.rb
# Env
class Env &< Hash
def initialize(keys=[], vals=[], outer=nil)
@outer = outer
keys.zip(vals).each{|p| store(*p)}
end
def [](name) super(name) || @outer[name] end
def set(name, value) key?(name) ? store(name, value) : @outer.set(name, value) end
end
# 預定義
def add_globals(env)
ops = [:+, :-, :*, :/, :&>, :&<, :&>=, :&<=, :==]
ops.each{|op| env[op] = lambda{|a, b| a.send(op, b)}}
env.update({ :length =&> lambda{|x| x.length}, :cons =&> lambda{|x, y| [x]+y},
:car =&> lambda{|x| x[0]}, :cdr =&> lambda{|x| x[1..-1]}, :append =&> lambda{|x,y| x+y},
:list =&> lambda{|*xs| xs}, :list? =&> lambda{|x| x.is_a? Array}, :null? =&> lambda{|x| x==nil},
:symbol? =&> lambda{|x| x.is_a? Symbol}, :not =&> lambda{|x| !x}, :display =&> lambda{|x| p x}})
end
編程語言實現模式
自製編程語言兩周自製腳本語言
編譯器設計前兩本簡單些, 其實第一本已經可以滿足大多數人的好奇心。我覺得如果考慮作用域的話,利用一個作用域樹即可,樹中節點存放當前作用域的符號,每個節點有父指針,指向其上一層作用域,樹的根為全局作用域。再定義四個基本操作來管理符號,1push,為在當前樹節點下建立一個新的子節點並將作用域壓入棧中。2pop,為將作用域彈出棧。3def,為在當前作用域即棧頂的作用域裡面定義符號。4find,為查找符號,當前作用域無定義則通過其父指針到父作用域中查找,直到全局作用域中。利用作用域的好處是不僅概念上簡單同時還能處理面向對象中class中的符號,也包括繼承等,因為可以定義多種作用域如函數作用域類作用域等,這些都是作用域樹的節點,節點間也可相互指向,如子類b的作用域節點指向了其父類a的節點,這樣當在b的實例對象中查找成員符號時可上溯至其父類中。
沒看明白你的問題描述在講什麼
符號表的另外一個名字叫環境,只不過前者多用於編譯器,後者多用於解釋器
一般來說符號表最多就兩個,一個類型符號表,一個變數符號表
您那種劃分有何意義?
建議好好看下 @姚培森 的答案
推薦閱讀: