├── .gitignore ├── images ├── 解压后.png ├── path修改.png └── ubuntu.png ├── Assignment-Answer ├── PA2 │ ├── images │ │ └── 目录.png │ ├── cool.flex │ └── myAnswer.md └── PA1 │ ├── images │ └── stack-command.png │ └── myAnswer.md ├── EnvironmentResources ├── flex-2.5.35.tar.bz2 └── asset-v1_StanfordOnline+SOE.YCSCS1+1T2020+type@asset+block@student-dist.tar.gz └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | EnvironmentResources/linux -------------------------------------------------------------------------------- /images/解压后.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunnyHaze/Stanford-CS143-Compiler/HEAD/images/解压后.png -------------------------------------------------------------------------------- /images/path修改.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunnyHaze/Stanford-CS143-Compiler/HEAD/images/path修改.png -------------------------------------------------------------------------------- /images/ubuntu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunnyHaze/Stanford-CS143-Compiler/HEAD/images/ubuntu.png -------------------------------------------------------------------------------- /Assignment-Answer/PA2/images/目录.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunnyHaze/Stanford-CS143-Compiler/HEAD/Assignment-Answer/PA2/images/目录.png -------------------------------------------------------------------------------- /EnvironmentResources/flex-2.5.35.tar.bz2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunnyHaze/Stanford-CS143-Compiler/HEAD/EnvironmentResources/flex-2.5.35.tar.bz2 -------------------------------------------------------------------------------- /Assignment-Answer/PA1/images/stack-command.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunnyHaze/Stanford-CS143-Compiler/HEAD/Assignment-Answer/PA1/images/stack-command.png -------------------------------------------------------------------------------- /EnvironmentResources/asset-v1_StanfordOnline+SOE.YCSCS1+1T2020+type@asset+block@student-dist.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SunnyHaze/Stanford-CS143-Compiler/HEAD/EnvironmentResources/asset-v1_StanfordOnline+SOE.YCSCS1+1T2020+type@asset+block@student-dist.tar.gz -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 斯坦福CS143编译原理 2 | + 本仓库为Stanfor CS143课程的学习和环境配置教程。 3 | 4 | ## 主要资源链接 5 | 6 | 7 | 前者主要是本课程相关的PDF资料,比如PPT,书面作业,编程作业要求等等。 8 | 9 | 10 | ;l;kl;klkkl;kl 11 | 后者是另一种形式的在线网课,包含课程视频,提供了代码、配环境指导等等较为连贯的信息。主要内容与线下课相同,但是课程布置顺序,每周的任务布置与实际的斯坦福教学有所出入。作者主要跟随`edx.org`的内容学习。 12 | 13 | ## 环境配置 14 | > 如果本文的链接失效了,在本仓库的`Environment resources`文件夹下有一些较小的文件的备份,可以从这里面提取所需内容 15 | 16 | 课程第一节课结束后就可以考虑配置作业环境了,课程使用了`COOL`语言来教学,全称为`Classroom Object Oriented Language`,是一门专门用于学习如何构建编译器的教学语言。这也导致了它是一门编译器远多于自身语言程序的语言。 17 | 18 | 第一课结束后要求配置好`COOL`语言环境进行体验,整个运行环境是基于LINUX的。课程提供了一个很早的ubuntu某发行版的虚拟机镜像,但是由于其32位的内核,导致对于目前的Windows用户来说,难以通过VSCODE的REMOTE SSH功能穿透进去(VSCODE不支持32位架构远程ssh)。而纯使用VIM或者VI一类的编辑器对我来说学习成本较高(请尊重个人喜好),所以采用较新的`Ubuntu LTS 20.04`虚拟机环境,并尽可能安装与课程提供的环境相似的**老版本程序**来保证一切的正常运行。 19 | 20 | ### 构建虚拟机环境 21 | - 从[Virtual Box官网](https://www.virtualbox.org/)下载虚拟机环境并安装。 22 | - 从[Ubuntu Desktop官网](https://ubuntu.com/download/desktop)下载`Ubuntu LTS 20.04`光盘镜像(`*.iso`)文件 23 | - 在`Virtual Box`中新建新环境,并导入Ubuntu的ISO文件,按要求推进安装流程即可。 24 | - 安装成功后进入桌面即可。 25 | 26 | ![](images/ubuntu.png) 27 | 28 | - 此时建议配置一个**共享文件夹**,便于实现从宿主机与虚拟机的文件传输。 29 | - 作为基本的Linux环境,建议先按如下方式安装`gcc` `g++` `m4` `net-tools` `vim`这些必要包用于编译、配置等等工作。 30 | ``` 31 | sudo apt-get install xxxxx 32 | ``` 33 | ### 按照课程要求配置编译环境 34 | 35 | > 本段主要参考[edx.org教程](https://courses.edx.org/courses/course-v1:StanfordOnline+SOE.YCSCS1+2T2020/6b750292e90d4950b895f621a5671b49/),中间针对环境不同有一些改动,英文好的或者对于Linux较为了解可以忽略此部分 36 | 37 | > 同时本文也参考了[知乎anarion的回答](https://zhuanlan.zhihu.com/p/226190284?utm_source=wechat_session),感谢前人的文章减少了我配环境的时间 38 | - 首先需要将课程配置好的文件打包下载到Ubuntu环境中,建议在`~`路径(`/home/用户名/`)下新建一个`class`文件夹用于本课程文件的存放和书写。 39 | - 在这个`class`文件夹下,使用`wget`从官网下载数据包,具体语句如下 40 | ``` 41 | wget https://courses.edx.org/asset-v1:StanfordOnline+SOE.YCSCS1+1T2020+type@asset+block@student-dist.tar.gz 42 | ``` 43 | - 下载后解压到虚拟机的`~/class`文件夹即可,这时我们可以看到很多解压好的目录结构:(flex相关的文件是后面解压的) 44 | 45 | ![](images/解压后.png) 46 | 47 | - 此时我们可以在`bin`文件夹中看到很多可执行文件,这些就是编译好的`cool`语言编译器,MIPS执行环境等等本课程所需的可执行文件。我们需要将它们添加到Ubuntu的环境变量`PATH`中,这样我们就可以在其他路径下随时调用这些指令编译`cool`文件了。 48 | - 我们先用文本编辑器打开存放全局环境变量的文件,这里使用`vim` 49 | ``` 50 | sudo vim /etc/environment 51 | ``` 52 | 在这里可以看到`PATH`变量目前的内容,我们向其中添加刚才解压的文件夹的`bin`路径,以我的环境为例,我的用户名是`sunnyhaze`,于是我对于`PATH`的修改后的结果如下: 53 | 54 | ![](images/path修改.png) 55 | 56 | 主要是添加了红线所示的路径,请根据你的具体路径和用户名做出修改即可,注意添加对应的分隔符。 57 | 58 | - 此时还不能正常工作,需要让系统重新读取一遍环境变量执行,这一步通过在终端执行如下指令即可: 59 | ``` 60 | source /etc/environment 61 | ``` 62 | - 此时我们对于配置好的环境进行测试,随便在一个路径下输入`coolc`指令查看cool的编译器是否正常运行,如果显示为如下状态,恭喜你,基本配置安装成功。 63 | ``` 64 | Class Main is not defined. 65 | Compilation halted due to static semantic errors. 66 | ``` 67 | ### 辅助配置MIPS编译环境 68 | - 完成这一步还不可以,如果你尝试用`spim`进一步运行由`coolc`编译的文件,发现可能会报错“找不到文件”,这主要是因为`Ubuntu LTS 20.04`是64位的系统,而此文件由32位编译运行,需要先安装一个32位的适配程序,通过如下方式安装即可: 69 | ``` 70 | sudo apt-get install lib32z1 71 | ``` 72 | ### 后续课程所需内容预安装 73 | 经过上面的流程,基本就完成了`cool`语言编译环境的配置 74 | 75 | 然而后续的课程还需要安装`flex` 和`bison`这里先提前安装一下: 76 | 77 | 早期版本的flex和最新的版本有一些更迭,主要是针对语言特性的优化,这也就导致了如果我们直接安装最新的flex可能会导致后面的代码无法运行,所以这里建议下载早期的安装包进行安装,windows下可以直接通过此链接下载 78 | 79 | [https://src.fedoraproject.org/lookaside/pkgs/flex/flex-2.5.35.tar.bz2/10714e50cea54dc7a227e3eddcd44d57/flex-2.5.35.tar.bz2](https://src.fedoraproject.org/lookaside/pkgs/flex/flex-2.5.35.tar.bz2/10714e50cea54dc7a227e3eddcd44d57/flex-2.5.35.tar.bz2) 80 | 81 | 也可以在Ubuntu中使用`wget`下载,下载后请在某文件夹下解压后即可。 82 | 83 | 需要注意的是,这里采用的是编译方式安装,所以需要通过在路径下使用`./configure && make && sudo make install`命令实现安装。(如果没安装make,请先安装) 84 | 85 | 86 | 而后的`bison`可以直接通过如下语句安装: 87 | ``` 88 | sudo apt-get install bison 89 | ``` -------------------------------------------------------------------------------- /Assignment-Answer/PA2/cool.flex: -------------------------------------------------------------------------------- 1 | /* 2 | * The scanner definition for COOL. 3 | */ 4 | 5 | /* 6 | * Stuff enclosed in %{ %} in the first section is copied verbatim to the 7 | * output, so headers and global definitions are placed here to be visible 8 | * to the code in the file. Don't remove anything that was here initially 9 | */ 10 | %{ 11 | #include 12 | #include 13 | #include 14 | 15 | /* The compiler assumes these identifiers. */ 16 | #define yylval cool_yylval 17 | #define yylex cool_yylex 18 | 19 | /* Max size of string constants */ 20 | #define MAX_STR_CONST 1025 21 | #define YY_NO_UNPUT /* keep g++ happy */ 22 | 23 | extern FILE *fin; /* we read from this file */ 24 | 25 | /* define YY_INPUT so we read from the FILE fin: 26 | * This change makes it possible to use this scanner in 27 | * the Cool compiler. 28 | */ 29 | #undef YY_INPUT 30 | #define YY_INPUT(buf,result,max_size) \ 31 | if ( (result = fread( (char*)buf, sizeof(char), max_size, fin)) < 0) \ 32 | YY_FATAL_ERROR( "read() in flex scanner failed"); 33 | 34 | char string_buf[MAX_STR_CONST]; /* to assemble string constants */ 35 | char *string_buf_ptr; 36 | 37 | extern int curr_lineno; 38 | extern int verbose_flag; 39 | 40 | extern YYSTYPE cool_yylval; 41 | 42 | std::string stringbuff; 43 | /* 44 | * Add Your own definitions here 45 | */ 46 | 47 | 48 | %} 49 | 50 | /* 51 | * Define names for regular expressions here. 52 | */ 53 | %x COMMENT 54 | %x STR 55 | %x STR_ESCAPE 56 | CLASS [cC][lL][aA][sS][sS] 57 | IF [iI][fF] 58 | FI [fF][iI] 59 | ELSE [eE][lL][sS][eE] 60 | IN [iI][nN] 61 | INHERITS [iI][nN][hH][eE][rR][iI][tT][sS] 62 | ISVOID [iI][sS][vV][oO][iI][dD] 63 | LET [lL][eE][tT] 64 | LOOP [lL][oO][oO][pP] 65 | POOL [pP][oO][oO][lL] 66 | THEN [tT][hH][eE][nN] 67 | WHILE [wW][hH][iI][lL][eE] 68 | CASE [cC][aA][sS][eE] 69 | ESAC [eE][sS][aA][cC] 70 | NEW [nN][eE][wW] 71 | OF [oO][fF] 72 | NOT [nN][oO][tT] 73 | TRUE [t][rR][uU][eE] 74 | FALSE [f][aA][lL][sS][eE] 75 | 76 | DARROW => 77 | ASSIGN <- 78 | LE <= 79 | 80 | %% 81 | 82 | [\[\]\!\#\?\'\>] { 83 | yylval.error_msg = yytext; 84 | return ERROR; 85 | } 86 | 87 | /* 88 | * single line comment 89 | */ 90 | --.* {} 91 | /* 92 | * Nested comments 93 | */ 94 | 95 | "(*" { 96 | BEGIN(COMMENT); 97 | } 98 | 99 | 100 | [^\*\)\n] {} 101 | [\n] { 102 | ++curr_lineno; 103 | } 104 | <> { 105 | BEGIN(INITIAL); 106 | cool_yylval.error_msg = "EOF in comment"; 107 | return ERROR; 108 | } 109 | 110 | . {} 111 | 112 | "*)" { 113 | BEGIN(INITIAL); 114 | } 115 | 116 | 117 | 118 | "*)" { 119 | cool_yylval.error_msg = "Unmatched *)"; 120 | return ERROR; 121 | } 122 | 123 | \" {BEGIN(STR);} 124 | 125 | <> { 126 | BEGIN(INITIAL); 127 | cool_yylval.error_msg = "EOF in string constant"; 128 | return ERROR; 129 | } 130 | 131 | [\0] { 132 | BEGIN(INITIAL); 133 | cool_yylval.error_msg = "String contains null character"; 134 | return ERROR; 135 | } 136 | 137 | [\n] { 138 | BEGIN(INITIAL); 139 | cool_yylval.error_msg = "Unterminated string constant"; 140 | curr_lineno++; 141 | return ERROR; 142 | } 143 | \\ { 144 | BEGIN(STR_ESCAPE); 145 | } 146 | 147 | n { 148 | stringbuff.append("\n"); 149 | BEGIN(STR); 150 | } 151 | 152 | b { 153 | stringbuff.append("\b"); 154 | BEGIN(STR); 155 | } 156 | 157 | t { 158 | stringbuff.append("\t"); 159 | BEGIN(STR); 160 | } 161 | 162 | b { 163 | stringbuff.append("\f"); 164 | BEGIN(STR); 165 | } 166 | 167 | 0 { 168 | stringbuff.append("0"); 169 | BEGIN(STR); 170 | } 171 | 172 | \" { 173 | BEGIN(INITIAL); 174 | char tmp[stringbuff.size()]; 175 | strcpy(tmp, stringbuff.c_str()); 176 | cool_yylval.symbol = stringtable.add_string(tmp); 177 | stringbuff.clear(); 178 | return STR_CONST; 179 | } 180 | 181 | . { 182 | stringbuff.append(yytext); 183 | } 184 | 185 | /* 186 | * The multiple-character operators. 187 | */ 188 | \n { ++curr_lineno;} 189 | {DARROW} { return (DARROW); } 190 | {ASSIGN} { return (ASSIGN);} 191 | {LE} { return (LE);} 192 | /* 193 | * Keywords are case-insensitive except for the values true and false, 194 | * which must begin with a lower-case letter. 195 | */ 196 | {CLASS} { return (CLASS);} 197 | {IF} { return (IF);} 198 | {FI} { return (FI);} 199 | {ELSE} { return (ELSE);} 200 | {IN} { return (IN);} 201 | {INHERITS} { return (INHERITS);} 202 | {ISVOID} { return (ISVOID);} 203 | {LET} { return (LET);} 204 | {LOOP} { return (LOOP);} 205 | {POOL} { return (POOL);} 206 | {THEN} { return (THEN);} 207 | {WHILE} { return (WHILE);} 208 | {CASE} { return (CASE);} 209 | {ESAC} { return (ESAC);} 210 | {NEW} { return (NEW);} 211 | {OF} { return (OF);} 212 | {NOT} { return (NOT);} 213 | 214 | 215 | {TRUE} { 216 | cool_yylval.boolean = true; 217 | return BOOL_CONST; 218 | } 219 | 220 | {FALSE} { 221 | cool_yylval.boolean = false; 222 | return BOOL_CONST; 223 | } 224 | /* 225 | * 226 | */ 227 | 228 | [A-Z][a-zA-Z_0-9]* { 229 | cool_yylval.symbol = idtable.add_string(yytext); 230 | return TYPEID; 231 | } 232 | 233 | [a-z][a-zA-Z_0-9]* { 234 | cool_yylval.symbol = idtable.add_string(yytext); 235 | return OBJECTID; 236 | } 237 | 238 | [0-9]+ { 239 | cool_yylval.symbol = inttable.add_string(yytext); 240 | return INT_CONST; 241 | } 242 | 243 | [\+\-\*\/\<\=\.\@\~\;\,\{\}\:\(\)] {return yytext[0];} 244 | 245 | /* 246 | * String constants (C syntax) 247 | * Escape sequence \c is accepted for all characters c. Except for 248 | * \n \t \b \f, the result is c. 249 | * 250 | */ 251 | 252 | [ \t\f\r\v] {} 253 | %% -------------------------------------------------------------------------------- /Assignment-Answer/PA2/myAnswer.md: -------------------------------------------------------------------------------- 1 | # 第二次作业:写一个COOL语言词法分析器 2 | 这一次作业的内容跨度比较大,并且涉及到的新技术和文件较多,所以先梳理一下学习流程。 3 | 4 | 本文件夹下有一个完整的`cool.flex`文件,即我实现的代码工作。 5 | 6 | ## 词法分析 7 | Lexical Analysis, 即词法分析,也是本作业的重点。要求是将用户输入的字符串分解为一个又一个的`token`,以便接下来进一步分析使用。 8 | ## 浅析 9 | 词法分析器主要通过正则匹配合适的字符实现对于各个“词法单元”的匹配。比如`标识符`、`整型变量`、`字符串变量`、`关键字`、`运算符`等等。以便下一步语法分析(句法分析)时利用这些分割好的词法单元进一步处理。 10 | 11 | 实现上,本课程并没有要求从头实现正则匹配的功能,而是利用了`flex`这个程序来帮助我们正则匹配。实际上我们主要需要做的就是理解好COOL的语法特性,并掌握了`flex`的简要使用方法后,按照规则撰写对应的正则表达式即可。 12 | 13 | 知道了这个目标,我们需要做的就是阅读足量的英文文档来掌握所需的相关信息,这个过程可能会比较痛苦,但是锻炼快速提取英文信息的能力是值得的,尽量还是不要用整段的翻译软件。 14 | ## 需要阅读的文档 15 | - 在课程提供的压缩包内所有文件中 16 | 17 | ![](images/目录.png) 18 | 19 | 请先阅读如下资料: 20 | - `handouts/PA2.pdf`中包含了课程的基本要求,并简要的先介绍了一下`flex`大致使用方法。 21 | - `assignmen/PA2/README.md`包含了课程的基本目录结构,文件含义。 22 | - 然后去阅读`flex`的使用方法 23 | - [`flex`官方文档](http://westes.github.io/flex/manual/)为了可以正确的撰写`.flex`文件,需要稍微深究一下`flex`的基本功能和语法,强烈建议学习前10章后再重新琢磨一下前两个文件。 24 | > 请注意,flex的文档网站需要用`IE`兼容模式打开,否则可能会渲染混乱。 25 | - 可能需要的内容: 26 | - `handouts/cool-tour.pdf`的前4章,可以更好的理解不同组件之间的协作 27 | - 为了精准的撰写COOL语法所需的解析方式,想必`handouts/cool-manual.pdf`的COOL文档也是要时常翻看的。 28 | 29 | ## 注意事项 30 | ### cool.flex文件 31 | 在`assignments/PA2/`路径下的`cool.flex`文件中是我们在本作业中进行**全部代码**书写的地方,主要是书写正则表达式的规则以完成匹配。完成代码想要测试的时候可以根据`README`中的指引使用如下指令测试自己的lexer是否正确: 32 | ``` 33 | make dotest 34 | ``` 35 | 这个指令会帮你按要求解析此路径下的`test.cl`文件。如果你之前配置的环境没有问题的话,那么此时你可以使用下面这个指令,来查看课程提供的正确的`lexer`的目标输出是什么。 36 | ``` 37 | lexer test.cl 38 | ``` 39 | 40 | 举个例子,如果我们撰写了如下的代码在`test.cl`中。 41 | ```java 42 | class Main inherits IO{ 43 | main() : Object{ 44 | out_string("Hello world!\n") 45 | }; 46 | }; 47 | ``` 48 | 49 | 我们用官方的`lexer`指令执行后输出了如下内容: 50 | ``` 51 | #name "test.cl" 52 | #1 CLASS 53 | #1 TYPEID Main 54 | #1 INHERITS 55 | #1 TYPEID IO 56 | #1 '{' 57 | #2 OBJECTID main 58 | #2 '(' 59 | #2 ')' 60 | #2 ':' 61 | #2 TYPEID Object 62 | #2 '{' 63 | #3 OBJECTID out_string 64 | #3 '(' 65 | #3 STR_CONST "Hello world!\n" 66 | #3 ')' 67 | #3 ';' 68 | #4 '}' 69 | #4 ';' 70 | #5 '}' 71 | #5 ';' 72 | ``` 73 | 这个就是我们的目标输出内容 74 | 75 | 76 | 所以换句话说,我们的任务就是要让我们写的`lexer`的输出和官方的`lexer`的输出完全一样即可。 77 | 78 | 这里我copy了知乎用户[【一粟】的回答](https://zhuanlan.zhihu.com/p/423604775) 79 | 创建了`assignment/PA2/test`字路径,并添加了一个`compare.sh`脚本。这个脚本运行后可以自动帮你比较你的`lexer`和正确的`lexer`的输出是否一致。 80 | 81 | > 脚本中直接使用了Linux下的diff工具进行比对,我因为使用了VS Code的ssh链接,所以我是利用脚本生成输出文件后,再用VS Code的“选择以比较”功能来比较两个文件的异同,更加直观的认识到哪里有问题。 82 | 83 | ```sh 84 | #!/bin/bash 85 | 86 | #assignments/PA2/test/compare.sh 87 | 88 | cd .. 89 | make lexer 90 | cd - 91 | 92 | filename=$1 93 | 94 | if [[ $filename == "" ]] 95 | then 96 | filename="../test.cl" 97 | fi 98 | 99 | ../lexer $filename > my_result.log 100 | ../../../bin/lexer $filename > ref_result.log 101 | 102 | diff my_result.log ref_result.log 103 | 104 | ``` 105 | 106 | ### 其他需要关注文件 107 | 阅读完文档后,按照文中的提示需要注意`./include/PA2`路径下的两个头文件`cool-parse.h`和`stringtab.h`。 108 | - 前者保存了关键字的各种`宏定义`,这里需要注意这些关键字都是我们在正则表达式书写中最后需要`return`的token字段。只有`return`了,才会以井号形式显示出解析后的词法单元。 109 | - 后者构造了用于保存`identifier`的值的符号表,需要通过阅读这里的函数来了解如何将字符加入符号表中。在此文件的最后可以看到分开的3个符号表。 110 | ```C++ 111 | extern IdTable idtable; //存放TypeID(类名)和ObjectID(对象名) 112 | extern IntTable inttable; //存放int变量内容的表 113 | extern StrTable stringtable;//存放字符串变量的表 114 | ``` 115 | 虽然上述变量类型不一,但是实际存储的均是一段**字符串**作为内容。 116 | 117 | ## 代码书写 118 | 重点在于细节,不能有遗漏,还需要自行完善测试样例以覆盖所有情况。 119 | ### flex的结构 120 | ``` 121 | %{ 122 | declarations 123 | %} 124 | definitions 125 | %% 126 | rules 127 | %% 128 | user subroutines 129 | ``` 130 | `declarations`部分用于定义一些C++代码,它们会被原封不动地copy到`cool-lex.cc`中。`definition`部分用于定义正则表达式模板和"start condition"。`rules`部分用于定义状态机,这部分是最重要的。`user subroutines`部分用于定义一些C++函数以便简化代码,这部分是可选的。 131 | ### 关键字 132 | 首先在`declarations`区确定各个关键字的正则表达式,注意,关键字是不区分大小写的。 133 | ``` 134 | CLASS [cC][lL][aA][sS][sS] 135 | IF [iI][fF] 136 | FI [fF][iI] 137 | ELSE [eE][lL][sS][eE] 138 | IN [iI][nN] 139 | INHERITS [iI][nN][hH][eE][rR][iI][tT][sS] 140 | ISVOID [iI][sS][vV][oO][iI][dD] 141 | LET [lL][eE][tT] 142 | LOOP [lL][oO][oO][pP] 143 | POOL [pP][oO][oO][lL] 144 | THEN [tT][hH][eE][nN] 145 | WHILE [wW][hH][iI][lL][eE] 146 | CASE [cC][aA][sS][eE] 147 | ESAC [eE][sS][aA][cC] 148 | NEW [nN][eE][wW] 149 | OF [oO][fF] 150 | NOT [nN][oO][tT] 151 | TRUE [t][rR][uU][eE] 152 | FALSE [f][aA][lL][sS][eE] 153 | 154 | DARROW => 155 | ASSIGN <- 156 | LE <= 157 | ``` 158 | 需要注意的是,TRUE和FALSE关键字在语法中,是不能以大写字母开头匹配(区别于TypeID)。 159 | 然后需要在`rules`区域定义遇到这些关键字需要进行的操作,这里只需要返回对应的大写字母即可,宏定义会帮我们转换成对应的数值。需要注意的是,按照要求,`true`和`false`需要单独加入到`cool_yylval.boolean`这个域中,并单独`returen BOOL_CONST` 160 | ``` 161 | /* 162 | * The multiple-character operators. 163 | */ 164 | \n { ++curr_lineno;} 165 | {DARROW} { return (DARROW); } 166 | {ASSIGN} { return (ASSIGN);} 167 | {LE} { return (LE);} 168 | /* 169 | * Keywords are case-insensitive except for the values true and false, 170 | * which must begin with a lower-case letter. 171 | */ 172 | {CLASS} { return (CLASS);} 173 | {IF} { return (IF);} 174 | {FI} { return (FI);} 175 | {ELSE} { return (ELSE);} 176 | {IN} { return (IN);} 177 | {INHERITS} { return (INHERITS);} 178 | {ISVOID} { return (ISVOID);} 179 | {LET} { return (LET);} 180 | {LOOP} { return (LOOP);} 181 | {POOL} { return (POOL);} 182 | {THEN} { return (THEN);} 183 | {WHILE} { return (WHILE);} 184 | {CASE} { return (CASE);} 185 | {ESAC} { return (ESAC);} 186 | {NEW} { return (NEW);} 187 | {OF} { return (OF);} 188 | {NOT} { return (NOT);} 189 | 190 | 191 | {TRUE} { 192 | cool_yylval.boolean = true; 193 | return BOOL_CONST; 194 | } 195 | 196 | {FALSE} { 197 | cool_yylval.boolean = false; 198 | return BOOL_CONST; 199 | } 200 | ``` 201 | ### 空白符 202 | 首先是比较重要的换行符代码需要利用换行来反向定位每个词法单元的位置,这里是通过全局变量`curr_lineno`在`rules`区域实现控制: 203 | ``` 204 | \n { ++curr_lineno;} 205 | ``` 206 | 而在末尾,需要对无法匹配的其他空白符进行过滤,不需要进行任何操作,根据`cool`文档显示的内容有: 207 | ``` 208 | [ \t\f\r\v] {} 209 | ``` 210 | 211 | 212 | 213 | 214 | 215 | -------------------------------------------------------------------------------- /Assignment-Answer/PA1/myAnswer.md: -------------------------------------------------------------------------------- 1 | # 第一次作业:熟悉COOL语言 答案 2 | ## 作业要求 3 | 作业大意是通过`COOL`语言实现一个可以实现“整数加”计算的`stack`,且可以通过向输入区填入以下指令进行基本操作: 4 | 5 | ![](images/stack-command.png) 6 | 7 | 其中前三个指令是可以**进栈保留**的指令,后三个指令是对栈整体的操作。 8 | 9 | - 进栈保留的指令 10 | |指令|含义| 11 | |:-:|-| 12 | |int|向栈中推入一个整数| 13 | |+|向栈中推入一个‘+’号| 14 | |s|向栈中推入一个‘s’号| 15 | 16 | - 对栈整体的操作 17 | |指令|含义| 18 | |:-:|-| 19 | |e|对栈顶元素进行“计算”| 20 | |d|显示栈内所有的内容| 21 | |x|程序结束| 22 | 23 | 其中`e`操作对于栈顶不同的元素有不同的效果: 24 | - `int`:不产生任何改变 25 | - `+`:将`+`号及其后两个元素(默认正确情况均为`int`元素)弹出,并将两`int`元素的值相加后得到的`int`值推入栈 26 | - `s`:将`s`本身弹出,其后两个元素交换位置。 27 | ## 作业浅析 28 | + 这次作业主要是熟悉cool语言的写法,完成一个简单的“计算栈”的功能。 29 | + 设计者主要意图让学生了解`COOL`语言的继承功能,并且通过阅读文档熟悉语法。 30 | + 难点在于如何从纯英文的文档中快速找到所需的关键字、功能,并理解写法。 31 | + 完成时需要构建`Stack`类作为基本的数据结构,以及`StackCommand`类成员作为操作的载体(实际使用时需要派生新的类对应3种操作符)。 32 | ## 示例题解 33 | 按照题目推荐的解法,我首先构建了`StackCommand`类并派生了3种对应的类。 34 | 35 | 需要注意的是,这里在`excute`函数中使用了下文中提到的栈节点作为参数,此处先大致理解即可。 36 | 37 | ```java 38 | (* 39 | * CS164 Fall 94 40 | * 41 | * Programming Assignment 1 42 | * Implementation of a simple stack machine. 43 | * 44 | * Skeleton file 45 | *) 46 | 47 | class StackCommand { 48 | getChar() : String{ 49 | "Get char function called from base class" 50 | }; 51 | getInt() : Int { 52 | 0 53 | }; 54 | execute(node : StackNode) : StackNode{ 55 | node 56 | }; 57 | setInt(num : Int) : Int{ 58 | 0 59 | }; 60 | display() : String{ 61 | getChar() 62 | }; 63 | }; 64 | 65 | class IntCommand inherits StackCommand{ 66 | value : Int; 67 | trans : A2I <- new A2I; 68 | getChar() : String{ 69 | trans.i2a(value) 70 | }; 71 | getInt() : Int{ 72 | value 73 | }; 74 | --// execute Function is same as base 75 | --// display Function is same as base 76 | setInt(num : Int) : Int{ 77 | value <- num 78 | }; 79 | }; 80 | 81 | class PlusCommand inherits StackCommand{ 82 | getChar() : String{ 83 | "+" 84 | }; 85 | --// getInt() is as same as base 86 | execute(node : StackNode) : StackNode{{ 87 | if not (isvoid node.getNext()) then { 88 | if not (isvoid node.getNext().getNext()) then{ 89 | let n1 : StackNode <- node.getNext(), 90 | n2 : StackNode <- n1.getNext() in { 91 | { 92 | n2.getCommand().setInt(n1.getCommand().getInt() + n2.getCommand().getInt()); 93 | n2; 94 | }; 95 | }; 96 | } 97 | else 98 | 0 99 | fi; 100 | } 101 | else 102 | 0 103 | fi; 104 | node.getNext().getNext(); 105 | } 106 | }; 107 | --// display Function is same as base 108 | }; 109 | 110 | class SwapCommand inherits StackCommand{ 111 | getChar() : String { 112 | "s" 113 | }; 114 | execute(node : StackNode) : StackNode{ 115 | let tmp : StackNode <- node.getNext().getNext() in {{ 116 | node.getNext().setNext(tmp.getNext()); 117 | tmp.setNext(node.getNext()); 118 | tmp; 119 | }; 120 | } 121 | }; 122 | --// display Function is same as base 123 | }; 124 | ``` 125 | 126 | 接下来就需要先构建`StackNode`类作为栈的组件,并由它构成`Stack`类。`Stack`类它本身只保存了当前的头结点`head`,通过指针不断向后来访问全部内容。 127 | 128 | ```java 129 | class StackNode{ 130 | command : StackCommand; 131 | next : StackNode; 132 | init(co : StackCommand, ne : StackNode) : StackNode{ 133 | { 134 | command <- co; 135 | next <- ne; 136 | } 137 | }; 138 | getCommand() : StackCommand{ 139 | command 140 | }; 141 | getNext() : StackNode { 142 | next 143 | }; 144 | setNext(node : StackNode): StackNode{ 145 | next <- node 146 | }; 147 | }; 148 | --// Class of the Stack 149 | class Stack inherits IO{ 150 | nil : StackNode; --// Void pointer 151 | head : StackNode; 152 | trans : A2I <- new A2I; 153 | push(str : String) : StackNode{{ 154 | let tmp : StackNode <- head in { 155 | head <- new StackNode; --//operator + 156 | if str = "+" then 157 | head.init(new PlusCommand, tmp) 158 | else 159 | if str = "s" then --//operator S 160 | head.init(new SwapCommand, tmp) 161 | else --// int number 162 | { 163 | head.init(new IntCommand, tmp); 164 | head.getCommand().setInt(trans.a2i(str)); 165 | } 166 | fi 167 | fi; 168 | }; 169 | head; 170 | } 171 | }; 172 | 173 | pop() : StackNode{ 174 | if not(isvoid head) then 175 | head <- head.getCommand().execute(head) 176 | else 177 | head 178 | fi 179 | }; 180 | 181 | show() : Object{ 182 | if not(isvoid head) then 183 | let ptr : StackNode <- head in 184 | while not (isvoid ptr) loop{{ 185 | out_string(ptr.getCommand().display()); 186 | out_string(" "); 187 | ptr <- ptr.getNext(); 188 | }; 189 | } 190 | pool 191 | else 192 | 0 193 | fi 194 | }; 195 | 196 | }; 197 | ``` 198 | 最后,我们还需要构建主要的输入输出控制,这里通过嵌套分支语句实现即可。 199 | ```java 200 | class Main inherits IO { 201 | newline() : Object { 202 | out_string("\n") 203 | }; 204 | 205 | prompt() : String { 206 | { 207 | out_string(">"); 208 | in_string(); 209 | } 210 | }; 211 | 212 | x : String ; --// buff for input string 213 | testCommand : StackCommand <- new StackCommand; 214 | main() : Object { 215 | --// --// out_string("Nothing implemented\n" 216 | let stack : Stack <- new Stack in{ 217 | x <- prompt(); 218 | while not x = "x" loop{ 219 | if x = "e" then 220 | stack.pop() 221 | else 222 | if x = "d" then{ 223 | stack.show(); 224 | newline(); 225 | } 226 | else 227 | stack.push(x) 228 | fi 229 | fi; 230 | x <- prompt(); 231 | } 232 | pool; 233 | } 234 | }; 235 | }; 236 | 237 | ``` 238 | 239 | ## 总结 240 | 最重要的问题在于`COOL`语言起到C语言`return`作用的是代码块的求值,如果只有一行则无所谓,如果有多行代码,则需要在大括号内再套大括号,并通过`;`分割各个语句,这会使得代码的语法结构经常会有问题,需要特别注意。 241 | --------------------------------------------------------------------------------