CS143 PA2 cool语法解析 c++版

啥也不说,上代码先。

cool.flex

/*
 *  The scanner definition for COOL.
 */

/*
 *  Stuff enclosed in %{ %} in the first section is copied verbatim to the
 *  output, so headers and global definitions are placed here to be visible
 * to the code in the file.  Don't remove anything that was here initially
 */
%{
#include <cool-parse.h>
#include <stringtab.h>
#include <utilities.h>

/* The compiler assumes these identifiers. */
#define yylval cool_yylval
#define yylex  cool_yylex

/* Max size of string constants */
#define MAX_STR_CONST 1025
#define YY_NO_UNPUT   /* keep g++ happy */

extern FILE *fin; /* we read from this file */

/* define YY_INPUT so we read from the FILE fin:
 * This change makes it possible to use this scanner in
 * the Cool compiler.
 */
#undef YY_INPUT
#define YY_INPUT(buf,result,max_size) \
	if ( (result = fread( (char*)buf, sizeof(char), max_size, fin)) < 0) \
		YY_FATAL_ERROR( "read() in flex scanner failed");

char string_buf[MAX_STR_CONST]; /* to assemble string constants */
char *string_buf_ptr;

extern int curr_lineno;
extern int verbose_flag;

extern YYSTYPE cool_yylval;

/*
 *  Add Your own definitions here
 */
extern IdTable idtable;
extern IntTable inttable;
extern StrTable stringtable;


%}

/*
 * Define names for regular expressions here.
 */

CLASS           [cC][lL][aA][sS][sS]
ELSE            [eE][lL][sS][eE]
FI              [fF][iI]
IF              [iI][fF]
IN              [iI][nN]
INHERITS        [iI][nN][hH][eE][rR][iI][tT][sS]
LET             [lL][eE][tT]
LOOP            [lL][oO][oO][pP]
POOL            [pP][oO][oO][lL]
THEN            [tT][hH][eE][nN]
WHILE           [wW][hH][iI][lL][eE]
CASE            [cC][aA][sS][eE]
ESAC            [eE][sS][aA][cC]
OF              [oO][fF]
DARROW          "=>"
NEW             [nN][eE][wW]
ISVOID          [iI][sS][vV][oO][iI][dD]
ASSIGN          "<-"
NOT             [nN][oO][tT]
LE              "<="
INT_CONST       [0-9]+
LETTER          [a-zA-Z]


%x COMMENT
%x STRING
%x ESCAPE

%%

 /*
  *  Nested comments
  */


 /*
  *  The multiple-character operators.
  */
\n { ++curr_lineno; }
<INITIAL>{CLASS}		{ return (CLASS); }
<INITIAL>{DARROW}		{ return (DARROW); }
<INITIAL>{ELSE}		{ return (ELSE); }
<INITIAL>{FI}		{ return (FI); }
<INITIAL>{IF}		{ return (IF); }
<INITIAL>{IN}    { return (IN); }
<INITIAL>{INHERITS}		{ return (INHERITS); }
<INITIAL>{ISVOID}		{ return (ISVOID); }
<INITIAL>{LET}       { return (LET); }
<INITIAL>{LOOP}      { return (LOOP); }
<INITIAL>{POOL}		{ return (POOL); }
<INITIAL>{THEN}		{ return (THEN); }
<INITIAL>{WHILE}		{ return (WHILE); }
<INITIAL>{CASE}		{ return (CASE); }
<INITIAL>{ESAC}		{ return (ESAC); }
<INITIAL>{NEW}		{ return (NEW); }
<INITIAL>{OF}		{ return (OF); }

<INITIAL>{ASSIGN}		{ return (ASSIGN); }
<INITIAL>{NOT}		{ return (NOT); }
<INITIAL>{LE}    {return (LE);}

<INITIAL>t[rR][uU][eE] { 
    cool_yylval.boolean=true;
    return (BOOL_CONST); 
}

