用C语言编写解释器(一)――我们的目标


  声明

    为提高教学质量,我地址的学院正在规画编写C说话教材。《用C说话写诠释器》系列文章经清算后将收入书中“综合尝试”一章。是以该系列的文章首要阅读对象定为刚学完C说话的学生(不要求有数据结构等其他常识),所以行文斗劲罗嗦,请勿见责。本人水平有限,若有描述不适当或错误之处请不惜赐教!特此声明。

    原由

    比来,我们学院教员联系我,但愿我能供给一段用 C 说话编写的 BASIC 诠释器,用于 C 说话课程设计教学。我前段时刻也正好陷溺于“说话”自己,本就有筹算写一个诠释器,这下正中我下怀,于是欣然接管。

    以前在藏书楼看过梁肇新的《编程高手规语》,第四章“编程说话的运行机理”中就包含了一段 C 说话编写的 BASIC 诠释器代码,但代码仿佛并不完整(我翻了好几遍,都没发现函数 get_token 的实现代码);再者,此次的代码还有其他用处,不宜牵扯版权问题;最后的原因是我有“想自己编码”的感动 ^_^。综上所述,我要从零起头用 C 说话来编写一个 BASIC 诠释器。

    前置常识

    1. 要编写诠释器,首先就要年夜白什么是诠释器(具体的诠释请参看维基百科:http://zh.wikipedia.org/zh-cn/诠释器)。盗用《编程高手规语》里的话:诠释轨范就是一个字符串的诠释器(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说话写诠释器(二)》。


 

转载于:https://www.cnblogs.com/keven2011/archive/2011/02/20/1959288.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值