To understand the interpreter, make a small interpreter-Xiaohua C language (20)

Source: Internet
Author: User
Tags echo command

Author: Chen Xi

Date: 11:31:12

Environment: [Mac 10.7.1 lion intel-based x64 gcc4.2.1 xcode4.2]

Reprinted please indicate the source

Q: What does the interpreter come from?

A: If it is a broad interpreter, you can understand it as a translator, as long as you can translate a thing that is regarded as original into what you need, processing can be called an interpreter. From the programming language perspective, the interpreter expresses more in the meaning that data in an initial state (generally text) conversion to another type of text or execution process that is generally easier to understand.

The interpreter is derived from natural language machines and is not easy to understand.

Q: Let's try a C language interpreter.

A: Rome was not built in one day. Let's make a simple one. This simple interpreter makes us feel that we don't need to do it, but we still need to do it.

It mainly implements the following simple functions:

The interpreter name is simple_interpreter. Execute it in the command line to run the interpreter;

1. After Entering hello, it will prompt the text: Hello, I am a interpreter!

2. After you enter the ver, it will prompt the text: Version: 1.0.

3. Input print [String], which outputs the corresponding string without any delimiters.

4. After you enter exit or quit, the interpreter will be disabled.

5. If other commands are input, no such command is output.

Q: The following Code is based on the above requirements.

#include <stdio.h>#include <string.h>#include <stdlib.h>#include "str_process.h"int main (int argc, const char * argv[]){    char    buf[4096] = {0};    size_t  buf_size = sizeof(buf);    char*   ret;        // input from stdin    while (1)    {        ret = fgets((char *)&buf, (int)buf_size, stdin);        if(ret == NULL) // error or eof        {            if(ferror(stdin))                printf("error occurs...terminating now...\n");            else if(feof(stdin))                printf("eof occurs...terminating now...\n");        }        else    // input sth        {            // set the last '\n' to NULL            if(buf[strlen(buf) - 1] == '\n')                buf[strlen(buf) - 1] = '\0';                        if(!strcmp(buf, "hello"))            {                printf("hello, i am a interpreter!\n");            }            else if(!strcmp(buf, "ver"))            {                printf("version:1.0\n");            }            else if(!strncmp(buf, "print", strlen("print")))            {                char* temp = buf + strlen("print");                if(*temp == ' ')                {                    cc_skip_blank(&temp);                       printf("%s\n", temp);                }                else if(*temp == '\0')                {                   printf("\n");                 }                else                {                    printf("no such command\n");                }            }            else if(!strcmp(buf, "exit") || !strcmp(buf, "quit"))            {                exit(0);            }            else            {                printf("no such command\n");            }        }    }        return 0;}

Save as simple_interpreter.c;

The str_process.h and str_process.c codes are as follows:

#ifndef CCSH_STR_PROCESS_H#define CCSH_STR_PROCESS_H#include <stdbool.h>bool    cc_is_blank(char ch);char    *cc_get_next_blank(const char *str);void    cc_skip_blank(char  **str);bool    cc_str_is(const char *str1, const char *str2);bool    cc_str_begin_with(const char *str, char ch);#endif

#include <stdio.h>#include "str_process.h"#include <string.h>inline bool    cc_is_blank(char ch){    return ch == '\n'         || ch == '\t'         || ch == ' ';}char *cc_get_next_blank(const char *str){    while (!cc_is_blank(*str) && *str != '\0')        ++str;    return (char *)str;}void    cc_skip_blank(char  **str){    while(cc_is_blank(**str) && *str != '\0')    {        (*str)++;    }}bool    cc_str_is(const char *str1, const char *str2){    return strcmp(str1, str2) == 0;}inline bool    cc_str_begin_with(const char *str, char ch){    return str[0] == ch;}

 

The project generates simple_interpreter and runs:

A: Yes. The above code can be executed as required. However, it has its disadvantages. First, the format is too fixed. If you enter another space, it may cause the error of no such command; second, the codes for different input processing are too concentrated. If some code is added, code maintenance may be very problematic. Third, the input and output above does not have a flag, it is easy to confuse.

Q: If you need to solve the first problem, you need to parse the input text, remove spaces, tabs, and other information, and leave useful information. If you want to solve the second problem, you can use separate functions of different files to process different conditional branches. The third problem is to add a prompt, similar to $.

A: The following figure shows the first problem:

Q: Convert the buffer data to a parameter list. The Code is as follows:

Arglist. h:

