您的位置:首页 > 编程语言

little c原代码分析[一]

2004-08-01 23:20 447 查看
此代码为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 */

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: