14.1 第 0.1 版

首先对上一章的雏形版本稍微升级一下,增加变量声明和 print 语句,一共有 5 个文件:

词法分析文件: scanner.l

  1. %{
  2. #define YYSTYPE char *
  3. #include "y.tab.h"
  4. int cur_line = 1;
  5. void yyerror(const char *msg);
  6. void unrecognized_char(char c);
  7. #define _DUPTEXT {yylval = strdup(yytext);}
  8. %}
  9.  
  10. /* note \042 is '"' */
  11.  
  12. OPERATOR ([-/+*()=,;])
  13. INTEGER ([0-9]+)
  14. STRING (\042[^\042\n]*\042)
  15. IDENTIFIER ([_a-zA-Z][_a-zA-Z0-9]*)
  16. WHITESPACE ([ \t]*)
  17.  
  18. %%
  19. {OPERATOR} { return yytext[0]; }
  20. "int" { return T_Int; }
  21. "print" { return T_Print; }
  22.  
  23. {INTEGER} { _DUPTEXT; return T_IntConstant; }
  24. {STRING} { _DUPTEXT; return T_StringConstant; }
  25. {IDENTIFIER} { _DUPTEXT; return T_Identifier; }
  26.  
  27. {WHITESPACE} { /* ignore every whitespace */ }
  28. \n { cur_line++; }
  29. . { unrecognized_char(yytext[0]); }
  30. %%
  31.  
  32. int yywrap(void) {
  33. return 1;
  34. }
  35.  
  36. void unrecognized_char(char c) {
  37. char buf[32] = "Unrecognized character: ?";
  38. buf[24] = c;
  39. yyerror(buf);
  40. }
  41.  
  42. void yyerror(const char *msg) {
  43. printf("Error at line %d:\n\t%s\n", cur_line, msg);
  44. exit(-1);
  45. }

语法分析文件: parser.y

  1. %{
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. void yyerror(const char*);
  5. #define YYSTYPE char *
  6. %}
  7.  
  8. %token T_StringConstant T_IntConstant T_Identifier T_Int T_Print
  9.  
  10. %left '+' '-'
  11. %left '*' '/'
  12. %right U_neg
  13.  
  14. %%
  15.  
  16. S:
  17. Stmt { /* empty */ }
  18. | S Stmt { /* empty */ }
  19. ;
  20.  
  21. Stmt:
  22. VarDecl ';' { printf("\n\n"); }
  23. | Assign { /* empty */ }
  24. | Print { /* empty */ }
  25. ;
  26.  
  27. VarDecl:
  28. T_Int T_Identifier { printf("var %s", $2); }
  29. | VarDecl ',' T_Identifier { printf(", %s", $3); }
  30. ;
  31.  
  32. Assign:
  33. T_Identifier '=' E ';' { printf("pop %s\n\n", $1); }
  34. ;
  35.  
  36. Print:
  37. T_Print '(' T_StringConstant Actuals ')' ';'
  38. { printf("print %s\n\n", $3); }
  39. ;
  40.  
  41. Actuals:
  42. /* empty */ { /* empty */ }
  43. | Actuals ',' E { /* empty */ }
  44. ;
  45.  
  46. E:
  47. E '+' E { printf("add\n"); }
  48. | E '-' E { printf("sub\n"); }
  49. | E '*' E { printf("mul\n"); }
  50. | E '/' E { printf("div\n"); }
  51. | '-' E %prec U_neg { printf("neg\n"); }
  52. | T_IntConstant { printf("push %s\n", $1); }
  53. | T_Identifier { printf("push %s\n", $1); }
  54. | '(' E ')' { /* empty */ }
  55. ;
  56.  
  57. %%
  58.  
  59. int main() {
  60. return yyparse();
  61. }

makefile 文件: makefile

  1. OUT = tcc
  2. TESTFILE = test.c
  3. SCANNER = scanner.l
  4. PARSER = parser.y
  5.  
  6. CC = gcc
  7. OBJ = lex.yy.o y.tab.o
  8. TESTOUT = $(basename $(TESTFILE)).asm
  9. OUTFILES = lex.yy.c y.tab.c y.tab.h y.output $(OUT)
  10.  
  11. .PHONY: build test simulate clean
  12.  
  13. build: $(OUT)
  14.  
  15. test: $(TESTOUT)
  16.  
  17. simulate: $(TESTOUT)
  18. python pysim.py $<
  19.  
  20. clean:
  21. rm -f *.o $(OUTFILES)
  22.  
  23. $(TESTOUT): $(TESTFILE) $(OUT)
  24. ./$(OUT) < $< > $@
  25.  
  26. $(OUT): $(OBJ)
  27. $(CC) -o $(OUT) $(OBJ)
  28.  
  29. lex.yy.c: $(SCANNER) y.tab.c
  30. flex $<
  31.  
  32. y.tab.c: $(PARSER)
  33. bison -vdty $<

测试文件: test.c

  1. int a, b, c, d;
  2. a = 1 + 2 * ( 2 + 2 );
  3. c = 5;
  4. d = 10;
  5. b = c + d;
  6.  
  7. print("a = %d, b = %d, c = %d, d = %d", a, b, c, d);

Pcode 模拟器: pysim.py ,已经在第 4 章中介绍了。

这个版本在上一章的雏形版本的基础上,进行了以下扩充:

词法分析文件中:

增加了 T_StringConstant, T_Int, T_Print 类型的 token ,以及相应的正则表达式;

增加了一个 _DUPTEXT 宏,表示 yylval = strdup(yytext) 。

语法分析文件中:

增加了 VarDecl 和 Print 两个非终结符以及相应的产生式。

本版本的语法分析文件中,同样要注意源文件的解析过程中各产生式的折叠顺序以及相应的 Pcode 生成顺序。

makefile 里面是编译和测试这个程序的命令,在终端输入 make 后,将编译生成可执行文件 tcc ,然后输入 make test ,(相当于 ”./tcc < test.c > test.asm” ) ,将输出 test.asm 文件,内容如下:

  1. var a, b, c, d
  2.  
  3. push 1
  4. push 2
  5. push 2
  6. push 2
  7. add
  8. mul
  9. add
  10. pop a
  11.  
  12. push 5
  13. pop c
  14.  
  15. push 10
  16. pop d
  17.  
  18. push c
  19. push d
  20. add
  21. pop b
  22.  
  23. push a
  24. push b
  25. push c
  26. push d
  27. print "a = %d, b = %d, c = %d, d = %d"

可以看出 test.c 文件里的所有语句都被转换成相应的 Pcode 了。再用 Pcode 模拟器运行一下这些 Pcode ,在终端输入 “make simulate” (相当于 “python pysim.py test.asm” ) ,将输出:

  1. a = 9, b = 5, c = 10, d = 15