<INITIAL>f[aA][lL][sS][eE] {
    cool_yylval.boolean=false;
    return (BOOL_CONST); 
}

<INITIAL>{INT_CONST} {
    //数字常量
    cool_yylval.symbol=inttable.add_string(yytext,yyleng);;
    return (INT_CONST);
}

<INITIAL>[A-Z][a-zA-Z0-9_]* {
    cool_yylval.symbol=idtable.add_string(yytext,yyleng);
    return (TYPEID);
}

<INITIAL>[a-z][a-zA-Z0-9_]* {
    cool_yylval.symbol=idtable.add_string(yytext,yyleng);
    return (OBJECTID);
}

<INITIAL>[ \t\n] {}
<INITIAL>--.* {}

<INITIAL>"(*" {
    //cout<<"begin"<<endl;
    BEGIN(COMMENT);
}

<INITIAL>"*)" {
    cool_yylval.error_msg = "EOF in comment";
    return (ERROR);
}

<COMMENT><<EOF>> {
    BEGIN(INITIAL);
    cool_yylval.error_msg = "EOF in comment";
    return (ERROR);
}

<COMMENT>[^(\*\))] {
    if(yytext[0]=='\n')
        ++curr_lineno;
}

<COMMENT>"*)" {
    BEGIN(INITIAL);
    //cout<<"end"<<endl;
}

<COMMENT>. {
    //什么都不做
}

<INITIAL>"\\" {
    //在非字符串的状态下,\是非法字符
    cool_yylval.error_msg = "wrong escape sysbol";
    return (ERROR);
}

<INITIAL>\" {
    BEGIN(STRING);
    string_buf_ptr=string_buf;
}

<STRING>\\ {
    BEGIN(ESCAPE);
}

<STRING>[^"\\] {
    *string_buf_ptr++=yytext[0];
}

<STRING>\" {
    *string_buf_ptr++='\0';
    cool_yylval.symbol=idtable.add_string(string_buf);
    BEGIN(INITIAL);
    return (STR_CONST);
}

<ESCAPE>.  {
    char ch=yytext[0];
    switch (ch){
        case 'n':
            *string_buf_ptr++='\n';
            break;
        case 't':
            *string_buf_ptr++='\t';
            break;
        case 'b':
            *string_buf_ptr++='\b';
            break;
        case 'f':
            *string_buf_ptr++='\f';
            break;
        case '"':
            *string_buf_ptr++='"';
            break;
        case '\'':
        case '\\':
        case '\n':
            *string_buf_ptr++=ch;
            break;
        case '\0':
        default:
            BEGIN(INITIAL);
            cool_yylval.error_msg = "EOF in comment";
            return (ERROR);
    }
    BEGIN(STRING);
}

<INITIAL>[\]\[\'>] {
    //错误字符
    cool_yylval.error_msg = yytext;
    return (ERROR);
}


<INITIAL>[,:\{\}\(\);\.@] {return yytext[0];}


<INITIAL>[=\-+<\*~/] {
    //运算符号
    return  yytext[0];
}


 /*
  * Keywords are case-insensitive except for the values true and false,
  * which must begin with a lower-case letter.
  */


 /*
  *  String constants (C syntax)
  *  Escape sequence \c is accepted for all characters c. Except for 
  *  \n \t \b \f, the result is c.
  *
  */


%%

词法解析的流程

刚开始看的时候一脸蒙逼,仔细PA2.pdf里,有讲flex的基础过程,因为不是很熟悉,我就大概看了个一下,后面具体不会的问gpt。

flex语法简单介绍

%{
Declarations
%}
Definitions
%%
Rules
%%
User subroutines

这里的Declarations相当于你的代码解析的时候,用上的一些全局变量和包含的库。

