• C语言 一个简单的声明语义分析器



      前面我们已经学会了如何理解声明:https://www.cnblogs.com/surplusvalue/p/12123398.html

      事实上,在我们读源码的时候,或许也会遇到错综复杂的声明语句,为什么不写一个程序帮助我们理解呢?接下来我们将编写一个能够分析C语言的声明并把它们翻译成通俗语言的程序。为了简单起见,暂且忽略错误处理,而且在处理结构、枚举和联合时只简单地用“struct”、“enum”和“union”来代表它们的具体内容。最后,这个程序假定函数的括号内没有参数列表(实际上我们在分析的时候,参数列表也被忽略了)。


      主要的数据结构是一个堆栈,我们从左向右读取,把各个标记依次压入堆栈,直到读到标识符为止。然后我们向右读入一个标记,也就是标识符右边的那个标记。接着观察标识符左边的那个标记(需要从堆栈中弹出)。数据结构大致如下:
    struct token {
        char type;
        char string[MAXTOKENLEN];
    };
    
    /* 保存第一个标识之前的所有标记 */
    struct token stack[MAXTOKENS];
    
    /* 保存刚读入的那个标记 */
    struct token t;

    伪码如下:

    实用程序----------
    classify_string(字符串分类)
        查看当前标记,
        通过t.type返回一个值,内容为“type(类型)”,“qualifier(限定符)”或“identifier(标识符)”
    gettoken(取标记)
    把下一个标记读入t.string
        如果是字母数字组合,调用classify_string
        否则,它必是一个单字符标记,t.type=该标记;用一个null结束t.string
    read_to_first_identifier(读至第一个标识符)
        调用gettoken,并把标记压入到堆栈中,直到遇见第一个标识符。
        Print“identifier is (标识符是)”,t.string
        继续调用gettoken

    解析程序----------
    deal_with_function_args(处理函数参数)
      当读取越过右括号‘)’后,打印“函数返回”
    deal_with_arrays(处理函数数组)
      当你读取“[size]”后,将其打印并继续向右读取。
    deal_with_any_pointers(处理任何指针)
      当你从堆栈中读取“*”时,打印“指向...的指针”并将其弹出堆栈。
    deal_with_declarator(处理声明器)
      if t.type is '[' deal_with_arrays
      if t.type is '(' deal_with_function_args
      deal_with_any_pointers
      while 堆栈里还有东西
      if 它是一个左括号'('
      将其弹出堆栈,并调用gettoken;应该获得右括号')'
      deal_with_declarator
      else 将其弹出堆栈并打印它

    主程序----------
    main
      read_to_first_identifier
      deal_with_declarator

    代码如下:

    #pragma warning( disable : 4996)
    #define _CRT_SECURE_NO_WARNINGS
    #include <stdio.h>
    #include <string.h>
    #include <ctype.h>
    #include <stdlib.h>
    
    #define MAXTOKENS 100
    #define MAXTOKENLEN 64
    
     //标识符identifier,修饰词qualifier,类型type(具体的例子见classify_string函数)
    enum type_tag { IDENTIFIER, QUALIFIER, TYPE };
    struct token {
        char type;                        //词素的类型
        char string[MAXTOKENLEN];        //保存词素的内容
    };
    
    int top = -1;        //栈顶指针
    //从左到右依次读取,把各个标记依次压入堆栈;保存第一个标识之前的所有标记的堆栈
    struct token stack[MAXTOKENS];    
    struct token t;        //保存刚读入的那个标记;
    
    //出栈和入栈操作
    #define pop stack[top--]
    #define push(s) stack[++top]=s
    
    enum type_tag classify_string(void)
        //推断标识符的类型
    {
        char *s = t.string;
        if (!strcmp(s, "const"))
        {//复习strcmp,两字符串相同,返回0
            strcpy(s, "read-only");
            return QUALIFIER;
        }
        if (!strcmp(s, "volatile")) return QUALIFIER;
        if (!strcmp(s, "void")) return TYPE;
        if (!strcmp(s, "char")) return TYPE;
        if (!strcmp(s, "signed")) return TYPE;
        if (!strcmp(s, "unsigned")) return TYPE;
        if (!strcmp(s, "short")) return TYPE;
        if (!strcmp(s, "int")) return TYPE;
        if (!strcmp(s, "long")) return TYPE;
        if (!strcmp(s, "float")) return TYPE;
        if (!strcmp(s, "double")) return TYPE;
        if (!strcmp(s, "struct")) return TYPE;
        if (!strcmp(s, "union")) return TYPE;
        if (!strcmp(s, "enum")) return TYPE;
        return IDENTIFIER;    //上述结构都不是的情况,就是标识符
    }
    
    void gettoken(void) // 读取下一个标记到 "t" 
    {
        char *p = t.string;
    
        // 略过空白字符
        while ((*p = getchar()) == ' ');
    
        if (isalnum(*p))
        {
            // 读入的标识符以A-Z, 0-9开头 
            while (isalnum(*++p = getchar()));
            ungetc(*p, stdin);
            *p = '';
            t.type = classify_string();
            return;
        }
    
        if (*p == '*')
        {
            strcpy(t.string, "pointer to");
            t.type = '*';
            return;
        }
        t.string[1] = '';
        t.type = *p;
        return;
    }
    
    // 理解所有分析过程的代码段
    void read_to_first_identifier()
    {
        gettoken();        //把标记压入到堆栈,直到遇见第一个标识符
        while (t.type != IDENTIFIER)
        {    //只要没有遇见标识符,就将t压栈,继续取标记,直到遇见第一个标识符
            push(t);
            gettoken();
        }
        printf("%s is ", t.string);    //这条分析语句的主语是while循环后得到的标识符
        gettoken();    //继续向右读入一个标记,也就是标识符右边的标记
    }
    
    void deal_with_arrays()
    {
        while (t.type == '[')
        {
            printf("array ");
            gettoken(); // 数字或 ']' 
            if (isdigit(t.string[0]))
            {
                printf("0..%d ", atoi(t.string) - 1);
                gettoken(); // 读取 ']'
            }
            gettoken(); // 读取 ']' 之后的再一个标记 
            printf("of ");//这个数组是:
        }
    }
    
    void deal_with_function_args()
    {
        while (t.type != ')')
        {
            gettoken();//忽略函数的参数列表
        }
        gettoken();//读到和左括号匹配的右括号,再读括号后的一个标记
        printf("function returning ");
    }
    
    void deal_with_pointers()
    {
        while (stack[top].type == '*')
        {
            printf("%s ", pop.string);//* 号出栈,输出pointer to
        }
    }
    
    void deal_with_declarator()
    {
        // 处理标识符之后可能存在的数组或函数
        switch (t.type)
        {
        case '[': deal_with_arrays(); break;
        case '(': deal_with_function_args();
        }
        //观察标识符左边的标记
        deal_with_pointers();
    
        // 处理在读入到标识符之前压入到堆栈中的符号
        while (top >= 0)
        {    //直到栈空
            if (stack[top].type == '(')
            {
                pop;
                gettoken(); // 读取 ')'之后的符号 
                deal_with_declarator();//这个可以对着例子学习,如果这对括号后面还有标记就继续处理
            }
            else
            {
                printf("%s ", pop.string);
            }
        }
    }
    
    int main()
    {
        // 将标记压入堆栈中,直到遇见标识符
        read_to_first_identifier();
        // 处理标识符右边的那个标记
        deal_with_declarator();
        printf("
    ");
        return 0;
    }

     运行结果:

     

    错误解决

    触发断点的解决办法  https://blog.csdn.net/leowinbow/article/details/82380252

  • 相关阅读:
    umeng社交分享最新版5.0的跨进程使用崩溃的问题及解法-Android
    AlertDialog禁止返回键
    一个男人想经商,不读 100本商人自传,怎么会了解商人的思维状态
    Android中使用Gson解析JSON数据的两种方法
    DevExpress gridControl控件动态绑定列 zt
    获得WCF Client端的本地端口 z
    log4net.dll配置以及在项目中应用 zt
    系统交易策略 hylt
    判斷作業系統為 64bit 或 32bit z
    路徑 z
  • 原文地址:https://www.cnblogs.com/surplusvalue/p/12124960.html
Copyright © 2020-2023  润新知