做一个简单的解释器----小话c语言

时间:2022-09-24 21:36:37

http://blog.csdn.net/cxsjabcabc/article/details/7653020#

 

环境:[Mac 10.7.1  Lion  Intel-based  x64  gcc4.2.1  xcode4.2]  

 

Q: 解释器来源于什么?

A: 如果说是广义的解释器,那么可以把它理解成翻译器,只要能将一种被看成原始的东西翻译成需要的东西,处理的东西就可以被称为解释器。从编程语言角度,解释器更多地表达的含义是,将一种初始状态的数据(一般是文本)转换成另外一种通常来说是较为容易理解的文本或者一种执行过程。

     解释器正来源于自然语言的机器不容易理解性。


Q: 我们尝试做个c语言解释器吧。

A:  罗马也不是一日建成的。我们先做个简单的,这个简单的解释器简单地让我们觉得可以不用做它,但是,我们还是要做它。

    它主要实现以下几个简单的功能:

    解释器名称为simple_interpreter, 命令行下执行它将运行解释器;

    1、输入hello后,它会提示文本: hello, i am a interpreter!

    2、输入ver后,它会提示文本: version: 1.0

    3、输入print  [字符串], 它会输出对应的字符串,字符串不需要任何分界符

    4、输入exit或者quit后,解释器将关闭

    5、如果输入其它命令,输出no such command


Q: 下面的代码是根据上面的需求做出的。

 

[cpp]   view plain copy
  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #include <stdlib.h>  
  4. #include "str_process.h"  
  5.   
  6. int main (int argc, const char * argv[])  
  7. {  
  8.     char    buf[4096] = {0};  
  9.     size_t  buf_size = sizeof(buf);  
  10.     char*   ret;  
  11.       
  12.     // input from stdin  
  13.     while (1)  
  14.     {  
  15.         ret = fgets((char *)&buf, (int)buf_size, stdin);  
  16.         if(ret == NULL) // error or eof  
  17.         {  
  18.             if(ferror(stdin))  
  19.                 printf("error occurs...terminating now...\n");  
  20.             else if(feof(stdin))  
  21.                 printf("eof occurs...terminating now...\n");  
  22.         }  
  23.         else    // input sth  
  24.         {  
  25.             // set the last '\n' to NULL  
  26.             if(buf[strlen(buf) - 1] == '\n')  
  27.                 buf[strlen(buf) - 1] = '\0';  
  28.               
  29.             if(!strcmp(buf, "hello"))  
  30.             {  
  31.                 printf("hello, i am a interpreter!\n");  
  32.             }  
  33.             else if(!strcmp(buf, "ver"))  
  34.             {  
  35.                 printf("version:1.0\n");  
  36.             }  
  37.             else if(!strncmp(buf, "print", strlen("print")))  
  38.             {  
  39.                 char* temp = buf + strlen("print");  
  40.                 if(*temp == ' ')  
  41.                 {  
  42.                     cc_skip_blank(&temp);     
  43.                     printf("%s\n", temp);  
  44.                 }  
  45.                 else if(*temp == '\0')  
  46.                 {  
  47.                    printf("\n");   
  48.                 }  
  49.                 else  
  50.                 {  
  51.                     printf("no such command\n");  
  52.                 }  
  53.             }  
  54.             else if(!strcmp(buf, "exit") || !strcmp(buf, "quit"))  
  55.             {  
  56.                 exit(0);  
  57.             }  
  58.             else  
  59.             {  
  60.                 printf("no such command\n");  
  61.             }  
  62.         }  
  63.     }  
  64.       
  65.     return 0;  
  66. }  


 

保存为simple_interpreter.c;

str_process.h和str_process.c代码如下:

 

[cpp]   view plain copy
  1. #ifndef CCSH_STR_PROCESS_H  
  2. #define CCSH_STR_PROCESS_H  
  3. #include <stdbool.h>  
  4.   
  5. bool    cc_is_blank(char ch);  
  6. char    *cc_get_next_blank(const char *str);  
  7. void    cc_skip_blank(char  **str);  
  8. bool    cc_str_is(const char *str1, const char *str2);  
  9. bool    cc_str_begin_with(const char *str, char ch);  
  10.   
  11. #endif  


 

