Stanford Compiler PA2
课程主页:
https://www.edx.org/course/compilers
课程视频:
https://www.bilibili.com/video/BV1NE411376V?p=20&t=6
这次回顾作业2——词法分析。个人感觉这次作业还是很难的,主要是以下几点:
- flex工具的使用——入手需要看一定的资料,并不是半天就能掌握的。
- 正则表达式识别的逻辑——例如注释和字符串的正则表达式应该怎样写才能保证不漏不重。
- Cool编程语言的规则——这部分算是相对简单的,但是入门文档也有30页有余,还是需要花一定时间的。
这次基本是根据大佬的解答才勉强理解的,这里会给出代码的逻辑解释,基本是算是对大佬代码的解析了,主要参考了第一份资料:
其余参考资料:
- https://blog.csdn.net/benladeng29hao/article/details/109111367
- http://dinosaur.compilertools.net/lex/index.html
- https://blog.csdn.net/deepfuture/article/details/83523136
作业分析
准备工作
完成本次作业的整体流程如下:
阅读如下内容:
- PA2。
- cool-manual至少第10章,11章。
- flex介绍:
- cool-parse.h,stringtab.h,stringtab_functions.h
编写cool.flex
下载评分脚本:
wget https://courses.edx.org/assets/courseware/v1/2aa4dec0c84ec3a8d91e0c1d8814452b/asset-v1:StanfordOnline+SOE.YCSCS1+1T2020+type@asset+block/pa1-grading.pl
编译:
make lexer
测试:
perl pa1-grading.pl
报错处理
如果
make lexer
产生如下报错:
/usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/libfl.so: undefined reference to `yylex' collect2: error: ld returned 1 exit status
那么在definitions和rules之间增加如下内容即可:
%option noyywrap
参考资料:
https://blog.csdn.net/benladeng29hao/article/details/109111367
几个重要变量
cool_yylval——该变量记录相关信息,这部分使用如下属性:
- error_msg(报错)
- symbol(符号表中变量)
- boolean(布尔变量的值)
相关代码:
extern YYSTYPE cool_yylval;
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
typedef union YYSTYPE
#line 46 "cool.y"
{
Boolean boolean;
Symbol symbol;
Program program;
Class_ class_;
Classes classes;
Feature feature;
Features features;
Formal formal;
Formals formals;
Case case_;
Cases cases;
Expression expression;
Expressions expressions;
char *error_msg;
}
三个string table:
- idtable
- inttable
- stringtable
记录标识符,整数,字符串的信息。
流程图
整个词法分析的过程可以用如下流程图概括:
词法分析
所有的说明都在代码中,需要补充的点会加以补充。
注释
/*
* Nested comments
*/
/* 只有不在STRING状态时, 遇到(*才进入BLOCKCOMMENT状态,
commentNum记录嵌套层数, 每次遇到(*则更新。 */
<INITIAL,BLOCKCOMMENT,INLINECOMMENT>"(*" {
commentNum += 1;
BEGIN BLOCKCOMMENT;
};
/* 如果在BLOCKCOMMENT状态遇到*), 则匹配一个(*, 即commentNum减1;
如果commentNum为0, 则说明BLOCKCOMMENT结束, 恢复INITIAL状态。 */
<BLOCKCOMMENT>"*)" {
commentNum -= 1;
if (commentNum == 0){
BEGIN 0;
}
}
/* 如果在非BLOCKCOMMENT状态遇到*), 则直接报错。 */
"*)" {
cool_yylval.error_msg = "Unmatched *)";
return ERROR;
}
/* 换行 */
<BLOCKCOMMENT>"\n" {
curr_lineno++;
};
/* 忽略非\n, (, *字符 */
<BLOCKCOMMENT>[^\n(*]* {};
/* 如果进入该状态, 说明匹配了(或*或)
如果*后续有), 根据规则的出现顺序会匹配*);
如果(后续有*, 根据规则的出现顺序会匹配(*;
其余情形直接忽略。 */
<BLOCKCOMMENT>[(*)] {};
/* 根据4.1, COMMENT中遇到<EOF>直接报错 */
<BLOCKCOMMENT><<EOF>> {
cool_yylval.error_msg = "EOF in comment";
BEGIN 0;
return ERROR;
}
/*
* INLINECOMMENT comments
*/
/* INITIAL状态下进入INLINECOMMENT状态 */
<INITIAL>"--" {
BEGIN INLINECOMMENT;
};
/* 忽略非换行符 */
<INLINECOMMENT>[^\n]* {};
/* 识别换行符 */
<INLINECOMMENT>"\n" {
curr_lineno++;
BEGIN 0;
}
整数,标识符和特殊符号
整数
/*
* Integers
*/
{DIGIT}+ {
cool_yylval.symbol = inttable.add_string(yytext);
return INT_CONST;
}
标识符
/*
* Identifiers
*/
[A-Z][0-9a-zA-Z_]* {
cool_yylval.symbol = idtable.add_string(yytext);
return TYPEID;
}
[a-z][0-9a-zA-Z_]* {
cool_yylval.symbol = idtable.add_string(yytext);
return OBJECTID;
}
特殊符号
特殊符号见cool-manul的第16,17页:
/*
* The multiple-character operators.
*/
"=>" {
return DARROW;
}
"." {
return '.';
}
"@" {
return '@';
}
"~" {
return '~';
}
"*" {
return '*';
}
"/" {
return '/';
}
"+" {
return '+';
}
"-" {
return '-';
}
"<=" {
return LE;
}
"<" {
return '<';
}
"=" {
return '=';
}
"<-" {
return ASSIGN;
}
":" {
return ':';
}
"{" {
return '{';
}
"}" {
return '}';
}
";" {
return ';';
}
"(" {
return '(';
}
")" {
return ')';
}
"," {
return ',';
}
字符串
字符串是词法分析中最复杂的部分,这部分要详细讨论。
注意我们的输入是字符串,所以如果输入是转义字符$\text{\c}$,那么得到的结果是$\text{c}$。因此,如果我们希望得到转义字符$\text{\c}$,那么输入应该是$\text{\ \ \ c}$。
根据这点可以将输入分为几类:
- 开始结束符号:$\text{\”}$
- 输出为转义字符的形式:$\text{\ \ \ c}$
- 输入本身即为转义字符:$\text{\c}$
- 其中$\text{\n}$以及$\text{\0}$为非法字符。
- 普通字符。
- $\text{
}$。
整体代码如下:
/*
* String constants (C syntax)
* Escape sequence \c is accepted for all characters c. Except for
* \n \t \b \f, the result is c.
*
*/
/* 只有在INITIAL状态下, 遇到\"才进入STRING状态;
将识别内容存储到yytext */
<INITIAL>\" {
BEGIN STRING;
yymore();
}
/* 识别普通字符 */
<STRING>[^\\\"\n]* {
yymore();
}
/* 转义字符 */
<STRING>\\[^\n] {
yymore();
}
/* 换行 */
<STRING>\\\n {
curr_lineno++;
yymore();
}
/* 涉及到curr_lineno++, 必须直接处理 */
<STRING>\n {
cool_yylval.error_msg = "Unterminated string constant";
curr_lineno++;
BEGIN 0;
return ERROR;
}
/* 结束符直接报错 */
<STRING><<EOF>> {
cool_yylval.error_msg = "EOF in string constant";
BEGIN 0;
yyrestart(yyin);
return ERROR;
}
/* 处理字符串 */
<STRING>\" {
std::string str(yytext, yyleng);
int n = yyleng;
int i = 1;
int j = 0;
char res[n + 1];
//判断是否有\0
for (int j = 1; j < n - 1; j++){
if (str[j] == '\0'){
cool_yylval.error_msg = "String contains null character";
BEGIN 0;
return ERROR;
}
}
while (i < n - 1){
if (str[i] == '\\'){
switch(str[i + 1]){
case 'b':
res[j++] = '\b';
break;
case 't':
res[j++] = '\t';
break;
case 'n':
res[j++] = '\n';
break;
case 'f':
res[j++] = '\f';
break;
default:
res[j++] = str[i + 1];
break;
}
i += 2;
}else{
res[j++] = str[i++];
}
}
if (j >= MAX_STR_CONST){
cool_yylval.error_msg = "String constant too long";
BEGIN 0;
return ERROR;
}
res[j] = '\0';
cool_yylval.symbol = stringtable.add_string((char*)res);
BEGIN 0;
return STR_CONST;
}
关键词
这部分很简单的,只要了解
(?i:string)
是忽略string的大小写即可。
关键词的列表见cool-parse.h的第115行。
代码如下:
/*
* Keywords are case-insensitive except for the values true and false,
* which must begin with a lower-case letter.
*/
(?i:CLASS) {
return CLASS;
}
(?i:ELSE) {
return ELSE;
}
(?i:FI) {
return FI;
}
(?i:IF) {
return IF;
}
(?i:IN) {
return IN;
}
(?i:INHERITS) {
return INHERITS;
}
(?i:ISVOID) {
return ISVOID;
}
(?i:LET) {
return LET;
}
(?i:LOOP) {
return LOOP;
}
(?i:POOL) {
return POOL;
}
(?i:THEN) {
return THEN;
}
(?i:WHILE) {
return WHILE;
}
(?i:CASE) {
return CASE;
}
(?i:ESAC) {
return ESAC;
}
(?i:NEW) {
return NEW;
}
(?i:OF) {
return OF;
}
(?i:NOT) {
return NOT;
}
t(?i:rue) {
cool_yylval.boolean = 1;
return BOOL_CONST;
}
f(?i:alse) { cool_yylval.boolean = 0;
return BOOL_CONST;
}
空格换行
空格换行见cool-manu第16页10.5。
/*
* White space
*/
\n {
curr_lineno++;
}
[ \f\r\t\v]+ {}
其他
/*
* else
*/
. {
cool_yylval.error_msg = yytext;
return ERROR;
}
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Doraemonzzz!
评论
ValineLivere