自己动手写basic解释器(五)

来源:互联网 发布:ip地址和mac地址绑定 编辑:程序博客网 时间:2024/06/08 05:32
 

自己动手写basic解释器

刺猬@http://blog.csdn.net/littlehedgehog

 





注: 文章basic解释源码摘自梁肇新先生的《编程高手箴言》(据他所说这个代码也是网上摘录的),源码解读参考《java编程艺术》。《java编程艺术》里面自然是java版了(可能旭哥更加适合点儿),我这里还是解读的C版basic解释器代码。




是所谓先来后到,我们还是按照主函数出现的顺序,依次分析逻辑函数。

1、print
print函数主要的关注两点,第一点是打印格式,第二点是打印方式。比如我举两例:

  1. print a
  2. print "hello world "    '要分清楚打印变量还是打印字符串  这是打印方式的问题
  3. print a,b
  4. print a;b      '要分清楚打印格式,注意一个是分号一个是逗号  两者有区别!
print代码:

  1. /* execute a simple version of the BASIC PRINT statement 
  2.  * 执行打印  这里我们还是举例说明*/
  3. void print()
  4. {
  5.     int answer;
  6.     int len=0,spaces;
  7.     char last_delim;
  8.    
  9.     do  {
  10.         get_token();  /* get next list item */
  11.         if (tok==EOL||tok==FINISHED)  break;  //如果取到的符号是一行结束或者文件结束  自然的打印结束
  12.                  
  13.         //BASIC 中print一般有两种用法  第二种就是print "hello world"  打印字符串  
  14.         if (token_type==QUOTE)  
  15.         {  
  16.             printf ("%s",token);
  17.             len+=strlen(token);
  18.             get_token();    //注意我们打印了后又取了一次符号
  19.         }
  20.         else   //打印变量的
  21.         { 
  22.             putback();
  23.             get_exp(&answer);
  24.             get_token();    //注意我们打印了后又取了一次符号
  25.             len += printf ("%d",answer);
  26.         }
  27.         last_delim = *token;    
  28.         
  29.         
  30.         /* Basic 有两种打印间隔标识 
  31.          * 比如 print a,b 表示按标准格式打印
  32.          * 而print a;b 表示按照紧凑格式打印  
  33.          * 所谓标准格式简单来讲就是间隔大点儿  紧凑自然间隔小点儿  
  34.          */
  35.         if (*token==',')  
  36.         {
  37.             /* compute number of move to next tab */
  38.             spaces = 8-(len%8);
  39.             len += spaces;  /* add in the tabbing position */
  40.             while (spaces)  {
  41.                 printf (" ");
  42.                 spaces--;
  43.             }
  44.         }
  45.         else if (*token==';')  
  46.             printf ("  ");
  47.         else if (tok != EOL && tok != FINISHED) serror (0);     //print a,b 打完一次后 要么是逗号、分号 要么就是行结束或者文件结束  如果四者不居其一  必然错了
  48.     } while (*token==';'||*token==',');     //例如 print a,b,c 如果token是逗号、分号 那么表示后面还有打印  继续来
  49.     /* 当处于行结束或者文件结束  那么前一次分界符不能是;或者,  
  50.      * 示例 如果 "print a," 这个明显是语法错误 a后面不应该要逗号
  51.      * 那么打印完a取出token是逗号  我们赋值给last_delim 继续循环
  52.      *  下一个是行结束  跳出打印但是检验出last_delim是逗号  出错 */
  53.     if (tok==EOL||tok==FINISHED)    
  54.     {
  55.         if (last_delim != ';' && last_delim != ',') printf ("/n");
  56.     }
  57.     else serror(0);  /* error is not, or ; */
  58. }


2、goto
关于goto的争论还是上个世纪计算机洪荒时代,争论的结果是大量程序员在程序中尽量回避goto这个关键字,其实合理的使用goto不但提升效率,还能增加代码可读性。

  1. /* execute a GOTO statement. 
  2.  * goto一般形式即是 goto label 
  3.  */
  4. void exec_goto()
  5. {
  6.     char *loc;
  7.     get_token();  /* 这里获取标号,即是标签内容 */
  8.     
  9.     loc = find_label (token);  //标签是为跳转所用,所以获取标签后我们马上要想办法得到标签所代表地址
  10.     if (loc=='/0')
  11.         serror(7);  /* 出错 */
  12.     else prog=loc;  /* 重新 设置prog指针  指出了下一个我们运行的地址  我们得完全听他的*/
  13. }