[cpp]   view plain copy
  1. #include <stdio.h>  
  2. #include "str_process.h"  
  3. #include <string.h>  
  4.   
  5. inline bool    cc_is_blank(char ch)  
  6. {  
  7.     return ch == '\n'   
  8.         || ch == '\t'   
  9.         || ch == ' ';  
  10. }  
  11.   
  12. char *cc_get_next_blank(const char *str)  
  13. {  
  14.     while (!cc_is_blank(*str) && *str != '\0')  
  15.         ++str;  
  16.     return (char *)str;  
  17. }  
  18.   
  19. void    cc_skip_blank(char  **str)  
  20. {  
  21.     while(cc_is_blank(**str) && *str != '\0')  
  22.     {  
  23.         (*str)++;  
  24.     }  
  25. }  
  26.   
  27. bool    cc_str_is(const char *str1, const char *str2)  
  28. {  
  29.     return strcmp(str1, str2) == 0;  
  30. }  
  31.   
  32. inline bool    cc_str_begin_with(const char *str, char ch)  
  33. {  
  34.     return str[0] == ch;  
  35. }  


 

工程生成simple_interpreter, 运行:

做一个简单的解释器----小话c语言


A: 是的,上面的代码可以按要求执行。不过,它有它的缺点,一是格式固定的太死,如果多输入一个空格,可能造成no such command的错误; 二是它进行不同输入处理的代码过为集中,如果再增加一些,代码维护可能有很大的问题;三是上面的输入输出没有一个标志,很容易混淆。


Q: 如果需要解决第一个问题,那么需要对输入的文本进行初步解析,去除空格、TAB等信息,留下有用的信息;如果对第二个问题,可以用不同文件的单独函数来处理不同的条件分支;第三个问题,增加一个提示符,类似$.

A: 对于第一个问题,可采用如下的图示:

做一个简单的解释器----小话c语言


Q: 将缓冲区数据转换成参数列表,代码如下:

arglist.h:

 

[cpp]   view plain copy
  1. #ifndef CCSH_ARGLIST_H  
  2. #define CCSH_ARGLIST_H  
  3.   
  4. typedef struct _cc_arg_obj  
  5. {  
  6.     char    *str;  
  7.     size_t  len;  
  8.     struct  _cc_arg_obj    *next;  
  9.     char    *buf_pointer;       // the pointer that points the buf, for possible use, eg. echo command  
  10. }cc_arg_obj;  
  11.   
  12. typedef struct _cc_arg_list  
  13. {  
  14.     cc_arg_obj              *head;  
  15.     cc_arg_obj              *tail;  
  16. }cc_arg_list;  
  17.   
  18.   
  19. cc_arg_obj  *cc_arg_obj_make(const char *str,   
  20.                              size_t len,   
  21.                              cc_arg_obj *next,  
  22.                              char       *buf_pointer);  
  23. void        cc_arg_obj_free(cc_arg_obj *obj);  
  24.   
  25. cc_arg_list *cc_arg_list_make(cc_arg_obj    *head);  
  26. cc_arg_obj *cc_arg_list_append(cc_arg_list *list, cc_arg_obj     *obj);  
  27. void        cc_arg_list_free(cc_arg_list    *list);  
  28. void        cc_arg_list_show_all_args(cc_arg_list   *list);  
  29.   
  30. #endif  


arglist.c:

