LCC编译器源程序分析(8)语法分析开始x

来源:初三 发布时间:2020-08-31 点击:

 LCC 编译器的源程序分析(8) 语法分析的开始 准备好词法分析之后,接着的工作就是检查源程序是否合法,以及源程序表达的意思是什么。这两个问题就是语法和语义的分析,也就是把源程序里所包含的属性分析出来,并保存到符号表里。下面就来仔细地分析 LCC 编译器是怎么样处理这两个问题的。

 1 t = gettok(); 2

  3

 //调用后端代码生成初始化工作。

 4

 (*IR->progbeg)(argc, argv); 5

 6

 for (i = 1; i < argc; i++) 7

 { 8

  if (strcmp(argv[i], "-n") == 0)

 09

  { 0

  if (!YYnull)

 1

  { 2

 = install(string("_YYnull"), &globals, GLOBAL, PERM); 3

 type = func(voidptype, NULL, 1); 4

 YYnull->sclass = EXTERN; 5

 (*IR->defsymbol)(YYnull); 6

  } 7

 8

  }

 19

  else if (strncmp(argv[i], "-n", 2) == 0)

 0

  {

 /* -nvalid[,check] */ 1

  char *p = strchr(argv[i], ",");

 2

  if (p)

 3

  { 4

 = install(string(p+1), &globals, GLOBAL, PERM); 5

 type = func(voidptype, NULL, 1); 6

 YYcheck->sclass = EXTERN; 7

 (*IR->defsymbol)(YYcheck); 8

 p = stringn(argv[i]+2, p - (argv[i]+2)); 29

  }

 0

  else 1

  { 2

 p = string(argv[i]+2); #033

  }

 4

 5

  = install(p, &globals, GLOBAL, PERM); 6

  type = func(voidptype, NULL, 1); 7

  YYnull->sclass = EXTERN; 8

  (*IR->defsymbol)(YYnull); 39

 0

  }

 1

  else

 2

  { 3

  profInit(argv[i]); 4

  traceInit(argv[i]); 5

  } 6

 } 7

 8

 if (glevel && IR->stabinit) 49

 { 0

  (*IR->stabinit)(firstfile, argc, argv); 1

 }

  2

 3

 //开始整个程序处理。

 #054

 program(); 在获取一个记号之后,在第 4 行就调用后端代码生成器准备生成代码的初始化。接着第 6 行到第 46 行是处理-n 参数,由于我们的例子里没有这些参数,就不会运行这些代码。把这些不是很重要的细节代码放下,我们继续地分析后面的代码。

 第 48 行到第 51 行是调用后端 stabinit 进行初始化。

 最重要的语法和语义分析开始了,它就是在第 54 行里调用函数 program。由于这个 C 编译器是使用递归下降的分析方法来进行语法和语义分析的,所以在这个函数里面会有很多递归调用的函数。简单地来说递归调用就如下面的形式:

 int Test1(void) {

 return Test3(); }

  int Test2(void) {

 return Test1();

 }

  int Test3(void) {

  return Test2(); } 上面只是形式说明一下,并不能进行运行的程序,现在就去分析 C 编译器的语法语义分析的总入口函数 program:

 蔡军生 2007/5/18 于深圳 // 1 //程序开始分析。

 2 void program(void)

 3 { 4

 int n; 5

 6

 //作用域为全局。

 7

 level = GLOBAL; 8

 09

 //分析源程序到文件尾。

 0

 for (n = 0; t != EOI; n++) 1

 { 2

  if (kind[t] == CHAR || kind[t] == STATIC 3

  || t == ID || t == "*" || t == "(")

 4

  { 5

  //声明开始. 6

  decl(dclglobal); 7

 8

  deallocate(STMT); 19

  if (!(glevel >= 3 || xref)) 0

  { 1

 deallocate(FUNC); 2

  }

  3

  }

 4

  else if (t == ";")

 5

  { 6

  warning("empty declaration/n"); 7

  t = gettok(); 8

  }

 29

  else

 0

  { 1

  error("unrecognized declaration/n"); 2

  t = gettok(); #033

  }

 4

 } 5

 6

 if (n == 0) 7

 { 8

  warning("empty input file/n"); 39

 }

  #040 } 函数开始的第 7 行设置作用域为全局作用域 GLOBAL。比如分析例子里的代码,在main 函数以外的代码,都是全局作用域的。

 10 行使用一个 for 循环,它的终止条件是分析源程序文件到结束,第 11 行到34 行里都是分析源程序的代码。

 第 12 行和第 13 行就判断开始的记号是否合法的。先看一下,就发现有一个数组kind[t],并且用它来判断记号的合法性。这个数组的定义如下:

 char kind[] = { xx(a,b,c,d,e,f,g) f, #define yy(a,b,c,d,e,f,g) f, #include "token.h" }; 上面定义了两个宏,然后包含了头文件 token.h,因为所有宏定义解释中在头文件里,如下:

 1 /* 2 xx(symbol,

  value,

  prec,

 op, optree, kind,

 string) 3 */ 4 yy(0,

  0, 0, 0,

 0,

 0,

 0) 5 FLOAT,

  1, 0, 0,

 0,

 CHAR,

  "float") 6 DOUBLE,

 2, 0, 0,

 0,

 CHAR,

  "double") 7 CHAR,

 3, 0, 0,

 0,

 CHAR,

  "char") 8 SHORT,

  4, 0,

 0,

 CHAR,

  "short") 09 INT,

  5, 0, 0,

 0,

 CHAR,

  "int") 0 UNSIGNED, 6, 0, 0,

 0,

 CHAR,

  "unsigned") 1 PINTER,

  7, 0, 0,

 0,

 0,

 "pointer") 2 VOID,

 8, 0, 0,

 0,

 CHAR,

  "void") 3 STRUCT,

 9,

 ,

 ,

  "struct4 UNION,

 10, 0, 0,

 0,

 CHAR,

  "union") 5 FUNCTION, 11, 0,

 0,

 0,

 "function") 6 ARRAY,

 12, 0, 0,

 0,

 0,

 "array") 7 ENUM,

  13, 0, 0,

 0,

 CHAR,

  "enum") 8 LG,

  14, 0, 0,

 0,

 CHAR,

  "long") 19 CONST,

 15, 0, 0,

 0,

 CHAR,

  "const") #020 xx(VOLATILE, 16, 0, 0,

 0,

 CHAR,

  "volatile")

 1 7 2 80,

 0,

 0,

 0) 3 190,

 0,

 0,

 0) 4 0 5 1 6 2 7 3 8 4 29 5 0 6 1 70) 2 8"long long") 3 29 4 00) 5 yy(0,

 31,

  0,

 0,

 0,

 0,

 "const volatile") 6 xx(ID,

  32,

  0,

 0,

 0,

 ID,

  "identifier") 7 yy(0,

 33,

  0,

 0,

 0,

 ID,

  "!") 8 FCON,

  34,

  0,

 0,

 0,

 ID,

  "floating constant") 39 ICON,

  35,

  0,

 0,

  0,

 ID,

  "integer constant") 0 xx(SCON,

  36,

  0,

 0,

 0,

 ID,

  "string constant") 1 713,

  MOD, bittree,"%",

 "%") 2 yy(0,

 38,

  8,

 BAND, bittree,ID,

  "&") 3 xx(INCR,

  39,

  0,

 ADD, addtree,ID,

  "++") 4 0ID,

  "(") 5 1

  0,

 0

 0,

 ")",

 ")") 6 23MUL, multree,ID,

  "* 7 32, ADD, addtree,ID,

  "+") 8 4, 0,

 0,

 ",",

 ",") 49 512, SUB, subtree,ID,

  "-") 0 60, 0,

 0,

 ".",

 ".") 1 yy(0,

 47, 13, DIV, multree,"/",

 "/") 2 CR,

  48, 0, SUB, subtree,ID,

  "--") 3 DEREF,

 49, 0, 0,

 0,

 DEREF, "->") 4 ANDAND,

  50, 5, AND, andtree,ANDAND, "&&") 5 OROR,

  51, 4OR,

  andtree,OROR,

  "||6 LEQ52, 10, LE,

  cmptree,LEQ,

 "<=") 7 EQL,

  53,

  9,

 EQ,

  eqtree, EQL,

 "==") 8 NEQ,

  54,

  9,

 NE,

  eqtree, NEQ,

 "!=") 59 GEQ,

  55,

  10,

  GE,

  cmptree,GEQ,

 ">=") #060 xx(RSHIFT,

 56,

  11,

  RSH,

 shtree, RSHIFT, ">>")

 1 xx(LSHIFT,

 57,

  11,

  LSH,

 shtree, LSHIFT, "<<") 2 8":",

 ":") 3 590,

 0,

 0,

 IF,

  ";") 4 010,

  LT,

  cmptree,"<",

 "<") 5 12,

 ASGN,

  asgntree,"=",

  "=") 6 210,

  GT,

  cmptree,">",

 ">") 7 yy(0,

  63,

  0,

 0,

 0,

 "?",

 "?") 8 ELLIPSIS,

 64,

  0,

 0,

 0,

 ELLIPSIS,"...") 69 xx(SIZEOF,

 65,

  0,

 0,

 0,

 ID,

  "sizeof") 0 yy(0,

  66,

  0,

 0,

 0,

 0,

 0) 1 xx(AUTO,

 67,

  0,

 0,

 0,

 STATIC, "auto") 2

 xx(BREAK,

  68,

  0,

 0,

 0,

 IF,

  "break") 3 ASE,

 69,

  0,

 0,

 0,

 IF,

  "case") 4 CONTINUE,

 70,

  0,

 0,

 0,

 IF,

  "continue") 5 EFAULT,

  71,

  0,

 0,

 0,

 IF,

  "default") 6 DO,

 72,

  0,

 0,

 0,

 IF,

  "do") 7 LSE,

 73,

  0,

 0,

 0,

 IF,

  "else") 8 EXTERN,

 74,

  0,

 0,

 0,

 STATIC, "extern") 79 FOR,

  75,

  0,

 0,

 0,

 IF,

  "for") 0 GOTO,

 76,

  0,

 0,

 0,

 IF,

  "goto") 1 IF,

 77,

  0,

 0,

 0,

 IF,

  "if") 2 GISTER,

 78,

  0,

 0,

 0,

 STATIC, "register") 3 RETURN,

 79,

  0,

 0,

 0,

 IF,

  "return") 4 IGNED,

 80,

  0,

 0,

 0,

 CHAR,

  "signed") 5 TATIC,

 81,

  0,

 0,

 0,

 STATIC, "static") 6 SWITCH,

 82,

  0,

  0,

 0,

 IF,

  "switch") 7 TYPEDEF,

  83,

  0,

 0,

 0,

 STATIC, "typedef") 8 WHILE,

  84,

  0,

 0,

 0,

 IF,

  "while") 89 TYPECODE,

 85,

  0,

 0,

 0,

 ID,

  "__typecode") 0 xx(FIRSTARG,

 86,

  0,

 0,

 0,

 ID,

  "__firstarg") 1 7 2 8 3 89 4 00,

 0) 5 1"[",

 "[") 6 20,

 0,

 0) 7 300,

  0,

 "]",

 "]") 8 47BXOR,

  bittree,"^",

 "^") #099 5 #100 yy(0,

  96,

  0,

 0,

 0,

 0,

 0)

 1 70,

 0,

 0,

 0) 2 8 3 99,

  0,

 0,

 0,

 0,

 0) 4 0 5 1 6 2 7 3 8 4 09 5 0 6 1 7 2 8 3 09 4 0 5 1 6 2 7 3 8 4 19 5 0 6 1 7 2 8 3 19 4 0 5 1 6 20,

 0) 7 300,

 0,

 IF,

  "{") 8 46BOR,

 bittree,"|",

 "|") 29 50,

 0

 "}",

 "}") 0 yy(0,

  126,

 0,

 BCOM,

  0,

 ID,

  "~") 1 xx(EOI,

  127,

 0,

 0,

 0,

 EOI,

 "end of input") 2 xx #133 #undef yy 上面的构造了一个表格,这样可以灵活地构造任何的对应关系的表格。

 因此生成的 kind[]数组如下:

 kind[] = {0,CHAR,CHAR,…, EOI};

  再回过头来分析 program,因此第 12 行和第 13 行用 kind[t]就是判断是否关键字开始,并判断记号 t 是否 ID,或者’*’,或者’(‘开始。如果是合法的记号开始,接着

 在第 16 行就会调用函数 decl 来分析声明。第 18 行到第 22 行是删除分配内存空间。

 第 24 行到第 28 行是取得记号 t 为分号,那这种情况是出错的,发出警告给软件开发人员,说明写了一行空行代码,接着获取下一个记号。

 29 3 处理错误的声明,因为不能处理这种记号开始的声明。

 第 36 行到第 39 行是当整个文件没有写一行代码的情况给出警告。

 这样就可以循环地分析所有源程序,把所有声明和语义都分析出来,并且在遇到函数的定义时,就会调用后端生成汇编代码。LCC 编译器是一遍编译器,当语法和语义没有错误的情况下,一次分析就会生成所有的代码。

 这样就开始了语法分析,下一步会调用函数 decl 来分析声明的开始,由于 C 程序是采用所有变量和函数都要先声明再使用的规则。

推荐访问:分析 源程序 编译器
上一篇:乡镇党建带关建亮点工作汇报
下一篇:心血管检查试题(第一第二节课)

Copyright @ 2013 - 2018 优秀啊教育网 All Rights Reserved

优秀啊教育网 版权所有