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

自己动手写basic解释器

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

 





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




表达式已求,下面可以进入程序逻辑处理了,这里的代码量比较大,不过都很简单,后面主要是以程序注释为主。先来看看完整版的主函数:

  1. main (int argc,char *argv[])
  2. {
  3.     char in[80];
  4.     int answer;
  5.     char *p_buf;
  6.     char *t;

  7.     if (argc!=2)  {
  8.         printf ("usage: run <filename>/n");
  9.         exit (1);
  10.     }

  11.     /* allocate memory for the program */
  12.     if (!(p_buf=(char *)malloc(PROG_SIZE)))  {
  13.         printf ("allocation failure");
  14.         exit (1);
  15.     }

  16.     /* load the program to execute */
  17.     if (!load_program(p_buf,argv[1]))  exit(1);

  18.     if (setjmp(e_buf))  exit(1); /* initialize the long jump */

  19.     prog = p_buf;
  20.     scan_labels();  /*  搜索所有的标签  */
  21.     ftos = 0;  /* 初始化栈指针  这个是为for循环作准备的  */
  22.     gtos = 0;  /* 初始化栈指针  这个是为gosub作准备的 */
  23.     do  {
  24.         token_type = get_token();
  25.         /* 如果当前是变量 */
  26.         if (token_type==VARIABLE)  {
  27.             putback();  /* 回退prog指针到变量前 */
  28.             assignment();  /* 赋值  */
  29.         }
  30.         else  /* 除了变量那就是关键字了  可能有同学会问  呃  那个比如一个数字怎么没考虑  请想想一个数字怎么会单独出现 */
  31.             switch (tok)  {
  32.                 case PRINT:
  33.                     print();
  34.                     break;
  35.                 case GOTO:
  36.                     exec_goto();
  37.                     break;
  38.                 case IF:
  39.                     exec_if();
  40.                     break;
  41.                 case FOR:
  42.                     exec_for();
  43.                     break;
  44.                 case NEXT:
  45.                     next();
  46.                     break;
  47.                 case INPUT:
  48.                     input();
  49.                     break;
  50.                 case GOSUB:
  51.                     gosub();
  52.                     break;
  53.                 case RETURN:
  54.                     greturn();
  55.                     break;
  56.                 case END:
  57.                     exit(0);
  58.             }
  59.     }while (tok != FINISHED);
  60. }

main函数其实没啥好说的,主要是这个函数是个花架子,真正实在的逻辑处理不在这里。不过特别需要强调的是prog这个字符指针,这是程序的命根子,它打到哪儿我们就得运行到哪儿,get_token就得取哪儿的标识符。当然这种重要的东西肯定是掌握在我们自己手里。另外是setjmp函数,这个可以理解为windows系统中的系统还原,一旦出错我们程序可以跳到这个还原点。

在while循环里,我们一行一行处理源代码,注意是一行一行的进行,比如print a,b,c  我们会在print函数里面循环打印a,b,c 。而不会多次调用print,这种设计很巧妙。

来先看看变量赋值函数assignment:

  1. /* 给变量赋值  比如 a=3  
  2.  * 注意这里为了简化起见,我们的变量就设置为26个字母
  3.  */
  4. assignment()
  5. {
  6.     int var,value;
  7.     /* getthe variable name */
  8.     get_token();
  9.     if (!isalpha(*token))  //因为变量我们用字母代替 所以必定是字母类型
  10.     {
  11.         serror(4);
  12.         return;
  13.     }
  14.     var = toupper(*token)-'A';  //转化为大写字母  然后减去'A' 这样让变量在hash表中有了座次 比如A减去A为0 这样A字符变量在变量hash表中第一个位置
  15.     /* get the equals sign 
  16.      * 这里我们取a=3 中间的等号*/
  17.     get_token();
  18.     if (*token!='=')    //既然赋值么 肯定有等号了
  19.     {
  20.         serror(3);
  21.         return;
  22.     }
  23.     /* a=3  等号取走了 我们来取数值  */
  24.     get_exp(&value);
  25.    
  26.     /* 把我们取到的变量 比如a 值为3 存放在hash表中 */
  27.     variables[var] = value;
  28. }
这里的变量我们用26个字母表示,比较简单,减少了我们代码逻辑判断,当然缺点就是变量不能见名知义,还有有时不够用,要知道我们这个basic没有所谓的全局变量和局部变量,都作为全局处理的。这里面又嵌套了一个函数——serror,看名字就知道是错误处理的:

  1. /* display an error message */
  2. void serror(int error)
  3. {
  4.     char *e[] = {
  5.         "syntax error",
  6.         "unbalanced parentheses",
  7.         "no expression present",
  8.         "equal sign expected",
  9.         "not a variable",
  10.         "label table full",
  11.         "duplicate label",
  12.         "undefined label",
  13.         "THEN expected",
  14.         "TO expected",
  15.         "too many nested FOR loops",
  16.         "NEXT without FOR",
  17.         "too many nested GOSUB",
  18.         "RETURN without GOSUB"
  19.     };
  20.     printf ("%s/n",e[error]);
  21.     longjmp(e_buf,1);  /* return to save point */
  22. }
这个函数就是个打印,里面有个longjmp,就是上面所说的跳到"系统恢复点",与setjmp隔江相望。具体可以查查<unix高级环境编程>。










發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章