%{
#include <cool-parse.h>
#include <stringtab.h>
#include <utilities.h>

/* The compiler assumes these identifiers. */
#define yylval cool_yylval
#define yylex  cool_yylex

/* Max size of string constants */
#define MAX_STR_CONST 1025
#define YY_NO_UNPUT   /* keep g++ happy */

extern FILE *fin; /* we read from this file */

/* define YY_INPUT so we read from the FILE fin:
 * This change makes it possible to use this scanner in
 * the Cool compiler.
 */
#undef YY_INPUT
#define YY_INPUT(buf,result,max_size) \
	if ( (result = fread( (char*)buf, sizeof(char), max_size, fin)) < 0) \
		YY_FATAL_ERROR( "read() in flex scanner failed");

char string_buf[MAX_STR_CONST]; /* to assemble string constants */
char *string_buf_ptr;

extern int curr_lineno;
extern int verbose_flag;

extern YYSTYPE cool_yylval;

/*
 *  Add Your own definitions here
 */


%}

这里几乎都是c++的代码, fin是读入的文件,string_buf是解析的时候使用的字符数组,*string_buf_ptr是字符指针,curr_lineno是行数,每解析到一个\n (不在字符串里的\n),需要对他加一,这个会现实到token结果的第一个元素里。

Definitions 你在匹配的时候使用的一些变量,状态集。

CLASS           [cC][lL][aA][sS][sS]
ELSE            [eE][lL][sS][eE]
FI              [fF][iI]
IF              [iI][fF]
IN              [iI][nN]
INHERITS        [iI][nN][hH][eE][rR][iI][tT][sS]
LET             [lL][eE][tT]
LOOP            [lL][oO][oO][pP]
POOL            [pP][oO][oO][lL]
THEN            [tT][hH][eE][nN]
WHILE           [wW][hH][iI][lL][eE]
CASE            [cC][aA][sS][eE]
ESAC            [eE][sS][aA][cC]
OF              [oO][fF]
DARROW          "=>"
NEW             [nN][eE][wW]
ISVOID          [iI][sS][vV][oO][iI][dD]
ASSIGN          "<-"
NOT             [nN][oO][tT]
LE              "<="
INT_CONST       [0-9]+
LETTER          [a-zA-Z]


%x COMMENT
%x STRING
%x ESCAPE

前面的是简单的defination ,我感觉是类似于c++的define语句,单纯的把这些值替换过去。

