只有140⾏代码的C语⾔解释器,C语⾔解释器Parser剖析器
——笔记与⼼得
THE LITTLE C INTERPRETER
表达式剖析器(THE EXPRESSION PARSER)
读取和分析表达式的这部分代码叫做表达式剖析器。毫⽆疑问,表达式剖析器是C解释器中单⼀的最重要的部分。因为C语⾔定义表达式的⽅式⽐其他语⾔更加粗鄙,所以⽤⼤量的代码组成的C源⽂件来实现表达式剖析器。
有⼏种不同的⽅式来设计C的表达式剖析器。许多商业的编译器⽤⼀种由parser-generator创建的table-driven parser。尽管table-driven parser⼀般来说要快过其他⽅式,但却很难⼿⼯构建。为了开发简易的C解释器,在这⾥我们使⽤递归-继承剖析器(recursive-descent parser.)
⼀个递归-继承剖析器本质上是⼀⼤堆处理表达式的互相递归的函数。如果是在编译器⾥,那么剖析器将被⽤来⽣成与源码相符的标准的⽬标代码。⽆论如何,在解释器中,剖析器的⽬的就是对给定的表达式求值。
将源代码变成他们的组成元素
所有解释器的基础是⼀个读取源码然后返回下⼀个逻辑符号的特殊的函数。基于历史原因,这些逻辑标记⼀般与标记(token)相关联。⼀般⽽⾔,计算机语⾔,特别是C语⾔,依据标记来定义程序。你可以想象标记是⼀个不可见的程序单元。⽐如说,相等运算符==就是⼀个标记。这两个等于号分开后意义将彻底改变。同理,if 也是⼀个标记。在C语⾔中⽆论是i还是f都没有其他意思。
在ANSI C标准中,标记被定义为属于下列⼏组:
keywords identifiers constants
strings operators pinctuation
keywords(关键字)是那些如while的构成C语⾔的标记。Identifier(识别符)是变量、函数、⽤户定义类型的名字。⽽Constants(常量)和Strings(字符串)是不解⾃明的,就像operators(运算符)⼀样。最后Punctuation(标点符号)包括了⼏个项⽬,像分号,逗号,⼤括号,⼩括号等。
在简易C解释器中,这个被称作get_token()的函数可以从源码中读取标记。
/*Get a token*/
get_token()
{
register char *temp;
token_type=0;tok=0;
temp=token;
*temp='\0';
/* skip over white space*/
while(iswhite(*prog)&&*prog)
++prog;
if (*prog=='\r')
{
++prog;
++prog;
while (iswhite(*prog)&&*prog)
++prog;
}
if (*prog=='\0')
{
*token='\0';
tok=FINISHED;
return (token_type=DELIMITER); }
if (strchr("{}",*prog))
{
*temp=*prog;
temp++;
*temp='\0';
prog++;
return (token_type=BLOCK);
}
/* look for comments*/
if (*prog=='/')
if (*(prog+1)=='*')
{
prog+=2;
do
{
while(*prog!='*') prog++;
prog++;
}while (*prog!='/');
prog++;
}
if (strchr("!<>=",*prog))
{
switch (*prog)
{
case '=':
if (*(prog+1)=='=')
{
prog++;prog++;
*temp=EQ;
temp++; *temp=EQ; temp++; *temp='\0';
}
break;
case '!':
if (*(prog+1)=='=')
{
prog++;prog++;
*temp=NE;
temp++; *temp=NE; temp++; *temp='\0';
}
break;
case '
if (*(prog+1)=='=')
{
prog++;prog++;
*temp=LE;temp++;*temp=LE; }
else
{
prog++;
*temp=LT;
}
temp++;
*temp='\0';
break;
case '>':
if (*(prog+1)=='=')
{
prog++;prog++;
*temp=GE;temp++;*temp=GE;
}
else
{
prog++;
*temp=GT;
}
temp++;
*temp='\0';
break;
}
if (*token) return(token_type=DELIMITER); }
if (strchr("+-*^/%=;(),'",*prog))
{
*temp=*prog;
prog++;
temp++;
*temp='\0';
return (token_type=DELIMITER);
}
if (*prog=='"')
{
prog++;
while (*prog!='"' && *prog!='\r')
*temp++=*prog++;
if (*prog=='\r') sntx_err(SYNTAX);
prog++;*temp='\0';
return (token_type=STRING);
}
if (isdigit(*prog))
{
while(!isdelim(*prog))
*temp++=*prog++;
*temp='\0';
return(token_type=NUMBER);
}
if(isalpha(*prog))
{
while (!isdelim(*prog))
*temp++=*prog++;
token_type=TEMP;
}
*temp='\0';
/*see if a string is a command or variable*/
if (token_type==TEMP)
{
isalpha 函数tok=look_up(token);
if(tok) token_type=KEYWORD;
else token_type=IDENTIFIER;
}
return token_type;
}
get_token()函数⽤到了以下的全局变量和枚举类型:
extern char *prog;
extern char *p_buf;
extern char token[80];
extern char token_type;
extern char tok;
enum tok_types {DELIMITER,IDENTIFIER,NUMBER, KEYWORD,TEMP,STRING,BLOCK};
enum double_ops {LT=1,LE,GT,GE,EQ,NE};
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,
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论