[cpp]   view plain copy
  1. #include <stdio.h>  
  2. #include "arglist.h"  
  3. #include "common.h"  
  4. #include "error.h"  
  5.   
  6. cc_arg_obj  *cc_arg_obj_make(const char *str,   
  7.                              size_t len,   
  8.                              cc_arg_obj *next,  
  9.                              char       *buf_pointer)  
  10. {  
  11.     cc_arg_obj  *obj = (cc_arg_obj *)malloc(sizeof(cc_arg_obj));  
  12.     if(!obj)  
  13.     {  
  14.         cc_err(CC_ERR_NOMEM);  
  15.         return NULL;  
  16.     }  
  17.     char *obj_str = (char *)malloc(len + 1);  
  18.     if(!obj_str)  
  19.     {  
  20.         cc_err(CC_ERR_NOMEM);  
  21.         free(obj);  
  22.         return NULL;  
  23.     }  
  24.     strncpy(obj_str, str, len);  
  25.     obj->str = obj_str;  
  26.     obj->len = len;  
  27.     obj->next = next;  
  28.     obj->buf_pointer = buf_pointer;  
  29.     return obj;  
  30. }  
  31.   
  32.   
  33. void        cc_arg_obj_free(cc_arg_obj *obj)  
  34. {  
  35.     free(obj->str);  
  36.     free(obj);  
  37. }  
  38.   
  39.   
  40. cc_arg_list *cc_arg_list_make(cc_arg_obj    *head)  
  41. {  
  42.     cc_arg_list *list = (cc_arg_list *)malloc(sizeof(cc_arg_list));  
  43.     if(!list)  
  44.     {  
  45.         cc_err(CC_ERR_NOMEM);  
  46.         return NULL;  
  47.     }  
  48.       
  49.     list->head = list->tail = head;  
  50.     return list;  
  51. }  
  52.   
  53. cc_arg_obj *cc_arg_list_append(cc_arg_list *list, cc_arg_obj     *obj)  
  54. {  
  55.     if(list->head == NULL)  
  56.     {  
  57.         list->head = list->tail = obj;  
  58.         return obj;  
  59.     }  
  60.     list->tail->next = obj;  
  61.     list->tail = obj;  
  62.     return obj;  
  63. }  
  64.   
  65. void        cc_arg_list_free(cc_arg_list    *list)  
  66. {  
  67.     cc_arg_obj *head = list->head;  
  68.     while(head)  
  69.     {  
  70.         cc_arg_obj *next = head->next;  
  71.         cc_arg_obj_free(head);  
  72.         head = next;  
  73.     }  
  74. }  
  75.   
  76. void        cc_arg_list_show_all_args(cc_arg_list   *list)  
  77. {  
  78.     cc_arg_obj *head = list->head;  
  79.     while (head != NULL)  
  80.     {  
  81.         printf("arg:%s", head->str);  
  82.         head = head->next;  
  83.     }  
  84. }  


buf_to_arglist.h:

[cpp]   view plain copy
  1. #ifndef CCSH_BUF_TO_ARGLIST_H  
  2. #define CCSH_BUF_TO_ARGLIST_H  
  3.   
  4. #include "arglist.h"  
  5.   
  6. cc_arg_list *cc_buf_to_arglist(const char *buf);  
  7.   
  8.   
  9. #endif  


buf_to_arglist.c:

[cpp]   view plain copy
  1. #include <stdio.h>  
  2. #include "buf_to_arglist.h"  
  3. #include <stdlib.h>  
  4. #include "error.h"  
  5. #include "str_process.h"  
  6.   
  7. cc_arg_list *cc_buf_to_arglist(const char *buf)  
  8. {  
  9.     char    *temp = (char *)buf;  
  10.     cc_arg_list *list = cc_arg_list_make(NULL);  
  11.     if(!list)  
  12.     {  
  13.         cc_err(CC_ERR_NOMEM);  
  14.         return NULL;  
  15.     }  
  16.     while (*temp)  
  17.     {  
  18.         char    *next_blank = cc_get_next_blank(temp);  
  19.         if(temp != next_blank)  
  20.         {  
  21.             size_t len = next_blank - temp;  
  22.             cc_arg_obj *obj = cc_arg_obj_make(temp, len, NULL, temp);  
  23.             if(!obj)  
  24.             {  
  25.                 cc_err(CC_ERR_NOMEM);  
  26.                 cc_arg_list_free(list);  
  27.                 return NULL;  
  28.             }  
  29.             cc_arg_list_append(list, obj);  
  30.         }  
  31.         temp = next_blank;  
  32.         cc_skip_blank(&temp);  
  33.     }  
  34.     return list;  
  35. }  


另外,common.h:

[cpp]   view plain copy
  1. #ifndef CCSH_COMMON_H  
  2. #define CCSH_COMMON_H  
  3.   
  4. #include <stdlib.h>  
  5. #include <stdbool.h>  
  6. #include <string.h>  
  7.   
  8. #endif  


error.h:

