/*主要的数据结构是一个堆栈,我们从左向右读取,把各个标记依次压入堆栈,直到读到标识符为止然后我们继续向右读入一个标记,也就是标识符右边的那个标记。接着,观察标识符左边的那个标记(需要从堆栈中弹出)。
*/
struct token {
char type;
char string[MAXTOKENLEN];
};
/*保存第一个标识之前的所有标记*/
struct token stack[MAXTOKENS];
/*保存刚读入的那个标记*/
struct token this;
/*伪码如下:
实用程序-------------*/
classify_string(字符串分类)
/*查看当前的标记,
*通过this.type返回一个值,内容为"type"(类型),
*"qualifier"(限定符)或"identifier"标识符
*/
gettoken(取标记);
/*把下一个标记读入this.string
*如果是字母数字组合,调用classify_string
*否则,它必是一个单字符标记,this.type = 该标记;
*用一个nul结束this.string
*/
read_to_first_identifier(读至第一个标识符)
/*调用gettoken,并把标记压入到堆栈中,直到遇到第一个
*标识符
*Print"identifier is"(标识符是), this.string
*继续调用gettoken
*/
//解析程序
deal_with_function_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_declaration
#include
#include
#include
#include
#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_2;
#define pop stack[top--]
#define push(s) stack[++top] = s
enum type_tag classify_string(void)
/*判断标识符的类型*/
{
char *s = this_2.string;
if (!strcmp(s, "const")) {
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) /*读取下一个标记到this_2*/
{
char *p = this_2.string;
/*省略空白字符*/
while ((*p = getchar()) == ' ');
if (isalnum(*p)) {
/*读入的标识符以A-Z或0-9开头*/
while (isalnum(*++p = getchar()));
ungetc(*p, stdin);
*p = '\0';
this_2.type = classify_string();
return;
}
if (*p == '*') {
strcpy(this_2.string, "pointer to");
this_2.type = '*';
return;
}
this_2.string[1] = '\0';
this_2.type = *p;
return;
}
/*理解所有分析过程的代码段*/
void read_to_first_identifier() {
gettoken();
while (this_2.type != IDENTIFIER) {
push(this_2);
gettoken();
}
printf("%s is ", this_2.string);
gettoken();
}
void deal_with_arrays() {
while(this_2.type == '[') {
printf("array ");
gettoken(); /*数字或']'*/
if (isdigit(this_2.string[0])) {
printf("0..%d ", atoi(this_2.string) - 1);
gettoken(); /*读取']' */
}
gettoken(); /*读取']'之后的再一个标记*/
printf("of ");
}
}
void deal_with_function_args() {
while(this_2.type != ')') {
gettoken();
}
gettoken(); /*读取')'之后的再一个标记*/
printf("function returning ");
}
void deal_with_pointers() {
while (stack[top].type == '*') {
printf("%s ", pop.string);
}
}
deal_with_declarator() {
/*处理标识符之后可能存在的数组/函数*/
switch(this_2.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;
}
/* 输出:
*/
/*小启发*/
/*使字符串的比较看上去更自然
*strcmp()函数用于比较两个字符串:它所存在的一个问题是:当两个字符串相等时函数的返回值
*为零。当字符串比较是条件语句的一部分时,这个问题就是导致令人费解的代码:
*/
if (!strcmp(s, "volatile")) return QUALIFIER;
/*返回值零使条件语句的结果为假,所以我们不得不对其取反,得到需要的结果
*这里有一个更好的办法
*/
#define STRCMP(a, R, b) (strcmp(a, b) R 0)
if (STRCMP(s, ==, "volatile"))
Data Structures with Abstract Data Types
这本书覆盖了范围很广的数据结构,包括字符串、列表、堆栈、队列、树,堆、集合和图。
推荐大家阅读本书。