14.2 第 0.5 版
在第 0.1 版的基础上升级,增加函数定义及调用语句、注释等功能,一共有 5 个文件:
词法分析文件: scanner.l
- %{
- #define YYSTYPE char *
- #include "y.tab.h"
- int cur_line = 1;
- void yyerror(const char *msg);
- void unrecognized_char(char c);
- void unterminate_string();
- #define _DUPTEXT {yylval = strdup(yytext);}
- %}
- /* note \042 is '"' */
- WHITESPACE ([ \t\r\a]+)
- SINGLE_COMMENT1 ("//"[^\n]*)
- SINGLE_COMMENT2 ("#"[^\n]*)
- OPERATOR ([+*-/%=,;!<>(){}])
- INTEGER ([0-9]+)
- IDENTIFIER ([_a-zA-Z][_a-zA-Z0-9]*)
- UNTERM_STRING (\042[^\042\n]*)
- STRING (\042[^\042\n]*\042)
- %%
- \n { cur_line++; }
- {WHITESPACE} { /* ignore every whitespace */ }
- {SINGLE_COMMENT1} { /* skip for single line comment */ }
- {SINGLE_COMMENT2} { /* skip for single line comment */ }
- {OPERATOR} { return yytext[0]; }
- "int" { return T_Int; }
- "void" { return T_Void; }
- "return" { return T_Return; }
- "print" { return T_Print; }
- {INTEGER} { _DUPTEXT return T_IntConstant; }
- {STRING} { _DUPTEXT return T_StringConstant; }
- {IDENTIFIER} { _DUPTEXT return T_Identifier; }
- {UNTERM_STRING} { unterminate_string(); }
- . { unrecognized_char(yytext[0]); }
- %%
- int yywrap(void) {
- return 1;
- }
- void unrecognized_char(char c) {
- char buf[32] = "Unrecognized character: ?";
- buf[24] = c;
- yyerror(buf);
- }
- void unterminate_string() {
- yyerror("Unterminate string constant");
- }
- void yyerror(const char *msg) {
- fprintf(stderr, "Error at line %d:\n\t%s\n", cur_line, msg);
- exit(-1);
- }
语法分析文件: parser.y
- %{
- #include <stdio.h>
- #include <stdlib.h>
- void yyerror(const char*);
- #define YYSTYPE char *
- %}
- %token T_Int T_Void T_Return T_Print T_IntConstant
- %token T_StringConstant T_Identifier
- %left '+' '-'
- %left '*' '/'
- %right U_neg
- %%
- Program:
- /* empty */ { /* empty */ }
- | Program FuncDecl { /* empty */ }
- ;
- FuncDecl:
- RetType FuncName '(' Args ')' '{' VarDecls Stmts '}'
- { printf("ENDFUNC\n\n"); }
- ;
- RetType:
- T_Int { /* empty */ }
- | T_Void { /* empty */ }
- ;
- FuncName:
- T_Identifier { printf("FUNC @%s:\n", $1); }
- ;
- Args:
- /* empty */ { /* empty */ }
- | _Args { printf("\n\n"); }
- ;
- _Args:
- T_Int T_Identifier { printf("arg %s", $2); }
- | _Args ',' T_Int T_Identifier
- { printf(", %s", $4); }
- ;
- VarDecls:
- /* empty */ { /* empty */ }
- | VarDecls VarDecl ';' { printf("\n\n"); }
- ;
- VarDecl:
- T_Int T_Identifier { printf("var %s", $2); }
- | VarDecl ',' T_Identifier
- { printf(", %s", $3); }
- ;
- Stmts:
- /* empty */ { /* empty */ }
- | Stmts Stmt { /* empty */ }
- ;
- Stmt:
- AssignStmt { /* empty */ }
- | PrintStmt { /* empty */ }
- | CallStmt { /* empty */ }
- | ReturnStmt { /* empty */ }
- ;
- AssignStmt:
- T_Identifier '=' Expr ';'
- { printf("pop %s\n\n", $1); }
- ;
- PrintStmt:
- T_Print '(' T_StringConstant PActuals ')' ';'
- { printf("print %s\n\n", $3); }
- ;
- PActuals:
- /* empty */ { /* empty */ }
- | PActuals ',' Expr { /* empty */ }
- ;
- CallStmt:
- CallExpr ';' { printf("pop\n\n"); }
- ;
- CallExpr:
- T_Identifier '(' Actuals ')'
- { printf("$%s\n", $1); }
- ;
- Actuals:
- /* empty */ { /* empty */ }
- | Expr PActuals { /* empty */ }
- ;
- ReturnStmt:
- T_Return Expr ';' { printf("ret ~\n\n"); }
- | T_Return ';' { printf("ret\n\n"); }
- ;
- Expr:
- Expr '+' Expr { printf("add\n"); }
- | Expr '-' Expr { printf("sub\n"); }
- | Expr '*' Expr { printf("mul\n"); }
- | Expr '/' Expr { printf("div\n"); }
- | '-' Expr %prec U_neg { printf("neg\n"); }
- | T_IntConstant { printf("push %s\n", $1); }
- | T_Identifier { printf("push %s\n", $1); }
- | CallExpr { /* empty */ }
- | '(' Expr ')' { /* empty */ }
- ;
- %%
- int main() {
- return yyparse();
- }
makefile 文件: makefile, 和第 0.1 版本中唯一不同的只有 “python pysim.py $< -a” 那一行有一个 “-a” 。
测试文件: test.c
- // tiny c test file
- int main() {
- int a, b, c, d;
- c = 2;
- d = c * 2;
- a = sum(c, d);
- b = sum(a, d);
- print("c = %d, d = %d", c, d);
- print("a = sum(c, d) = %d, b = sum(a, d) = %d", a, b);
- return 0;
- }
- int sum(int a, int b) {
- int c, d;
- return a + b;
- }
Pcode 模拟器: pysim.py ,已经在第 4 章中介绍了。
这个版本在第 0.1 版本的基础上,进行了以下扩充:
词法分析文件中:
增加了 T_Void 和 T_Return 类型的 token ,以及相应的正则表达式;
增加了单行注释的过滤功能;增加了一个错误处理函数: unterminate_string ,该函数可以检查出未结束的字符串(不匹配的双引号)的词法错误。
语法分析文件中:
增加了 Program, FuncDecl, Args, Actuals, CallExpr 等非终结符以及相应的产生式,请注意各产生式的折叠顺序以及相应的 Pcode 生成顺序。
makefile 里面是编译和测试这个程序的命令,内容和第 0.1 版的基本一样,但增加了一些变量以便于扩充,另外,”python pysim.py…” 那一行最后的命令行参数是 “-a” 。在终端输入 make 后,将编译生成可执行文件 tcc ,然后输入 make test ,(相当于 ”./tcc < test.c > test.asm” ) ,将输出 test.asm 文件,内容如下:
- FUNC @main:
- var a, b, c, d
- push 2
- pop c
- push c
- push 2
- mul
- pop d
- push c
- push d
- $sum
- pop a
- push a
- push d
- $sum
- pop b
- push c
- push d
- print "c = %d, d = %d"
- push a
- push b
- print "a = sum(c, d) = %d, b = sum(a, d) = %d"
- push 0
- ret ~
- ENDFUNC
- FUNC @sum:
- arg a, b
- var c, d
- push a
- push b
- add
- ret ~
- ENDFUNC
可以看出 test.c 文件里的所有语句都被转换成相应的 Pcode 了。再用 Pcode 模拟器运行一下这些 Pcode ,在终端输入 “make simulate” (相当于 “python pysim.py test.asm -a” ,注意最后有一个 “-a” ) ,将输出:
- c = 2, d = 4
- a = sum(c, d) = 6, b = sum(a, d) = 10
有兴趣的读者还可以使用 “python pysim.py test.asm -da” 来逐句运行一下这个 Pcode 文件。