此代码为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 */
}
}