自己動手寫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高級環境編程>。










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