宣告為提高教學質量,我位址的學院正在規畫編寫c說話教材。《用c說話寫詮釋器》系列文章經清算後將收入書中「綜合嘗試」一章。是以該系列的文章首要閱讀物件定為剛學完c說話的學生(不要求有資料結構等其他常識),所以行文鬥勁羅嗦,請勿見責。本人水平有限,若有描述不適當或錯誤之處請不惜賜教!特此宣告。
原由比來,我們學院教員聯絡我,但願我能供給一段用 c 說話編寫的 basic 詮釋器,用於 c 說話課程設計教學。我前段時刻也正好陷溺於「說話」自己,本就有籌算寫乙個詮釋器,這下正中我下懷,於是欣然接管。
以前在藏書樓看過梁肇新的《程式設計高手規語》,第四章「程式設計說話的執行機理」中就包含了一段 c 說話編寫的 basic 詮釋器**,但**彷彿並不完整(我翻了好幾遍,都沒發現函式 get_token 的實現**);再者,此次的**還有其他用處,不宜牽扯版權問題;最後的原因是我有「想自己編碼」的感動 ^_^。綜上所述,我要從零起頭用 c 說話來編寫乙個 basic 詮釋器。
前置常識
1. 要編寫詮釋器,首先就要年夜白什麼是詮釋器(具體的詮釋請參看維基百科:詮釋器)。盜用《程式設計高手規語》裡的話:詮釋軌範就是乙個字串的詮釋器(p165 詮釋說話的事理)。所以,若是僅僅是為我小我編寫的話,我寧可會借助 lex & yacc 甚至 perl,而不會純粹用 c 說話來寫。
2. 在原由中已經提過,這個軌範會在學弟學妹們學完 c 說話後作為綜合嘗試。是以需要你熟悉 c 說話的語法、單鏈表新增/刪除節點等操作以及棧的概念(這些內容年夜部門都能在 c 說話的教材中找到),一些相對偏僻的手藝(例如 setjmp/longjmp)則不會呈此刻軌範中。
關於說話
我在《程式設計和說話之我見》一文中提過,程式設計是乙個很寬泛的概念。從某種意義上來說所有的軟體都是一種特定的說話,但按照軌範自己的矯捷性可以分為「硬編碼」、「可設定裝備擺設」、「可節制」和「可程式設計」四類(詳見《四類軌範》)。若是乙個軌範的矯捷性達到了「可程式設計」,它的設定裝備擺設檔案就可以被看作一種「程式設計說話」,而該軌範自己也就是乙個「詮釋器」。
要做到「可程式設計」,軌範至少應該具備「輸入/輸出」、「表示式運算」、「記憶體打點」和「按前提跳轉」四個功能(詳見《用dos批措置來做數字影象措置》)。這正好對應了馮?諾依曼計較機的結構:以運算器和節制器為中心,輸入/輸出裝置與儲存器之間的資料傳輸都要經由運算器。下面具體介紹各個部門。
我們的方針
我們要編寫詮釋器,自然也逃不出上面的條條例例。語法就參考 basic,但因為是設計我們自己的說話,當然可以按照小我興趣進行「添枝接葉」(好比表示式裡供給神往已久的階乘運算 ^_^)。下面是一段 basic 的示例**(example.bas):
0009 n = 0
0010 while n < 1 or n > 20
0011 print "請輸入乙個1-20之間的數"
0012 input n
0013 wend
0020 for i = 1 to n
0030 l = "*"
0040 for j = 1 to n - i
0050 l = " " + l
0060 next
0070 for j = 2 to 2 * i - 1 step 2
0080 l = l + "**"
0090 next
0100 print l
0110 next
0120 i = n - 1
0130 l = ""
0140 for j = 1 to n - i
0150 l = l + " "
0160 next
0170 for j = 1 to ((2*i) - 1)
0180 l = l + "*"
0190 next
0200 print l
0210 i = i - 1
0220 if i > 0 then
0230 goto 130
0240 else
0250 print "by redraiment"
0260 end if
basic 語法要求行首供給乙個 1->9999 之間的數字作為該行的行號(當前行的行號不小於上一行的行號),供 goto 語句跳轉時挪用。basic 的語法比 c 嚴酷,這不僅可以降低**的複雜性還使說話自己更易學。上面的**差不多涵蓋了我們需要實現的所有功能,若是能被正確解析,你將看到下面的執行下場:
下面來依次談判要實現的功能。
輸入/輸出(io)
經由過程輸入/輸出來和外部軌範某人互動,這是脫離「硬編碼」的最基本要求。輸入/輸出也是很抽象的概念,它並不侷限於尺度輸入輸出端(鍵盤、顯示器等),也可以經由過程檔案、網際網路等體例獲得資料(是以 c 說話中除了 scanf、printf 等,其實 #include 指令也算是一種 io 操作)。我們這個軌範並不強調 io,是以只要求實現 input 和 print 兩條指令,分袂用於從鍵盤輸入資料和列印到螢幕。指令的名目如下:
input var[, var ...]
其中 var 代表變數名(下同),變數之間用逗號離隔。
作用:從鍵盤獲得乙個或多個值,並賦值到響應的變數。同時輸入多個變數時,輸入的每個數之間用空格、回車或製表符離隔。
例如:input a, b, c
print expression[, expression ...]
其中 expression 為表示式(下同),表示式之間用逗號離隔。
作用:對表示式求值,將功效輸出到螢幕並換行。若是有多個表示式,表示式之間用製表符( )離隔。
例如:print i * 3 + 1, (a + b)*(c + d)
表示式運算 在《dos》中我稱號它為「算術運算」。但對於計較機來說,「算術運算」不僅包含諸如「四則運算」等算術運算,還蒐羅「關係運算」和「邏輯運算」。為了避免歧義,在此就改稱它為「表示式運算」。「表示式運算」是整個軌範的焦點,地位相當於計較機的運算器。在我們的軌範中,需要實現以下幾種運算子:
符號 名稱 優先順序 連繫性
( 左括號 17 left2right
) 右邊 17 left2right
+ 加 12 left2right
- 減 12 left2right
* 乘 13 left2right
/ 除 13 left2right
% 取模 13 left2right
^ 求冪 14 left2right
+ 正號 16 right2left
- 負號 16 right2left
! 階乘 16 left2right
> 年夜於 10 left2right
< 小於 10 left2right
= 等於 9 left2right
<> 不等於 9 left2right
<= 不年夜於 10 left2right
>= 不小於 10 left2right
and 邏輯與 5 left2right
or 邏輯或 4 left2right
not 邏輯非 15 right2left
記憶體打點
在我們這個迷你型的詮釋器中,可以不用考慮記憶體空間動態分配的問題,只要實現簡單的變數打點。我們預設供給 a-z 26個可用的弱型別變數(可以隨意賦值為整數、浮點數或字串)。變數要求先賦值才能使用,否則就會提醒變數不成用(是以示例**中第一行就是給 n 賦值為 0)。賦值語句的名目為
[let] var = expression
其中 let 是可選的關頭字。basic 中不許可呈現 var1 = var2 = expression 這樣的賦值語句,
因為在表示式中「=」被翻譯為「等於」,所以賦值合適沒有呈此刻上面的**中。
作用:計較表示式的值,並將功效賦值給變數 var。
例如:i = (123 + 456) * 0.09
按前提跳轉
若是設計一門最精練的說話,那它的節制語句就只需供給像彙編中的 jmp、jnz 等按照前提跳轉的語句即可,經由過程它們的組合即可模擬出 if、while、for、goto 等節制語句。但 basic 作為一門高階說話,需要供給更高層、更抽象的語句。我們將會實現以下四條語句:
1)goto expression
其中 expression 是乙個數值表示式,計較功效必需為可用的行號。因為它是乙個表示式,經由過程動態計較就能模擬子軌範挪用。
作用:無前提跳轉到指定行。
例如:goto 120+10
2)if expression then
sentence1
[else
sentence2]
end if
其中 sentence 是語句塊(下同),包含一條或多條可執行語句。else 為可選部門。
作用:分支結構。但表示式值為真(數字不等於0或者字串不為空)時執行語句塊1;否則,有 else 語句塊時執行 else 語句塊。
例如:if 1=1 then
print "true"
else
print "false"
end if
3)for var = expression to expression [step expression]
sentence
next
所有表示式均為數值表示式。step 為可選部門,為迭代器的步長。步長表示式的值不許可為 0。
作用:輪迴迭代結構
例如:for i = 1 to 10 step 3
print i
next
4)while expression
sentence
wend
作用:迭代執行語句塊,直到表示式的值為假。
例如:while n < 10
n = n + 1
wend
更多細節
basic 的源**不區分鉅細寫;
本軌範在實現中沒有措置字元轉義,是以無法無法輸出雙引號。在介紹完所有原始碼後,若是你有興趣可以考試考試自行完美;
本軌範同樣沒有考慮注釋(rem 關頭字)。其實這很簡單,但這個問題同樣留給你來措置 ^_^;
也許你也會有興趣新增 gosub 和 return 關頭字,讓子軌範功能從 goto 中解放出來。
總結
C語言編寫Linux shell直譯器的問題
include include include include include include define maxargs 20 最多接受20個引數 define arglen 100 長度最多100 intmain else return0 void execute char arglist c...
C 編寫乙個直譯器
一直想知道如何編寫一門指令碼語言,現在終於有機會實現了。非常感謝 thorsten ball 的大作 writing an interpreter in go 幫助我實現了這個想法!可能你比較好奇為啥這個專案叫 autumn,因為剛好這個專案是在國慶節開始的,正好是秋天,就以當時的季節命名了。c 是...
用c語言編寫乙個日期計算器
前提概述 用c語言 編寫乙個日期計算器思路 1 首先我要確定二個年份之間天數 當成滿年來看 j1值 2 然後我們再來確定月份 第一種是前年份的月份小於後年份的月份 那我們就先不看二個年份的本月份 計算二個年份除本月外還剩月份的天數 然後我們再加上前月份剩的天數和後月份過的天數。相加 第一種j2值 第...