此代码为c语言大全这本书作者实现的little c解释器的原代码,下面是部分分析。这是第1篇文章
/* Get a token. */ /* -------------------------------------------------------------------- */ /* 功能:此程序是从字符流中取得一个”单词“,把单词从字符中匹配出来 */ /* -------------------------------------------------------------------- */ /* 变量说明: */ /* token_type:这个变量是用来说明组成的关键字和的类型的类型 */ /* tok: 此为存放关键字 */ /* token: 此为存放字符串,数字等所有符号的值 */ /* temp: 此为token指针的缓冲区,主要是解决了指针移动的方便的问题 */ /* --------------------------------------------------------------------- */ int get_token(void) {
register char *temp;
token_type = 0; tok = 0;
temp = token; /* temp 为token的缓冲值 */ *temp = '/0';
/* skip over white space */ while(iswhite(*prog) && *prog) ++prog; /* iswhite标准库函数在此的功能是跳过原代码中的关键字,如果是空白符号的话,那么就跳过 */
if(*prog == '/r') { ++prog; ++prog; /* 如果是换行符的话,也跳过,字符流指针prog地址加1 */ /* skip over white space */ while(iswhite(*prog) && *prog) ++prog; } /* 在新行中如果有空白符号的话,那么也是忽略掉 */
if(*prog == '/0') { /* end of file */ /* 如果prog指针到达文件结尾,prog的值中存放的是'/0' */ *token = '/0'; /* *token给值为'/0' */ tok = FINISHED; /* tok变量为本解释器的内部关键字类型,这里是表示达到文件结尾,结束 */ return (token_type = DELIMITER); / } /* 此为程序块 */ if(strchr("{}", *prog)) { /* block delimiters */ *temp = *prog; temp++; *temp = '/0'; prog++; return (token_type = BLOCK); }
/* look for comments */ /* 处理注释部分,如果是/ '+' *字符开头的,表示其是注释,然后是以* '+' /作为结尾的 */ if(*prog == '/') if(*(prog+1) == '*') { /* is a comment */ prog += 2; do { /* find end of comment */ while(*prog != '*') prog++; prog++; } while (*prog != '/'); prog++; } /* 处理! < > =这几个运算符号 */ if(strchr("!<>=", *prog)) { /* is or might be a relational operator */ switch(*prog) {
case '=': if(*(prog+1) == '=') { /* 如果=后面还是一个=符号的话,那么此单词为内部符号EQ */ prog++; prog++; *temp = EQ; temp++; *temp = EQ; temp++; *temp = '/0'; } break; case '!': if(*(prog+1) == '=') { /* 如果是!加=符号的话,那么prog就是内部符号NE */ prog++; prog++; *temp = NE; temp++; *temp = NE; temp++; *temp = '/0'; } break; case '<': if(*(prog+1) == '=') { /* 如果是<符号加=符号的话,那么就是内部符号LE */ prog++; prog++; *temp = LE; temp++; *temp = LE; } else { prog++; /* 否则就是内部符号LT */ *temp = LT; } temp++; *temp = '/0'; break; case '>': if(*(prog+1) == '=') { /* 如果符号是>加=符号的话,那么就是内部运算符号布尔运算符号GE */ prog++; prog++; *temp = GE; temp++; *temp = GE; } else { prog++; /* 否则就是内部布尔运算符号GT */ *temp = GT; } temp++; *temp = '/0'; break; } if(*token) return (token_type = DELIMITER); } /* 下面是测试是否是算术运算符 */ if(strchr("+-*^/%=;(),'", *prog)){ /* delimiter */ *temp = *prog; prog++; /* advance to next position */ temp++; *temp = '/0'; return (token_type = DELIMITER); /* 返回类型为DELIMITER,此类型为算术运算符号 */ }
if(*prog=='"') { /* quoted string */ /* 如果是"符号的话,那么就表示是字符串 */ prog++; /* 只要不等于"符号,那么就把所有的东西全部放到temp里 */ while(*prog != '"'&& *prog != '/r') *temp++ = *prog++; if(*prog == '/r') sntx_err(SYNTAX); prog++; *temp = '/0'; return (token_type = STRING); /* 返回类型为字符串 */ }
if(isdigit(*prog)) { /* number */ /* 如果是一个数字的话,把所有的数字字符连接起来 */ while(!isdelim(*prog)) *temp++ = *prog++; *temp = '/0'; return (token_type = NUMBER); }
if(isalpha(*prog)) { /* var or command */ /* 如果是一个变量或是关键字的话,那么也是把符号组成单词 */ while(!isdelim(*prog)) *temp++ = *prog++; token_type = TEMP; }
*temp = '/0';
/* see if a string is a command or a variable */ if(token_type==TEMP) { tok = look_up(token); /* convert to internal rep */ /* 去符号表中查找是否是关键字 */ if(tok) token_type = KEYWORD; /* is a keyword */ /* 如果tok为真的话,那么这个单词就是关键字 */ else token_type = IDENTIFIER; /* 否则这个就是变量的名 */ } return token_type; }
listing 2 /* Display an error message. */ void sntx_err(int error) { char *p, *temp; int linecount = 0; register int i; /* 此字符指针数组里存放的是语法分析时的错误消息 */ static char *e[]= { "syntax error", "unbalanced parentheses", "no expression present", "equals sign expected", "not a variable", "parameter error", "semicolon expected", "unbalanced braces", "function undefined", "type specifier expected", "too many nested function calls", "return without call", "parentheses expected", "while expected", "closing quote expected", "not a string", "too many local variables", "division by zero" }; printf("/n%s", e[error]); p = p_buf; while(p != prog) { /* find line number of error */ p++; if(*p == '/r') { linecount++; } } printf(" in line %d/n", linecount);
temp = p; for(i=0; i < 20 && p > p_buf && *p != '/n'; i++, p--); for(i=0; i < 30 && p <= temp; i++, p++) printf("%c", *p);
longjmp(e_buf, 1); /* return to safe point */ }
listing 3 /* Recursive descent parser for integer expressions which may include variables and function calls. */ #include <setjmp.h> #include <math.h> #include <ctype.h> #include <stdlib.h> #include <string.h> #include <stdio.h>
#define NUM_FUNC 100 #define NUM_GLOBAL_VARS 100 #define NUM_LOCAL_VARS 200 #define ID_LEN 31 #define FUNC_CALLS 31 #define PROG_SIZE 10000 #define FOR_NEST 31 /* 标记类型 主要是标记是什么类型的,是数字,是字符,是块,是字符串等等.*/
enum tok_types {DELIMITER, IDENTIFIER, NUMBER, KEYWORD, TEMP, STRING, BLOCK}; /* 内部关键字值定义 */ enum tokens {ARG, CHAR, INT, IF, ELSE, FOR, DO, WHILE, SWITCH, RETURN, EOL, FINISHED, END}; /* 操作符定义 大于,等于,小于等算术操作符的内部形式 */ enum double_ops {LT=1, LE, GT, GE, EQ, NE};
/* These are the constants used to call sntx_err() when a syntax error occurs. Add more if you like. NOTE: SYNTAX is a generic error message used when nothing else seems appropriate. */ /* 错误消息的定义 */ enum error_msg {SYNTAX, UNBAL_PARENS, NO_EXP, EQUALS_EXPECTED, NOT_VAR, PARAM_ERR, SEMI_EXPECTED, UNBAL_BRACES, FUNC_UNDEF, TYPE_EXPECTED, NEST_FUNC, RET_NOCALL, PAREN_EXPECTED, WHILE_EXPECTED, QUOTE_EXPECTED, NOT_TEMP, TOO_MANY_LVARS, DIV_BY_ZERO}; /* prog函数是little c脚本语言的程序代码的指针变量 */ extern char *prog; /* current location in source code */ /* 缓冲prog,因为有时,分析可能会出现回朔,所以作者设置了此指针变量 */ extern char *p_buf; /* points to start of program buffer */ extern jmp_buf e_buf; /* hold environment for longjmp() */
/* An array of these structures will hold the info associated with global variables. */ /* 变量属性结构 */ extern struct var_type { char var_name[32]; /* 变量名字,名字最多允许32个字符 */ int v_type; /* 变量类型 */ int value; /* 变量的值 */ } global_vars[NUM_GLOBAL_VARS];
/* This is the function call stack. */ /* 函数调用堆栈 */ extern struct func_type { char func_name[32]; /* 函数名字 名字最大为32个字符*/ int ret_type; /* 返回类型 */ char *loc; /* location of function entry point in file */ } func_stack[NUM_FUNC];
/* Keyword table */ /* 关键字结构 */ extern struct commands { char command[20]; char tok; } table[];
/* "Standard library" functions are declared here so they can be put into the internal function table that follows. */ /* 下面的几个函数声明是little c内部函数 */ int call_getche(void), call_putch(void); int call_puts(void), print(void), getnum(void); /* 内部函数结构 */ struct intern_func_type { char *f_name; /* function name */ int (*p)(); /* pointer to the function */ } intern_func[] = { "getche", call_getche, "putch", call_putch, "puts", call_puts, "print", print, "getnum", getnum, "", 0 /* null terminate the list */ }; /* ----------------------------------------------------------- */ /* 变量说明: */ /* token: 此变量是存放单词符号 */ /* token_type: 此变量是存放单词的类型 */ /* tok: 此变量是存放内部关键字的值 */ /* ------------------------------------------------------------*/ extern char token[80]; /* string representation of token */ extern char token_type; /* contains type of token */ extern char tok; /* internal representation of token */ /* 存放用户定义的函数的返回值 */ extern int ret_value; /* function return value */ /* start 下面函数是表达式递归调用分析的子程序 */ void eval_exp0(int *value); void eval_exp(int *value); void eval_exp1(int *value); void eval_exp2(int *value); void eval_exp3(int *value); void eval_exp4(int *value); void eval_exp5(int *value); /* end */ /* 函数atom 是取得变量,表达式,函数等运算后的值,并存放在全局变量value里 */ void atom(int *value); /* 函数sntx_err是向屏幕上显示语法错误,通过整数参数变量error来解释不同的错误 */ /* 函数putback函数是返回一个字符流,就是在分析的时候可能会出现向前看一个字符,当看后需要在返回给原先的字符流 */ void sntx_err(int error), putback(void); /* assign_var函数是分配一个变量存储空间 */ void assign_var(char *var_name, int value); /* look_up函数是通过查找字符看是否是关键字表中的值,iswhite是查看字符是否是空白字符 */ int isdelim(char c), look_up(char *s), iswhite(char c); /* 函数find_var是查找一个变量的值 */ /* get_token函数是从当前的字符流中得到一个解释器内部的单词 */ int find_var(char *s), get_token(void); /* 从内部函数中查找是否有个*s的内部函数 */ int internal_func(char *s); /* ??????? */ int is_var(char *s); /* 找一个用户函数的位置,好使call()函数正确调用这个用户定义的函数 */ char *find_func(char *name); void call(void);
/*********************************************************************
函数名字:atom
*********************************************************************/
void atom(int *value)
{
int i;
switch(token_type) {
/* 选择token类型 */
case IDENTIFIER:
/* 如果是变量或函数 */
i = internal_func(token);
/* 从内部结构中查找函数名是否存在 */
if(i!= -1) { /* call "standard library" function */
/* 如果不是-1的话,那么表示是内部函数 */
*value = (*intern_func.p)();
/* 通过结构中的函数指针调用内部函数,返回的值放到 value指向地址里 */
}
else if(find_func(token)) { /* call user-defined function */
/* 否则通过函数find_func查找是否是用户定义的函数,如果是的话 */
call();
/* 通过call函数调用用户定义的函数 */
*value = ret_value;
/* 函数的返回值放到value指向的地址里 */
}
else *value = find_var(token); /* get var's value */
/* 否则就认为他是一个变量的名字,通过find_var函数找到token里存放到变量值,然后放到value里 */
get_token();
/* 返回 */
return;
case NUMBER: /* is numeric constant */
/* 如果是一个数字的话 那么通过标准库函数中的atoi(在stdio.h中定义了此函数) 把字符转化为数字类型,以方便表达式计算 */
*value = atoi(token);
get_token();
/* 返回 */
return;
case DELIMITER: /* see if character constant */
/* 如果是一个字符常量的话 */
if(*token == '/'') {
/* 如果是'字符,那么把当前的值放到value里 */
*value = *prog;
prog++;
if(*prog!='/'') sntx_err(QUOTE_EXPECTED);
/* 如果不是以'符号结尾,就抛出语法错误 */
prog++;
get_token();
return ;
}
if(*token==')') return; /* process empty expression */
else sntx_err(SYNTAX); /* syntax error */
default:
sntx_err(SYNTAX); /* syntax error */
}
} |
|