label表在我们最开始处理程序的时候已经完成,我理解为"预处理",代码如下,总的思路是扫描一遍代码,搜索标号。 注意 在我们这个basic中,我们标号的规定是必须在一行开头,并且是数字.  这个例子可以参照文章第一部分我举的那个basic例子,其实我们可以看到现实中确实有这样basic规定,比如smallbasic。
标签初始化:scan_labels():
  1. /* 搜索所有标签 
  2.  * 这个函数可以说是basic里面的预处理  
  3.  * 我们搜索源代码 找出里面的标签  将其存入标签表
  4.  * 所谓标签label 其实C语言也有 不过一般不常用 因为label多半和goto一起出现的  而在结构化程序设计中 goto出现被人认为是绝对不能的
  5.  * 不过内核中goto却是常常出现
  6.  */
  7. void scan_labels()
  8. {
  9.     int addr;
  10.     char *temp;
  11.     label_init();  /* zero all labels */
  12.     temp = prog;  /* save poiter to top of program */
  13.     /* 如果源代码中第一个token是个数字的话  存入标签表中  表明是个标签*/
  14.     get_token();
  15.     if (token_type==NUMBER)  
  16.     {
  17.         strcpy (label_table[0].name,token);
  18.         label_table[0].p=prog;
  19.     }
  20.    
  21.     find_eol();     //提行
  22.     do  {
  23.         get_token();
  24.         if (token_type==NUMBER)     //如果是数字  注意我们标签定义为行开头、数字
  25.         {
  26.             addr = get_next_label(token);
  27.             if (addr==-1||addr==-2)  
  28.             {
  29.                 (addr==-1) ? serror(5):serror(6);
  30.             }
  31.             strcpy (label_table[addr].name,token);
  32.             label_table[addr].p = prog;  /* 标签干什么用的  就是方便跳转阿  所以我们要记录标签所在地址 */
  33.         }
  34.         /* if not on a blank line , find next line */
  35.         if (tok!=EOL) find_eol();
  36.     } while (tok!=FINISHED);
  37.     prog = temp;  /* restore to original */
  38. }
3、if
if这个函数貌似有点儿问题。我直接加在注释里的,如果我理解错了,还请各位指出:

  1. /* execute an IF statement 
  2.  * 执行if语句
  3.  */
  4. void exec_if()
  5. {
  6.     int x,y,cond;
  7.     char op;
  8.     /* 这里我们只是处理一个简单的if  就是if (x operator y) */
  9.     get_exp(&x);  /* 获取操作符左边数值 */
  10.     get_token();  /* 获取操作符  "比较符" */
  11.     if (!strcmp("<>",*token))   //这里有点儿问题  一个字符串不可能跟一个字符比较吧
  12.     {
  13.         serror(0);  /* not a leagal oprator */
  14.         return;
  15.     }
  16.     op = *token;
  17.     get_exp(&y);  /* 操作符右边  */
  18.     /* determine the outcome */
  19.     cond = 0;
  20.     switch(op)  {
  21.         case '<':
  22.             if (x<y) cond=1;
  23.             break;
  24.         case '>':
  25.             if (x>y) cond=1;
  26.             break;
  27.         case '==':      //这里也是有点儿问题,op是字符类型 怎么会可能会是'==',而且好笑的是basic没有这个符号
  28.             if (x==y) cond=1;
  29.             break;
  30.     }
  31.     if (cond)  {  /* is true so process target of IF */
  32.         get_token();
  33.         if (tok != THEN)  {     //if 后面会连上then 所以有if没then是错误的
  34.             serror(8);
  35.             return;
  36.         }  /* else program execution starts on next line */
  37.     }
  38.     else find_eol();  /* find start of next line */
  39. }






原创粉丝点击