lastking的专栏

Nothing Lost,Nothing Gain!

little c原代码分析[一]



 

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

 }

}
阅读更多
个人分类: Compile编译技术
想对作者说点什么? 我来说一句

Little Kernel分析与移植

2013年10月11日 167KB 下载

little c解释器分析

2008年11月30日 8KB 下载

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