[cpp]   view plain copy
  1. #ifndef CCSH_ERROR_H  
  2. #define CCSH_ERROR_H  
  3.   
  4. typedef enum   
  5. {  
  6.     CC_OK,  
  7.     CC_ERR_NOMEM  
  8. }CC_ERR;  
  9.   
  10. typedef struct   
  11. {  
  12.     CC_ERR  err_no;  
  13.     char    *err_str;  
  14. }cc_err_info;  
  15.   
  16. extern  cc_err_info errs[];  
  17.   
  18. // global error number  
  19. extern  int         errno;  
  20.   
  21. void    cc_err(CC_ERR err_no);  
  22.   
  23. #endif  


error.c:

[cpp]   view plain copy
  1. #include <stdio.h>  
  2. #include "error.h"  
  3.   
  4. cc_err_info errs[] =   
  5. {  
  6.     {   CC_OK,              "no error"},  
  7.     {   CC_ERR_NOMEM,       "no enough mem"}  
  8. };  
  9.   
  10. int         errno;  
  11.   
  12. void    cc_err(CC_ERR err_no)  
  13. {  
  14.     printf("%s\n", errs[err_no].err_str);  
  15.     errno = CC_ERR_NOMEM;  
  16. }  


A: 文件中函数前面的cc是什么?


Q: 它是我的标志。

A: 那好吧。现在可以解决第二个问题了。


Q: 为了将不同的处理分离,下面首先把main函数的代码转移:

入口文件main.c:

 

[cpp]   view plain copy
  1. #include <stdio.h>  
  2. #include "internal_main.h"  
  3.   
  4. int main(int argc, const char * argv[])  
  5. {  
  6.     return  cc_internal_main(argc, argv);  
  7. }  


 

internal_main.h:

 

[cpp]   view plain copy
  1. #ifndef CCSH_INTERNAL_MAIN_H  
  2. #define CCSH_INTERNAL_MAIN_H  
  3.   
  4. int cc_internal_main(int argc, const char *argv[]);  
  5.   
  6. static int cc_process_string(char *str);  
  7.   
  8. #endif  


interl_main.c:

[cpp]   view plain copy
  1. #include <stdio.h>  
  2. #include "internal_main.h"  
  3. #include <string.h>  
  4. #include "buf_to_arglist.h"  
  5. #include "error.h"  
  6. #include "str_process.h"  
  7.   
  8.   
  9. int cc_internal_main(int argc, const char *argv[])  
  10. {  
  11.     char    buf[4096];  
  12.     char    *temp_buf = (char *)buf;  
  13.       
  14. repeat:    
  15.     cc_print_tipinfo();  
  16.   
  17.     memset(buf, 0, sizeof(buf));  
  18.     temp_buf = fgets(buf, sizeof(buf)), stdin);  
  19.   
  20.     if(temp_buf == NULL)  
  21.     {  
  22.         goto repeat;  
  23.     }  
  24.     else  
  25.     {  
  26.         cc_process_string(buf);  
  27.         goto repeat;  
  28.     }  
  29.       
  30.     return 0;  
  31. }  
  32.   
  33. static int cc_process_string(char *str)  
  34. {  
  35.     if(str[0] == '\n')  
  36.         return 0;  
  37.     str[strlen(str) - 1] = '\0';  
  38.       
  39.     cc_arg_list *list = cc_buf_to_arglist(str);  
  40.     if(!list)  
  41.     {  
  42.         return errno;  
  43.     }  
  44.     // cc_arg_list_show_all_args(list);  
  45.     if(cc_str_is(list->head->str, "echo"))  
  46.     {  
  47.         cc_execute_echo(list, str);  
  48.     }  
  49.     cc_arg_list_free(list);  
  50.       
  51.     return 0;  
  52. }  


第三个问题,显示提示符:

tip_info.h:

 

[cpp]   view plain copy
  1. #ifndef CCSH_TIP_INFO_H  
  2. #define CCSH_TIP_INFO_H  
  3.   
  4. void    cc_print_tipinfo();  
  5.   
  6. #endif  


tip_info.c:

