自己動手寫basic解釋器(二)

自己動手寫basic解釋器

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

 





注: 文章basic解釋源碼摘自梁肇新先生的《編程高手箴言》(據他所說這個代碼也是網上摘錄的),源碼解讀參考《java編程藝術》。《java編程藝術》裏面自然是java版了(可能旭哥更加適合點兒),我這裏還是解讀的C版basic解釋器代碼。

上次我們把程序裝載入內存,這裏我們開始做詞法分析了。hoho~ 開始 

一開始我們先再來回顧下:當我們的解釋器在執行時,每次讀入一條語句,並且根據這條語句執行特定的操作;然後再讀入下一條語句,依此執行下去。也就是說解釋器執行時,每次從程序的源代碼中讀入一個標識符。如果讀入的是關鍵字,解釋器就按照該關鍵字的要求執行規定的操作。舉例來說,當解釋器讀入一個 PRINT後,它將打印PRINT之後的字符;當讀入一個GOSUB時,它就執行指定的子程序。在到達程序的結尾之前,這個過程將反覆進行。
按照上面的分析,我們所做的第一步就是要讀取標識符,要一個單詞一個數字的區分,比如print要作爲一個關鍵字讀入,2345要作爲一個數字讀入,而a=3這裏要作爲三個標識符讀入,分別是變量a、賦值符號=以及數值3。看來我們在讀取標識符時,我們還需要給標識符分類。

  1. #define DELIMITER 1  //分界符 比如逗號 分號 等號 都屬於這之列
  2. #define VARIABLE 2   //變量
  3. #define NUMBER 3     //數字
  4. #define COMMAND 4    //關鍵字(命令)
  5. #define STRING 5     //字符串(這個比較特殊 既包括關鍵字 又包括常量字符串)
  6. #define QUOTE 6      //常量字符串 比如"hello world"
這裏仔細談分類收穫不大,待看了get_token 這個取標識符的函數之後我們就大致明白爲什麼要這樣分了。token主要目的就是讀取當前的標識符,放入token字符數組中,並確定標識符類型,放入token_type中,如果是關鍵字,那麼我們還需要處理tok,標記爲相應關鍵字。
  1. /* get a token 
  2.  * 從basic源碼中讀取一個符號(token) 並且區分出符號類型
  3.  * 我們把獲取的符號放在token字符數組裏面 在token_type中設置符號類型 在tok中設置
  4.  */
  5. get_token()
  6. {
  7.     register char *temp;
  8.     token_type = 0;     //記錄標識符的類型
  9.     tok = 0;            //記錄關鍵字  
  10.     temp = token;
  11.    
  12.    /* */
  13.     if (*prog == '/0')  {   /* 文件結束 */
  14.         *token = 0;
  15.         tok = FINISHED;     //設置文件結束符號
  16.         return (token_type = DELIMITER);    /* 標號類型設置爲分界符*/
  17.     }
  18.    
  19.     while (iswhite(*prog))  ++prog;  /* 這裏主要是除去字符串裏面的空格 */
  20.     if (*prog == '/r')  
  21.     {   /* 換行符處理 */
  22.         ++prog;++prog;  //這裏跳過的字符"/r/n"
  23.         tok = EOL;      //設置一行結束的標識 EOL(End Of Line) 
  24.         *token = '/r';token[1] = '/n';token[2] = 0;
  25.         return (token_type = DELIMITER);  //設置爲分界符
  26.     }
  27.    
  28.     if (strchr("+-*^/%=;(),><",*prog))    /* 在"+-*^/%=;(),><"查找prog */
  29.      { 
  30.         *temp = *prog;              //把這個分界符拷貝到token中
  31.         prog++;  /* advance to next position */
  32.         temp++;
  33.         *temp=0; //最後補上零 這樣讓token形成字符串  
  34.         return (token_type = DELIMITER);
  35.     }
  36.    
  37.    /* 這裏'"'表明後面的是字符串  basic中常常出現的情況是 print "hello world" 
  38.     * 下面的例子就以此舉例了
  39.     */
  40.     if (*prog == '"')   
  41.     {  
  42.         prog++;         //prog指向了字符'h',注意這裏已經進入了字符串
  43.         while (*prog!='"'&&*prog!='/r')  *temp++=*prog++;   //只要字符串沒結束(prog遇到雙引號),或者是沒換行('/r') 我們就要把原代碼拷貝到token中
  44.         if (*prog=='/r')  serror(1);    //字符串不能換行
  45.         prog++;*temp=0;     //prog已經走出字符串啦,照樣的token要補上結束符
  46.         return (token_type = QUOTE);  //token_type爲字符串
  47.     }
  48.    
  49.    /* 數字處理  
  50.     * 唯一要注意的是我們現在的數字是字符串形式的...
  51.     */
  52.     if (isdigit(*prog))  
  53.     {   
  54.         while (!isdelim(*prog))  *temp++=*prog++;   //如果沒有分界符,拷貝之 
  55.         *temp = '/0';
  56.         return (token_type = NUMBER);
  57.     }
  58.     /* 處理字符  仍舉例 print "hello world"
  59.      * 這裏我們得到print
  60.      * 注意該代碼塊沒有馬上返回
  61.      */
  62.     if (isalpha(*prog))  
  63.     {   
  64.         while (!isdelim(*prog))  *temp++=*prog++;   //沒有分界符拷貝之
  65.         token_type = STRING;    //string類型  這只是一箇中間類型  具體要劃分爲變量、關鍵字  你會想爲什麼不會是我們打印的常量字符串呢  因爲上面我們已經處理了
  66.     }
  67.     *temp = '/0';   //不過這一句放在外面  其實沒多大意義  前面的情況都return了, 剩下一個孤零零   因爲接下來我們還要處理這玩意兒
  68.     /* 查看究竟是個命令呢,還是一個變量,拭目以待... */
  69.     if (token_type == STRING)  
  70.     {
  71.         tok = look_up(token);  /* 在變量hash表中查之 */
  72.         if (!tok)  
  73.             token_type = VARIABLE;  //變量
  74.         else 
  75.             token_type = COMMAND;  //關鍵字
  76.     }
  77.     return token_type;
  78. }