#ifndef CCSH_ARGLIST_H#define CCSH_ARGLIST_Htypedef struct _cc_arg_obj{    char    *str;    size_t  len;    struct  _cc_arg_obj    *next;    char    *buf_pointer;       // the pointer that points the buf, for possible use, eg. echo command}cc_arg_obj;typedef struct _cc_arg_list{    cc_arg_obj              *head;    cc_arg_obj              *tail;}cc_arg_list;cc_arg_obj  *cc_arg_obj_make(const char *str,                              size_t len,                              cc_arg_obj *next,                             char       *buf_pointer);void        cc_arg_obj_free(cc_arg_obj *obj);cc_arg_list *cc_arg_list_make(cc_arg_obj    *head);cc_arg_obj *cc_arg_list_append(cc_arg_list *list, cc_arg_obj     *obj);void        cc_arg_list_free(cc_arg_list    *list);void        cc_arg_list_show_all_args(cc_arg_list   *list);#endif

Arglist. C:

#include <stdio.h>#include "arglist.h"#include "common.h"#include "error.h"cc_arg_obj  *cc_arg_obj_make(const char *str,                              size_t len,                              cc_arg_obj *next,                             char       *buf_pointer){    cc_arg_obj  *obj = (cc_arg_obj *)malloc(sizeof(cc_arg_obj));    if(!obj)    {        cc_err(CC_ERR_NOMEM);        return NULL;    }    char *obj_str = (char *)malloc(len + 1);    if(!obj_str)    {        cc_err(CC_ERR_NOMEM);        free(obj);        return NULL;    }    strncpy(obj_str, str, len);    obj->str = obj_str;    obj->len = len;    obj->next = next;    obj->buf_pointer = buf_pointer;    return obj;}void        cc_arg_obj_free(cc_arg_obj *obj){    free(obj->str);    free(obj);}cc_arg_list *cc_arg_list_make(cc_arg_obj    *head){    cc_arg_list *list = (cc_arg_list *)malloc(sizeof(cc_arg_list));    if(!list)    {        cc_err(CC_ERR_NOMEM);        return NULL;    }        list->head = list->tail = head;    return list;}cc_arg_obj *cc_arg_list_append(cc_arg_list *list, cc_arg_obj     *obj){    if(list->head == NULL)    {        list->head = list->tail = obj;        return obj;    }    list->tail->next = obj;    list->tail = obj;    return obj;}void        cc_arg_list_free(cc_arg_list    *list){    cc_arg_obj *head = list->head;    while(head)    {        cc_arg_obj *next = head->next;        cc_arg_obj_free(head);        head = next;    }}void        cc_arg_list_show_all_args(cc_arg_list   *list){    cc_arg_obj *head = list->head;    while (head != NULL)    {        printf("arg:%s", head->str);        head = head->next;    }}

Buf_to_arglist.h:

#ifndef CCSH_BUF_TO_ARGLIST_H#define CCSH_BUF_TO_ARGLIST_H#include "arglist.h"cc_arg_list *cc_buf_to_arglist(const char *buf);#endif

Buf_to_arglist.c:

#include <stdio.h>#include "buf_to_arglist.h"#include <stdlib.h>#include "error.h"#include "str_process.h"cc_arg_list *cc_buf_to_arglist(const char *buf){    char    *temp = (char *)buf;    cc_arg_list *list = cc_arg_list_make(NULL);    if(!list)    {        cc_err(CC_ERR_NOMEM);        return NULL;    }    while (*temp)    {        char    *next_blank = cc_get_next_blank(temp);        if(temp != next_blank)        {            size_t len = next_blank - temp;            cc_arg_obj *obj = cc_arg_obj_make(temp, len, NULL, temp);            if(!obj)            {                cc_err(CC_ERR_NOMEM);                cc_arg_list_free(list);                return NULL;            }            cc_arg_list_append(list, obj);        }        temp = next_blank;        cc_skip_blank(&temp);    }    return list;}

In addition, common. h:

#ifndef CCSH_COMMON_H#define CCSH_COMMON_H#include <stdlib.h>#include <stdbool.h>#include <string.h>#endif

Error. h:

#ifndef CCSH_ERROR_H#define CCSH_ERROR_Htypedef enum {    CC_OK,    CC_ERR_NOMEM}CC_ERR;typedef struct {    CC_ERR  err_no;    char    *err_str;}cc_err_info;extern  cc_err_info errs[];// global error numberextern  int         errno;void    cc_err(CC_ERR err_no);#endif

Error. C:

#include <stdio.h>#include "error.h"cc_err_info errs[] = {    {   CC_OK,              "no error"},    {   CC_ERR_NOMEM,       "no enough mem"}};int         errno;void    cc_err(CC_ERR err_no){    printf("%s\n", errs[err_no].err_str);    errno = CC_ERR_NOMEM;}