下面的 %x 用于定义一个状态,初始的状态是  INITIAL,COMMENT是解析到 (*的时候,进入到解析注释的状态,STRING是解析到 “ 的时候,进入到字符串的解析状态, ESCAPE是在STRING状态之后,碰到 \ , 进入到转义符号的状态。

RULES就是你匹配的规则了。

<INITIAL>{LE}    {return (LE);}

<INITIAL>"(*" {
    //cout<<"begin"<<endl;
    BEGIN(COMMENT);
}

<INITIAL>f[aA][lL][sS][eE] {
    cool_yylval.boolean=false;
    return (BOOL_CONST); 
}

前面是匹配的时候的状态,“(*”就算匹配的正则表达式,后面大括号里的,就是匹配到这些字符之后,需要在匹配之后,执行什么代码,我当时就奇怪,这里的cool_yylval.boolean是怎么知道的,我们后面再说。

BEGIN(COMMENT)就是开始一个新的状态。

流程 

我们在lextest.cc里找到了关键的执行函数

	    cout << "#name \"" << argv[optind] << "\"" << endl;
	    while ((token = cool_yylex()) != 0) {
		dump_cool_token(cout, curr_lineno, token, cool_yylval);
	    }
	    fclose(fin);
	    optind++;

dump_cool_token就是打印出token的内容,注意到这里需要curr_lineno和cool_yylval,这个我猜测是解析到每一个token之后,这些值都需要更新的。

转到cool_yylval的定义,这玩意就是YYSTYPE结构。

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;
}
/* Line 1489 of yacc.c.  */
#line 124 "cool.tab.h"
	YYSTYPE;

 这是一个union,相当于可以是这些数据结构里的任何一个,但只能是一个,和struct不同。

cool_yylval就相当于你解析到啥,把他存起来就好了。

<INITIAL>[a-z][a-zA-Z0-9_]* {
    cool_yylval.symbol=idtable.add_string(yytext,yyleng);
    return (OBJECTID);
}

 但是解析到id的时候,我们是不好直接把char*转换到symbol里的,这里老师肯定是给了相应的接口函数,把char*转换到symbol结构里。

IdTable idtable;
IntTable inttable;
StrTable stringtable;

我之前第一次看到这个答案的时候,就很蒙,这个变量哪里来的,我在stringtab.cc里找到了答案,这里IdTable是StringTable的子类,他提供了char*到Symbol的转换方法。

所以我们在声明自己定义的时候,需要 把这三个变量extern进入,不进去也行,链接的时候也不会报错,但是小心全局变量重名。

extern YYSTYPE cool_yylval;

/*
 *  Add Your own definitions here
 */
extern IdTable idtable;
extern IntTable inttable;
extern StrTable stringtable;

解析

关键词

CLASS           [cC][lL][aA][sS][sS]
ELSE            [eE][lL][sS][eE]
FI              [fF][iI]
IF              [iI][fF]
IN              [iI][nN]
INHERITS        [iI][nN][hH][eE][rR][iI][tT][sS]
LET             [lL][eE][tT]
LOOP            [lL][oO][oO][pP]
POOL            [pP][oO][oO][lL]
THEN            [tT][hH][eE][nN]
WHILE           [wW][hH][iI][lL][eE]
CASE            [cC][aA][sS][eE]
ESAC            [eE][sS][aA][cC]
OF              [oO][fF]
DARROW          "=>"
NEW             [nN][eE][wW]
ISVOID          [iI][sS][vV][oO][iI][dD]
ASSIGN          "<-"
NOT             [nN][oO][tT]
LE              "<="
INT_CONST       [0-9]+
LETTER          [a-zA-Z]


%x COMMENT
%x STRING
%x ESCAPE

%%

 /*
  *  Nested comments
  */


 /*
  *  The multiple-character operators.
  */
\n { ++curr_lineno; }
<INITIAL>{CLASS}		{ return (CLASS); }
<INITIAL>{DARROW}		{ return (DARROW); }
<INITIAL>{ELSE}		{ return (ELSE); }
<INITIAL>{FI}		{ return (FI); }
<INITIAL>{IF}		{ return (IF); }
<INITIAL>{IN}    { return (IN); }
<INITIAL>{INHERITS}		{ return (INHERITS); }
<INITIAL>{ISVOID}		{ return (ISVOID); }
<INITIAL>{LET}       { return (LET); }
<INITIAL>{LOOP}      { return (LOOP); }
<INITIAL>{POOL}		{ return (POOL); }
<INITIAL>{THEN}		{ return (THEN); }
<INITIAL>{WHILE}		{ return (WHILE); }
<INITIAL>{CASE}		{ return (CASE); }
<INITIAL>{ESAC}		{ return (ESAC); }
<INITIAL>{NEW}		{ return (NEW); }
<INITIAL>{OF}		{ return (OF); }

<INITIAL>{ASSIGN}		{ return (ASSIGN); }
<INITIAL>{NOT}		{ return (NOT); }
<INITIAL>{LE}    {return (LE);}

<INITIAL>t[rR][uU][eE] { 
    cool_yylval.boolean=true;
    return (BOOL_CONST); 
}

<INITIAL>f[aA][lL][sS][eE] {
    cool_yylval.boolean=false;
    return (BOOL_CONST); 
}

我们在cool_manual里发现,关键词是大小写不敏感的,我也是后面测试的时候才去翻文档的,

然后这里return bool_const的时候,需要记录下来是true还是false. 

数字,类名,变量名

<INITIAL>{INT_CONST} {
    //数字常量
    cool_yylval.symbol=inttable.add_string(yytext,yyleng);;
    return (INT_CONST);
}

<INITIAL>[A-Z][a-zA-Z0-9_]* {
    cool_yylval.symbol=idtable.add_string(yytext,yyleng);
    return (TYPEID);
}

<INITIAL>[a-z][a-zA-Z0-9_]* {
    cool_yylval.symbol=idtable.add_string(yytext,yyleng);
    return (OBJECTID);
}

这里yytext是解析出来的内容,是char* ,yyleng是解析出来的长度,是flex这个工具自己定义的变量。

文档说类名是大写开头,包含字母数字和下划线,变量名是小写开头。

空格,换行符 

<INITIAL>[ \t] {}
<INITIAL>--.* {}

不处理,直接抛弃就好了

注释

<COMMENT><<EOF>> {
    BEGIN(INITIAL);
    cool_yylval.error_msg = "EOF in comment";
    return (ERROR);
}

<COMMENT>[^(\*\))] {
    if(yytext[0]=='\n')
        ++curr_lineno;
}

