C专家编程cdecl

理解所有分析过程的代码段

Page71(中文版)
你可以轻松地编写一个能够分析C语言的声明并把他们翻译成通俗语言的程序。事实上,为什么不?C语言声明的基本形式已经描述清楚。我们所需要的只是编写一段能够理解声明的形式并能够以图3-3的方式对声明进行分析的代码。为了简单起见,暂且忽略错误处理,而且在处理结构、枚举和联合时只简单地用"struct", "enum"和"union"来代表它们的具体内容。最后,这个程序假定函数的括号内没有参数列表

  • 编程挑战

编写一个程序,把C语言的声明翻译成通俗语言

这里有一个设计方案,主要的数据结构是一个堆栈,我们从左向右读取,把各个标记依次压入堆栈,直到读到标识符为止。然后我们继续向右读入一个标记,也就是标识符右边的那个标记。接着,观察标识符左边的那个标记(需要从堆栈中弹出)。数据结构大致如下:

struct token { 
    char type;
    char string[MAXTOKENLEN];
};
/* 保存第一个标识之前的所有标记 */
struct token stack[MAXTOKENS];

/* 保存刚读入的那个标记 */
struct token this;

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

deal_with_fuction_args(处理函数参数)
    当读取越过右括号')'后, 打印"函数返回"
deal_with_arrays(处理函数数组)
    当你读取"[size]"后,将其打印并继续向右读取。
deal_with_any_pointers(处理任何指针)
    当你从堆栈中读取"*"时, 打印"指向...的指针"并将其弹出堆栈。
deal_with_declarator(处理声明器)
    if this.type is '[' deal_with_arrays
    if this.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
    

这是一个小型程序,在过去的几年中已被编写过无数次,通常取名为"cdecl". The C Programming Language有一个cdecl的不完整版本,本书的cdecl程序则更为详尽。它支持类型限定符const和volatile。同时它还涉及结构、枚举和联合,尽管在这方面做了简化。你可以轻松地用这个版本的程序来处理函数中的参数声明。这个程序可以用大约150行C代码实现。如果加入错误吹,并使程序能够处理的声明范围更广一些,程序就会更长一些。无论如何,当编制这个解析器时,相当于正在实现编译器中主要的子系统之一----这是一个相当了不起的编程成就,能够帮助你获得对这个领域的深刻理解。

更多的阅读材料

既然已经精通了在C语言中创建数据结构的方法,可能会对那些讲述通用目的的数据结构书感兴趣。其中一本是Data Structures with Abstract Data Types, Daniel F.Stubb和Neil W.Webre著,第二版,Pacific Grove, CA, Brooks/Cole, 1989.
这本书覆盖了范围很广的数据结构,包括字符串、列表、堆栈、队列、树、堆、集合和图。我推荐此书。

    #include <stdio.h>
    #include <string.h>
    #include <ctype.h>
    #include <stdlib.h>
    #define MAXTOKENS 100
    #define MAXTOKENLEN 64
    
    enum type_tag { IDENTIFIER, QUALIFIER, TYPE };
    
    struct token{
        char type;
        char string[MAXTOKENLEN];
        };
    
    int top = -1;
    struct token stack[MAXTOKENS];
    struct token this;
    
    #define pop stack[top--]
    #define push(s) stack[++top] = s
   
   /* 推断标识符的类型 */ 
    enum type_tag classify_string(void)
    {
        char *s = this.string;
        if (strcmp(s, "const") == 0) {
            strcpy(s, "read-only");
            return QUALIFIER;
        }
        
        if (strcmp(s, "volatile") == 0) {
            return QUALIFIER; 
        }
        if (strcmp(s, "void") == 0) {
            return TYPE; 
        }
        if (strcmp(s, "char") == 0) {
            return TYPE;
        }
        if (strcmp(s, "signed") == 0) {
            return TYPE;
        }
        if (strcmp(s, "unsigned") == 0) {
            return TYPE;
        }
        if (strcmp(s, "short") == 0) {
            return TYPE;
        }
        if (strcmp(s, "int") == 0) {
            return TYPE;
        }
        if (strcmp(s, "long") == 0) {
            return TYPE;
        }
        if (strcmp(s, "float") == 0) {
            return TYPE;
        }
        if (strcmp(s, "double") == 0) {
            return TYPE;
        }
        if (strcmp(s, "struct") == 0) {
            return TYPE;
        }
        if (strcmp(s, "union") == 0) {
            return TYPE;
        }
        if (strcmp(s, "enum") == 0) {
            return TYPE;
        }
        
        return INDENTIFIER;
    }
  
  /* 读取下一个标记到"this" */ 
   void gettoken(void)
   {
        char *p = this.string;
        /* 略过空白字符 */
        while((*p = getchar()) == ' ') {
            ;
        }
       
       /* 读入的标识符以A-Z, 0-9开头 */ 
        if (isalnum(*p)) {
             while( isalnum(*++p = getchar()) ) {
                ; 
             }
             
             ungetc(*p, stdin);
             *p = '\0';
             this.type = classify_string();
             return ;
        }
        if ( *p == '*' ) {
            strcpy(this.string, "pointer to ");
            this.type = '*';
            return;
        }
        
        this.string[1] = '\0';
        this.type = *p;
        return;
   }
   /* 理解所有分析过程的代码段 */
   void read_to_first_identifer() {
        gettoken();
        while( this.type != IDENTIFIER ) {
            push(this);
            gettoken();
        }
        printf("%s is ", this.string);
        gettoken();
    }
    
    void deal_with_arrays() {
        while( this.type == '[') {
            printf("array ");
            gettoken(); /* 数字或 ']' */
            if (isdigit(this.string[0])) {
                printf("0..%d ", atoi(this.string)-1 );
                gettoken(); /* 读取']' */
            }
            gettoken(); /* 读取']'之后的再一个标记 */
            printf("of ");
        }
    }
    void deal_with_function_args() {
        while(this.type != ')' ) {
            gettoken();
        }
        gettoken();
        printf("function returning ");
    }
    
    void deal_with_pointers() {
        while(stack[top].type == '*') {
            printf("%s ", pop.string);
        }
    }
   
   /* 处理标识符之后可能存在的数组/函数 */ 
    void deal_with_declarator() {
        switch(this.type) {
            case '[' : deal_with_arrays();
                        break;
            case '(' : deal_with_function_args();
                        break;
        }
        
        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("\n");
        return 0;
    }
posted @ 2015-10-20 23:40  drfxiaoliuzi  阅读(465)  评论(0编辑  收藏  举报