[cpp]   view plain copy
  1. #include <stdio.h>  
  2. #include "tip_info.h"  
  3.   
  4. void    cc_print_tipinfo()  
  5. {  
  6.     printf("$");  
  7. }  


 

echo.h:

 

[cpp]   view plain copy
  1. #ifndef CCSH_ECHO_H  
  2. #define CCSH_ECHO_H  
  3.   
  4. #include "arglist.h"  
  5.   
  6. int     cc_execute_echo(cc_arg_list    *arg_list, const char *buf);  
  7.   
  8. #endif  


echo.c:

[cpp]   view plain copy
  1. #include <stdio.h>  
  2. #include "echo.h"  
  3. #include "str_process.h"  
  4. #include <string.h>  
  5.   
  6. int     cc_execute_echo(cc_arg_list    *arg_list, const char *buf)  
  7. {  
  8.     cc_arg_obj *arg = arg_list->head->next;  
  9.     if(!arg)  
  10.         return 0;  
  11.     if(cc_str_begin_with(arg->str, '-'))  
  12.     {  
  13.         size_t len = strlen(arg->str);  
  14.         if(len != 2)  
  15.         {  
  16.             printf("%s\n", arg->buf_pointer);  
  17.             return 0;  
  18.         }  
  19.         else  
  20.         {  
  21.             if(arg->str[1] == 'n')  
  22.             {  
  23.                 arg = arg->next;  
  24.                 printf("%s", arg->buf_pointer);  
  25.                 return 0;  
  26.             }  
  27.             else  
  28.             {  
  29.                 printf("%s\n", arg->buf_pointer);  
  30.                 return 0;  
  31.             }  
  32.         }  
  33.     }  
  34.     else  
  35.     {  
  36.         printf("%s\n", arg->buf_pointer);  
  37.         return 0;  
  38.     }  
  39.       
  40.     return 0;  
  41. }  

 


A: 上面的代码是只处理了echo命令(它可以替代之前说的print命令),运行一下:

做一个简单的解释器----小话c语言

echo命令的-n参数表示不输出最后的换行。现在将之前的hello, ver和quit, exit命令都加上吧。


Q: version.h:

 

[cpp]   view plain copy
  1. #ifndef CCSH_VERSION_H  
  2. #define CCSH_VERSION_H  
  3.   
  4. void    cc_show_version();  
  5.   
  6. #endif  


version.c:

 

[cpp]   view plain copy
  1. #include <stdio.h>  
  2. #include "version.h"  
  3.   
  4. void    cc_show_version()  
  5. {  
  6.     printf("ccteam shell 1.0\n");  
  7. }  


修改后的cc_process_string函数如下:

 

[cpp]   view plain copy
  1. static int cc_process_string(char *str)  
  2. {  
  3.     if(str[0] == '\n')  
  4.         return 0;  
  5.     str[strlen(str) - 1] = '\0';  
  6.       
  7.     cc_arg_list *list = cc_buf_to_arglist(str);  
  8.     if(!list)  
  9.     {  
  10.         return errno;  
  11.     }  
  12.     // cc_arg_list_show_all_args(list);  
  13.     if(cc_str_is(list->head->str, "echo"))  // like print command  
  14.     {  
  15.         cc_execute_echo(list, str);  
  16.     }  
  17.     else if(cc_str_is(list->head->str, "hello"))  
  18.     {  
  19.         printf("hello, i am a interpreter!\n");  
  20.     }  
  21.     else if(cc_str_is(list->head->str, "ver"))  
  22.     {  
  23.         cc_show_version();  
  24.     }  
  25.     else if(cc_str_is(list->head->str, "quit") || cc_str_is(list->head->str, "exit"))  
  26.     {  
  27.         exit(0);  
  28.     }  
  29.     else  
  30.     {  
  31.         printf("no such command...\n");  
  32.     }  
  33.     cc_arg_list_free(list);  
  34.       
  35.     return 0;  
  36. }  


上面对于不同输入的具体处理,有一些已经分离出去了。工程保存为ccsh,

运行结果:

做一个简单的解释器----小话c语言


A: 不过,对于一个类似bash或者python解释器,上面做的仅仅是很初步的功能,对于复杂语法解析还没有涉及;但可以肯定的是,如果继续去扩展,这只是时间的问题。