get_token裏面還有一些小函數,這裏我們同樣也羅列出來。裏面嵌套的函數我都註釋了的,都比較好懂,注意點我都有解釋
  1. /* 回滾 */
  2. void putback()
  3. {
  4.     char *t;
  5.     t = token;
  6.     for (;*t;t++)  prog--;      //
  7. }
  8.  /* 這丫是幫我們在關鍵字table表裏面搜索是否包含當前字符串  */
  9. look_up(char *s)
  10. {
  11.     register int i,j;
  12.     char *p;
  13.     /* 命令因爲我們全都用小寫字符 這裏不得不轉換了 */
  14.     p = s;
  15.     while (*p)  { *p = tolower(*p); p++;  }
  16.     /* 順序查找  呃  這個是個tiny 所以效率不太考慮了  
  17.      * 想想如果我們關鍵字很多的話  應該做一個hash表
  18.      * 這裏如果查找成功返回一個標號 失敗返回0 請參閱table結構
  19.      */
  20.     for (i=0;*table[i].command;i++)
  21.         if (!strcmp(table[i].command,s))  
  22.             return table[i].tok;
  23.     return 0;   
  24. }
  25. /* 查字符c是否是分界符 
  26.  * 恩 有一點要注意  比如我們的數字是909 那麼這裏傳進來的c的ASCII碼可不是909 應該是9+'0','0',9+'0'*/
  27. isdelim(char c)
  28. {
  29.     if (strchr(";,+-<>/*%^=() ",c)||c==9||c=='/r'||c==0)  //所以這裏c==9 表示'/t' 0自然是'/0'
  30.         return 1;
  31.     return 0;
  32. }
  33. /* 查是不是"空白",就是空格和tab鍵*/
  34. iswhite (char c)
  35. {
  36.     if (c==' '||c=='/t')  return 1;
  37.     else return 0;
  38. }

至此 標識符處理我們全部完成,工作完成了一大塊,剩下就是關鍵字處理了,裏面涉及到語句邏輯。




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