話說有這樣一道神題:【集訓隊互測2015】未來程序·改。
大意是要求寫一個簡單的C++解釋器!這里去掉了C++的許多特性,連簡單的break和continue都沒有了!
話說NOI被屠了之后,一時心血來潮,打算A了這道題。最近的一個星期閑着無聊(其實還是很多事要做),在歷經險阻之后,終於A掉這道喪心病狂的OI題。
雖然我沒有看過任何關於解釋器的理論,但我覺得好像還是可以憑自己亂搞出來的,於是便開始了這長達一周的旅程。估計寫了4h的代碼,調試2h。
聽說可以用語言分析樹這樣高大的東西來做,我看到其他A了的3個人就是用這樣的東西的。Orz,以后有機會找來學學。
我認為我的代碼有三個最主要的部分,實現了之后發現寫掉這題也不算太難。
變量儲存
就是將單個的變量和數組儲存起來的一個東西。最最困難的一個地方是有些變量重名,但是不是同一個量,比如:
int i;
i = 10;
if (i) {
int i;
i = 0;
if (i) {
cout << 1;
}
else cout << 0;
}
這里的i就是不一樣的,運行后輸出0。
我是用了map<string, stack<Variable> >,來保存的,每次遇到左大括號時標記一下,遇到右大括號就清理一下。由於變量的讀寫是非常普遍的,而在這里由使用了map,所以導致了程序運行速度慢,不管啦,反正可以過就可以了,其實可以用hash代替。
語句執行
主要的思想是維護一些光標(我管它叫光標),光標就是一個變量,保存着程序運行到哪里。
首先要把所有的函數都預先找到它們的位置,然后寫兩個函數:
Return runStatement(int startPosition);Return runFunction(int startPosition, const vector<int> ¶ms);
這里Return是一個結構體,記錄返回的值、返回的命令(return,continue,break,雖說這里沒有后面兩個)以及運行完這個語句/函數后到哪個位置。
至於怎么寫runStatement,就主要靠下面的了。
表達式計算
一個經典的方法就是運用棧,一個符號棧,一個變量棧。
下面是一些主要的問題和解決方法:
- 正號和負號怎么處理?
- 預處理下,把
+(正號,不是加號)變為$+。並且注意它們是右結合的。 - 有右結合的運算符,比如
=。 - 當有一個新符號加入時,我們通過這樣的判斷就能解決這個問題:
while (levelCur < levelTop || (levelCur == levelTop && ! rightCombine(opt))) ...。 - cout這些特殊的怎么處理?
- 我們只需要標記一下就可以了(把它作為一個特殊變量)。
- 數組和函數的參數怎么處理?
- 比如
f(1, 3, 2, 5),我們把f當作一個右結合的四目運算符就可以了。
然后寫好了這些我們就可以快樂地AC了!
代碼鏈接。感覺用C++寫C++解釋器有點逗呢。