<COMMENT>"*)" {
    BEGIN(INITIAL);
    //cout<<"end"<<endl;
}

<COMMENT>. {
    //什么都不做
}

就算需要在解析到\n的时候,需要curr_lineno里将行数加一,然后因为是最长匹配嘛,第二条规则是当后面有两个字符的时候,会出现奇怪的内容,我就多加了一条。

字符串 

<INITIAL>\" {
    BEGIN(STRING);
    string_buf_ptr=string_buf;
}

<STRING>\\ {
    BEGIN(ESCAPE);
}

<STRING>[^"\\] {
    *string_buf_ptr++=yytext[0];
}

<STRING>\" {
    *string_buf_ptr++='\0';
    cool_yylval.symbol=stringtable.add_string(string_buf);
    BEGIN(INITIAL);
    return (STR_CONST);
}

解析到“ 的时候,开启字符串状态,然后把解析出来的字符储存在str_buf里,再碰到"的时候,在stringtable里加一项就好了。

转移字符


<ESCAPE>.  {
    char ch=yytext[0];
    switch (ch){
        case 'n':
            *string_buf_ptr++='\n';
            break;
        case 't':
            *string_buf_ptr++='\t';
            break;
        case 'b':
            *string_buf_ptr++='\b';
            break;
        case 'f':
            *string_buf_ptr++='\f';
            break;
        case '"':
            *string_buf_ptr++='"';
            break;
        case '\'':
        case '\\':
        case '\n':
            *string_buf_ptr++=ch;
            break;
        case '\0':
        default:
            BEGIN(INITIAL);
            cool_yylval.error_msg = "EOF in comment";
            return (ERROR);
    }
    BEGIN(STRING);
}

就是把 两个字符位置的 "\n"转换成一个字符的'\n'就好了。

错误字符,运算字符,标点

<INITIAL>[\]\[\'>] {
    //错误字符
    cool_yylval.error_msg = yytext;
    return (ERROR);
}


<INITIAL>[,:\{\}\(\);\.@] {return yytext[0];}


<INITIAL>[=\-+<\*~/] {
    //运算符号
    return  yytext[0];
}

该返回返回,该报错报错。 

测试

通过了老师的test.cl之后,我把所有的example里的.cl文件都解析了一遍,然后比对老师写的lexer输出的结果。

这是脚本

#!/bin/bash
# colordiff -u (lexer test.cl | psub) (./lexer test.cl | psub)
make lexer > /dev/null
make > /dev/null

for file in /home/sjk/project/cool-compiler/examples/*.cl; do
    if [ -f "$file" ]; then
        echo "Running colordiff -u for file: $file"
        colordiff -u <(lexer "$file") <(./lexer "$file")
    fi
done

我这里是fish终端,然后其他的需要把  < 改成 | ,linux的管道。colordiff自己下载就行。

这样就ok

  • 11
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值