A: What is CC before the function in the file?

Q: It is my logo.

A: Okay. Now we can solve the second problem.

Q: In order to separate different processes, the code of the main function is first transferred:

Main. C:

#include <stdio.h>#include "internal_main.h"int main(int argc, const char * argv[]){    return  cc_internal_main(argc, argv);}

Internal_main.h:

#ifndef CCSH_INTERNAL_MAIN_H#define CCSH_INTERNAL_MAIN_Hint cc_internal_main(int argc, const char *argv[]);static int cc_process_string(char *str);#endif

Interl_main.c:

#include <stdio.h>#include "internal_main.h"#include <string.h>#include "buf_to_arglist.h"#include "error.h"#include "str_process.h"int cc_internal_main(int argc, const char *argv[]){    char    buf[4096];    char    *temp_buf = (char *)buf;    repeat:      cc_print_tipinfo();    memset(buf, 0, sizeof(buf));    temp_buf = fgets(buf, sizeof(buf)), stdin);    if(temp_buf == NULL)    {        goto repeat;    }    else    {        cc_process_string(buf);        goto repeat;    }        return 0;}static int cc_process_string(char *str){    if(str[0] == '\n')        return 0;    str[strlen(str) - 1] = '\0';        cc_arg_list *list = cc_buf_to_arglist(str);    if(!list)    {        return errno;    }    // cc_arg_list_show_all_args(list);    if(cc_str_is(list->head->str, "echo"))    {        cc_execute_echo(list, str);    }    cc_arg_list_free(list);        return 0;}

The third question is that the prompt is displayed:

Tip_info.h:

#ifndef CCSH_TIP_INFO_H#define CCSH_TIP_INFO_Hvoid    cc_print_tipinfo();#endif

Tip_info.c:

#include <stdio.h>#include "tip_info.h"void    cc_print_tipinfo(){    printf("$");}

Echo. h:

#ifndef CCSH_ECHO_H#define CCSH_ECHO_H#include "arglist.h"int     cc_execute_echo(cc_arg_list    *arg_list, const char *buf);#endif

Echo. C:

#include <stdio.h>#include "echo.h"#include "str_process.h"#include <string.h>int     cc_execute_echo(cc_arg_list    *arg_list, const char *buf){    cc_arg_obj *arg = arg_list->head->next;    if(!arg)        return 0;    if(cc_str_begin_with(arg->str, '-'))    {        size_t len = strlen(arg->str);        if(len != 2)        {            printf("%s\n", arg->buf_pointer);            return 0;        }        else        {            if(arg->str[1] == 'n')            {                arg = arg->next;                printf("%s", arg->buf_pointer);                return 0;            }            else            {                printf("%s\n", arg->buf_pointer);                return 0;            }        }    }    else    {        printf("%s\n", arg->buf_pointer);        return 0;    }        return 0;}

A: The above Code only processes the echo command (it can replace the print command mentioned earlier). Run:

The-N parameter of the ECHO command does not output the final line feed. Add the preceding hello, Ver, quit, and Exit commands.

Q: version. h:

#ifndef CCSH_VERSION_H#define CCSH_VERSION_Hvoid    cc_show_version();#endif

Version. C:

#include <stdio.h>#include "version.h"void    cc_show_version(){    printf("ccteam shell 1.0\n");}

The modified cc_process_string function is as follows:

static int cc_process_string(char *str){    if(str[0] == '\n')        return 0;    str[strlen(str) - 1] = '\0';        cc_arg_list *list = cc_buf_to_arglist(str);    if(!list)    {        return errno;    }    // cc_arg_list_show_all_args(list);    if(cc_str_is(list->head->str, "echo"))  // like print command    {        cc_execute_echo(list, str);    }    else if(cc_str_is(list->head->str, "hello"))    {        printf("hello, i am a interpreter!\n");    }    else if(cc_str_is(list->head->str, "ver"))    {        cc_show_version();    }    else if(cc_str_is(list->head->str, "quit") || cc_str_is(list->head->str, "exit"))    {        exit(0);    }    else    {        printf("no such command...\n");    }    cc_arg_list_free(list);        return 0;}

The specific processing of different inputs has been separated. Save the project as ccsh,

Running result:

A: however, for a bash or Python interpreter, the above is just a very preliminary function, not involved in complex Syntax Parsing; but surely, if you continue to expand, this is only a matter of time.

Author: Chen Xi

Date: 11:31:12

Environment: [Mac 10.7.1 lion intel-based x64 gcc4.2.1 xcode4.2]

Reprinted please indicate the source

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.