14.3 第 1.0 版
继续在第 0.5 版的基础上升级,增加 if 和 while 语句、比较运算符和逻辑运算符以及 readint 命令,就形成了完整的 TinyC 前端。一共有 7 个文件:
词法分析文件: 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; }
- "readint" { return T_ReadInt; }
- "while" { return T_While; }
- "if" { return T_If; }
- "else" { return T_Else; }
- "break" { return T_Break; }
- "continue" { return T_Continue; }
- "<=" { return T_Le; }
- ">=" { return T_Ge; }
- "==" { return T_Eq; }
- "!=" { return T_Ne; }
- "&&" { return T_And; }
- "||" { return T_Or; }
- {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 *
- int ii = 0, itop = -1, istack[100];
- int ww = 0, wtop = -1, wstack[100];
- #define _BEG_IF {istack[++itop] = ++ii;}
- #define _END_IF {itop--;}
- #define _i (istack[itop])
- #define _BEG_WHILE {wstack[++wtop] = ++ww;}
- #define _END_WHILE {wtop--;}
- #define _w (wstack[wtop])
- %}
- %token T_Int T_Void T_Return T_Print T_ReadInt T_While
- %token T_If T_Else T_Break T_Continue T_Le T_Ge T_Eq T_Ne
- %token T_And T_Or T_IntConstant T_StringConstant T_Identifier
- %left '='
- %left T_Or
- %left T_And
- %left T_Eq T_Ne
- %left '<' '>' T_Le T_Ge
- %left '+' '-'
- %left '*' '/' '%'
- %left '!'
- %%
- 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("\targ %s", $2); }
- | _Args ',' T_Int T_Identifier
- { printf(", %s", $4); }
- ;
- VarDecls:
- /* empty */ { /* empty */ }
- | VarDecls VarDecl ';' { printf("\n\n"); }
- ;
- VarDecl:
- T_Int T_Identifier { printf("\tvar %s", $2); }
- | VarDecl ',' T_Identifier
- { printf(", %s", $3); }
- ;
- Stmts:
- /* empty */ { /* empty */ }
- | Stmts Stmt { /* empty */ }
- ;
- Stmt:
- AssignStmt { /* empty */ }
- | PrintStmt { /* empty */ }
- | CallStmt { /* empty */ }
- | ReturnStmt { /* empty */ }
- | IfStmt { /* empty */ }
- | WhileStmt { /* empty */ }
- | BreakStmt { /* empty */ }
- | ContinueStmt { /* empty */ }
- ;
- AssignStmt:
- T_Identifier '=' Expr ';'
- { printf("\tpop %s\n\n", $1); }
- ;
- PrintStmt:
- T_Print '(' T_StringConstant PActuals ')' ';'
- { printf("\tprint %s\n\n", $3); }
- ;
- PActuals:
- /* empty */ { /* empty */ }
- | PActuals ',' Expr { /* empty */ }
- ;
- CallStmt:
- CallExpr ';' { printf("\tpop\n\n"); }
- ;
- CallExpr:
- T_Identifier '(' Actuals ')'
- { printf("\t$%s\n", $1); }
- ;
- Actuals:
- /* empty */ { /* empty */ }
- | Expr PActuals { /* empty */ }
- ;
- ReturnStmt:
- T_Return Expr ';' { printf("\tret ~\n\n"); }
- | T_Return ';' { printf("\tret\n\n"); }
- ;
- IfStmt:
- If TestExpr Then StmtsBlock EndThen EndIf
- { /* empty */ }
- | If TestExpr Then StmtsBlock EndThen Else StmtsBlock EndIf
- { /* empty */ }
- ;
- TestExpr:
- '(' Expr ')' { /* empty */ }
- ;
- StmtsBlock:
- '{' Stmts '}' { /* empty */ }
- ;
- If:
- T_If { _BEG_IF; printf("_begIf_%d:\n", _i); }
- ;
- Then:
- /* empty */ { printf("\tjz _elIf_%d\n", _i); }
- ;
- EndThen:
- /* empty */ { printf("\tjmp _endIf_%d\n_elIf_%d:\n", _i, _i); }
- ;
- Else:
- T_Else { /* empty */ }
- ;
- EndIf:
- /* empty */ { printf("_endIf_%d:\n\n", _i); _END_IF; }
- ;
- WhileStmt:
- While TestExpr Do StmtsBlock EndWhile
- { /* empty */ }
- ;
- While:
- T_While { _BEG_WHILE; printf("_begWhile_%d:\n", _w); }
- ;
- Do:
- /* empty */ { printf("\tjz _endWhile_%d\n", _w); }
- ;
- EndWhile:
- /* empty */ { printf("\tjmp _begWhile_%d\n_endWhile_%d:\n\n",
- _w, _w); _END_WHILE; }
- ;
- BreakStmt:
- T_Break ';' { printf("\tjmp _endWhile_%d\n", _w); }
- ;
- ContinueStmt:
- T_Continue ';' { printf("\tjmp _begWhile_%d\n", _w); }
- ;
- Expr:
- Expr '+' Expr { printf("\tadd\n"); }
- | Expr '-' Expr { printf("\tsub\n"); }
- | Expr '*' Expr { printf("\tmul\n"); }
- | Expr '/' Expr { printf("\tdiv\n"); }
- | Expr '%' Expr { printf("\tmod\n"); }
- | Expr '>' Expr { printf("\tcmpgt\n"); }
- | Expr '<' Expr { printf("\tcmplt\n"); }
- | Expr T_Ge Expr { printf("\tcmpge\n"); }
- | Expr T_Le Expr { printf("\tcmple\n"); }
- | Expr T_Eq Expr { printf("\tcmpeq\n"); }
- | Expr T_Ne Expr { printf("\tcmpne\n"); }
- | Expr T_Or Expr { printf("\tor\n"); }
- | Expr T_And Expr { printf("\tand\n"); }
- | '-' Expr %prec '!' { printf("\tneg\n"); }
- | '!' Expr { printf("\tnot\n"); }
- | T_IntConstant { printf("\tpush %s\n", $1); }
- | T_Identifier { printf("\tpush %s\n", $1); }
- | ReadInt { /* empty */ }
- | CallExpr { /* empty */ }
- | '(' Expr ')' { /* empty */ }
- ;
- ReadInt:
- T_ReadInt '(' T_StringConstant ')'
- { printf("\treadint %s\n", $3); }
- ;
- %%
- int main() {
- return yyparse();
- }
makefile 文件: makefile ,内容和 第 0.5 版是一样的。
测试文件: test.c ,就是第二章的的示例源程序。
- #include "for_gcc_build.hh" // only for gcc, TinyC will ignore it.
- int main() {
- int i;
- i = 0;
- while (i < 10) {
- i = i + 1;
- if (i == 3 || i == 5) {
- continue;
- }
- if (i == 8) {
- break;
- }
- print("%d! = %d", i, factor(i));
- }
- return 0;
- }
- int factor(int n) {
- if (n < 2) {
- return 1;
- }
- return n * factor(n - 1);
- }
测试文件包:samples.zip ,包含了 7 个测试文件。
测试脚本: test_samples.sh 。
Pcode 模拟器: pysim.py 。
这个版本在第 0.1 版本的基础上,进行了以下扩充:
词法分析文件中:
增加了 T_Void 和 T_Return 类型的 token ,以及相应的正则表达式。语法分析文件中:
增加了 IfStmt, WhileStmt, BreakStmt, ContinueStmt, ReadInt 等非终结符以及相应的产生式,请注意各产生式的折叠顺序以及相应的 Pcode 生成顺序;
增加了比较运算符、逻辑运算符,以及相应的优先级;
在 Declarations 段,增加了几个全局变量和宏:
- int ii = 0, itop = -1, istack[100];int ww = 0, wtop = -1, wstack[100];#define _BEG_IF {istack[++itop] = ++ii;}#define _END_IF {itop—;}#define _i (istack[itop])#define _BEG_WHILE {wstack[++wtop] = ++ww;}#define _END_WHILE {wtop—;}#define _w (wstack[wtop])
这些全局变量和宏配合后面的 if/while 语句产生式中的 action 使用,是该文件中的最精妙的部分,它们的作用是:在生成 if 和 while 语句块的 Pcode 的过程中,给相应的 Label 进行编号。它们给每个 if 语句块和每个 while 语句块一个唯一的编号,使不同的 if/while 语句块的 jmp 不相互冲突。其中 _i 永远是当前的 if 语句块的编号, _w 永远是当前的 while 语句块的编号; ii/ww 永远是目前解析到的 if/while 语句块的总数。
将以上所有文件都放在当前目录,在终端直接输入 make test ,将自动编译生成 TinyC 前端: tcc ,并自动调用 tcc 将 test.c 编译成 test.asm 文件,内容如下,和第 5 章的手工编译的结果差不多吧:
- FUNC @main:
- var i
- push 0
- pop i
- _begWhile_1:
- push i
- push 10
- cmplt
- jz _endWhile_1
- push i
- push 1
- add
- pop i
- _begIf_1:
- push i
- push 3
- cmpeq
- push i
- push 5
- cmpeq
- or
- jz _elIf_1
- jmp _begWhile_1
- jmp _endIf_1
- _elIf_1:
- _endIf_1:
- _begIf_2:
- push i
- push 8
- cmpeq
- jz _elIf_2
- jmp _endWhile_1
- jmp _endIf_2
- _elIf_2:
- _endIf_2:
- push i
- push i
- $factor
- print "%d! = %d"
- jmp _begWhile_1
- _endWhile_1:
- push 0
- ret ~
- ENDFUNC
- FUNC @factor:
- arg n
- _begIf_3:
- push n
- push 2
- cmplt
- jz _elIf_3
- push 1
- ret ~
- jmp _endIf_3
- _elIf_3:
- _endIf_3:
- push n
- push n
- push 1
- sub
- $factor
- mul
- ret ~
- ENDFUNC
再输入 “make simulate”,将输出:
- 1! = 1
- 2! = 2
- 4! = 24
- 6! = 720
- 7! = 5040
和第二章中用 gcc 编译并运行此文件的结果完全一样。
再把测试文件包里的所有源文件全部测试一遍,将 samples.zip 解压到 samples 目录下,测试脚本 test_samples.sh 将分别调用 tcc 和 gcc 编译测试文件包中的每一个文件,并分别使用 pysim.py 和 操作系统 运行编译得到的目标文件,内容如下:
- for src in $(ls samples/*.c)
- do
- clear
- file=${src%%.c}
- echo build with tcc
- ./tcc < $file.c > $file.asm
- python pysim.py $file.asm -a
- echo
- echo build with gcc
- gcc -o $file $file.c
- ./$file
- echo
- echo press any key to continue...
- read -n 1
- done
在终端输入 bash ./test_samples.sh ,将分别输出一系列的结果,典型输出如下,可以看到 gcc 和 tcc 编译运行的结果完全一致。
- build with tcc, the output are:
- The first 10 number of the fibonacci sequence:
- fib(1)=1
- fib(2)=1
- fib(3)=2
- fib(4)=3
- fib(5)=5
- fib(6)=8
- fib(7)=13
- fib(8)=21
- fib(9)=34
- fib(10)=55
- build with gcc, the output are:
- The first 10 number of the fibonacci sequence:
- fib(1)=1
- fib(2)=1
- fib(3)=2
- fib(4)=3
- fib(5)=5
- fib(6)=8
- fib(7)=13
- fib(8)=21
- fib(9)=34
- fib(10)=55
至此 TinyC 前端完成。
第 14 章完