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;
}