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

来源:互联网 发布:js gmt转北京时间 编辑:程序博客网 时间:2024/06/06 02:52
准备好词法分析之后,接着的工作就是检查源程序是否合法,以及源程序表达的意思是什么。这两个问题就是语法和语义的分析,也就是把源程序里所包含的属性分析出来,并保存到符号表里。下面就来仔细地分析LCC编译器是怎么样处理这两个问题的。
#001 t = gettok();
#002  
#003  //调用后端代码生成初始化工作。
#004  (*IR->progbeg)(argc, argv);
#005 
#006  for (i = 1; i < argc; i++)
#007  {
#008         if (strcmp(argv[i], "-n") == 0)
#009         {
#010               if (!YYnull)
#011               {
#012                    YYnull = install(string("_YYnull"), &globals, GLOBAL, PERM);
#013                    YYnull->type = func(voidptype, NULL, 1);
#014                    YYnull->sclass = EXTERN;
#015                    (*IR->defsymbol)(YYnull);
#016               }
#017 
#018         }
#019         else if (strncmp(argv[i], "-n", 2) == 0)
#020         {    /* -nvalid[,check] */
#021               char *p = strchr(argv[i], ',');              
#022               if (p)
#023               {
#024                    YYcheck = install(string(p+1), &globals, GLOBAL, PERM);
#025                    YYcheck->type = func(voidptype, NULL, 1);
#026                    YYcheck->sclass = EXTERN;
#027                    (*IR->defsymbol)(YYcheck);
#028                    p = stringn(argv[i]+2, p - (argv[i]+2));
#029               }
#030               else
#031               {
#032                    p = string(argv[i]+2);
#033               }   
#034 
#035               YYnull = install(p, &globals, GLOBAL, PERM);
#036               YYnull->type = func(voidptype, NULL, 1);
#037               YYnull->sclass = EXTERN;
#038               (*IR->defsymbol)(YYnull);
#039 
#040         }
#041         else
#042         {
#043               profInit(argv[i]);
#044               traceInit(argv[i]);
#045         }
#046  }
#047 
#048  if (glevel && IR->stabinit)
#049  {
#050         (*IR->stabinit)(firstfile, argc, argv);
#051  }   
#052 
#053  //开始整个程序处理。
#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 于深圳
//
#001 //程序开始分析。
#002 void program(void)
#003 {
#004  int n;
#005 
#006  //作用域为全局。
#007  level = GLOBAL;
#008 
#009  //分析源程序到文件尾。
#010  for (n = 0; t != EOI; n++)
#011  {
#012         if (kind[t] == CHAR || kind[t] == STATIC
#013               || t == ID || t == '*' || t == '(')
#014         {
#015               //声明开始.
#016               decl(dclglobal);
#017 
#018               deallocate(STMT);
#019               if (!(glevel >= 3 || xref))
#020               {
#021                    deallocate(FUNC);
#022               }   
#023         }
#024         else if (t == ';')
#025         {
#026               warning("empty declaration/n");
#027               t = gettok();
#028         }
#029         else
#030         {
#031               error("unrecognized declaration/n");
#032               t = gettok();
#033         }
#034  }
#035 
#036  if (n == 0)
#037  {
#038         warning("empty input file/n");
#039  }   
#040 }
函数开始的第7行设置作用域为全局作用域GLOBAL。比如分析例子里的代码,在main函数以外的代码,都是全局作用域的。
10行使用一个for循环,它的终止条件是分析源程序文件到结束,第11行到第34行里都是分析源程序的代码。
12行和第13行就判断开始的记号是否合法的。先看一下,就发现有一个数组kind[t],并且用它来判断记号的合法性。这个数组的定义如下:
char kind[] = {
#define xx(a,b,c,d,e,f,g) f,
#define yy(a,b,c,d,e,f,g) f,
#include "token.h"
};
上面定义了两个宏,然后包含了头文件token.h,因为所有宏定义解释中在头文件里,如下:
#001 /*
#002 xx(symbol,   value,   prec,    op, optree, kind,    string)
#003 */
#004 yy(0,         0, 0, 0,    0,      0,      0)
#005 xx(FLOAT,     1, 0, 0,    0,      CHAR,   "float")
#006 xx(DOUBLE,    2, 0, 0,    0,      CHAR,   "double")
#007 xx(CHAR,      3, 0, 0,    0,      CHAR,   "char")
#008 xx(SHORT,     4, 0, 0,    0,      CHAR,   "short")
#009 xx(INT,       5, 0, 0,    0,      CHAR,   "int")
#010 xx(UNSIGNED, 6, 0, 0,    0,      CHAR,   "unsigned")
#011 xx(POINTER,   7, 0, 0,    0,      0,      "pointer")
#012 xx(VOID,      8, 0, 0,    0,      CHAR,   "void")
#013 xx(STRUCT,    9, 0, 0,    0,      CHAR,   "struct")
#014 xx(UNION,    10, 0, 0,    0,      CHAR,   "union")
#015 xx(FUNCTION, 11, 0, 0,    0,      0,      "function")
#016 xx(ARRAY,    12, 0, 0,    0,      0,      "array")
#017 xx(ENUM,     13, 0, 0,    0,      CHAR,   "enum")
#018 xx(LONG,     14, 0, 0,    0,      CHAR,   "long")
#019 xx(CONST,    15, 0, 0,    0,      CHAR,   "const")
#020 xx(VOLATILE, 16, 0, 0,    0,      CHAR,   "volatile")
#021 yy(0,        17,     0,      0,    0,      0,      0)
#022 yy(0,        18,     0,      0,    0,      0,      0)
#023 yy(0,        19,     0,     0,    0,      0,      0)
#024 yy(0,        20,     0,      0,    0,      0,      0)
#025 yy(0,        21,     0,      0,    0,      0,      0)
#026 yy(0,        22,     0,      0,    0,      0,      0)
#027 yy(0,        23,     0,      0,    0,      0,      0)
#028 yy(0,        24,     0,      0,    0,      0,      0)
#029 yy(0,        25,     0,      0,    0,      0,      0)
#030 yy(0,        26,     0,      0,    0,      0,      0)
#031 yy(0,        27,     0,      0,    0,      0,      0)
#032 yy(0,        28,     0,      0,    0,      0,      "long long")
#033 yy(0,        29,     0,      0,    0,      0,      0)
#034 yy(0,        30,     0,      0,    0,      0,      0)
#035 yy(0,        31,     0,      0,    0,      0,      "const volatile")
#036 xx(ID,       32,     0,      0,    0,      ID,     "identifier")
#037 yy(0,        33,     0,      0,    0,      ID,     "!")
#038 xx(FCON,     34,     0,      0,    0,      ID,     "floating constant")
#039 xx(ICON,     35,     0,      0,   0,      ID,     "integer constant")
#040 xx(SCON,     36,     0,      0,    0,      ID,     "string constant")
#041 yy(0,        37,     13,     MOD, bittree,'%',    "%")
#042 yy(0,        38,     8,      BAND, bittree,ID,     "&")
#043 xx(INCR,     39,     0,      ADD, addtree,ID,     "++")
#044 yy(0,        40,     0,      0,    0,      ID,     "(")
#045 yy(0,        41,     0,      0,    0,      ')',    ")")
#046 yy(0,        42, 13, MUL, multree,ID,     "*")
#047 yy(0,        43, 12, ADD, addtree,ID,     "+")
#048 yy(0,        44, 1, 0,    0,      ',',    ",")
#049 yy(0,        45, 12, SUB, subtree,ID,     "-")
#050 yy(0,        46, 0, 0,    0,      '.',    ".")
#051 yy(0,        47, 13, DIV, multree,'/',    "/")
#052 xx(DECR,     48, 0, SUB, subtree,ID,     "--")
#053 xx(DEREF,    49, 0, 0,    0,      DEREF, "->")
#054 xx(ANDAND,   50, 5, AND, andtree,ANDAND, "&&")
#055 xx(OROR,     51, 4, OR,   andtree,OROR,   "||")
#056 xx(LEQ,      52, 10, LE,   cmptree,LEQ,    "<=")
#057 xx(EQL,         53,     9,      EQ,     eqtree, EQL,    "==")
#058 xx(NEQ,         54,     9,      NE,     eqtree, NEQ,    "!=")
#059 xx(GEQ,         55,     10,     GE,     cmptree,GEQ,    ">=")
#060 xx(RSHIFT,      56,     11,     RSH,    shtree, RSHIFT, ">>")
#061 xx(LSHIFT,      57,     11,     LSH,    shtree, LSHIFT, "<<")
#062 yy(0,           58,     0,      0,      0,      ':',    ":")
#063 yy(0,           59,     0,      0,      0,      IF,     ";")
#064 yy(0,           60,     10,     LT,     cmptree,'<',    "<")
#065 yy(0,           61,     2,      ASGN,   asgntree,'=',   "=")
#066 yy(0,           62,     10,     GT,     cmptree,'>',    ">")
#067 yy(0,           63,     0,      0,      0,      '?',    "?")
#068 xx(ELLIPSIS,    64,     0,      0,      0,      ELLIPSIS,"...")
#069 xx(SIZEOF,      65,     0,      0,      0,      ID,     "sizeof")
#070 yy(0,           66,     0,      0,      0,      0,      0)
#071 xx(AUTO,        67,     0,      0,      0,      STATIC, "auto")
#072  xx(BREAK,       68,     0,      0,      0,      IF,     "break")
#073 xx(CASE,        69,     0,      0,      0,      IF,     "case")
#074 xx(CONTINUE,    70,     0,      0,      0,      IF,     "continue")
#075 xx(DEFAULT,     71,     0,      0,      0,      IF,     "default")
#076 xx(DO,          72,     0,      0,      0,      IF,     "do")
#077 xx(ELSE,        73,     0,      0,      0,      IF,     "else")
#078 xx(EXTERN,      74,     0,      0,      0,      STATIC, "extern")
#079 xx(FOR,         75,     0,      0,      0,      IF,     "for")
#080 xx(GOTO,        76,     0,      0,      0,      IF,     "goto")
#081 xx(IF,          77,     0,      0,      0,      IF,     "if")
#082 xx(REGISTER,    78,     0,      0,      0,      STATIC, "register")
#083 xx(RETURN,      79,     0,      0,      0,      IF,     "return")
#084 xx(SIGNED,      80,     0,      0,      0,      CHAR,   "signed")
#085 xx(STATIC,      81,     0,      0,      0,      STATIC, "static")
#086 xx(SWITCH,      82,     0,     0,      0,      IF,     "switch")
#087 xx(TYPEDEF,     83,     0,      0,      0,      STATIC, "typedef")
#088 xx(WHILE,       84,     0,      0,      0,      IF,     "while")
#089 xx(TYPECODE,    85,     0,      0,      0,      ID,     "__typecode")
#090 xx(FIRSTARG,    86,     0,      0,      0,      ID,     "__firstarg")
#091 yy(0,           87,     0,      0,      0,      0,      0)
#092 yy(0,           88,     0,      0,      0,      0,      0)
#093 yy(0,           89,     0,      0,      0,      0,      0)
#094 yy(0,           90,     0,      0,      0,      0,      0)
#095 yy(0,           91,     0,      0,      0,      '[',    "[")
#096 yy(0,           92,     0,      0,      0,      0,      0)
#097 yy(0,           93,     0,      0,     0,      ']',    "]")
#098 yy(0,           94,     7,      BXOR,   bittree,'^',    "^")
#099 yy(0,           95,     0,      0,      0,      0,      0)
#100 yy(0,           96,     0,      0,      0,      0,      0)
#101 yy(0,           97,     0,     0,      0,      0,      0)
#102 yy(0,           98,     0,      0,      0,      0,      0)
#103 yy(0,           99,     0,      0,      0,      0,      0)
#104 yy(0,           100,    0,      0,      0,      0,      0)
#105 yy(0,           101,    0,      0,      0,      0,      0)
#106 yy(0,           102,    0,      0,      0,      0,      0)
#107 yy(0,           103,    0,      0,      0,      0,      0)
#108 yy(0,           104,    0,      0,      0,      0,      0)
#109 yy(0,           105,    0,      0,      0,      0,      0)
#110 yy(0,           106,    0,      0,      0,      0,      0)
#111 yy(0,           107,    0,      0,      0,      0,      0)
#112 yy(0,           108,    0,      0,      0,      0,      0)
#113 yy(0,           109,    0,      0,      0,      0,      0)
#114 yy(0,           110,    0,      0,      0,      0,      0)
#115 yy(0,           111,    0,      0,      0,      0,      0)
#116 yy(0,           112,    0,      0,      0,      0,      0)
#117 yy(0,           113,    0,      0,      0,      0,      0)
#118 yy(0,           114,    0,      0,      0,      0,      0)
#119 yy(0,           115,    0,      0,      0,      0,      0)
#120 yy(0,           116,    0,      0,      0,      0,      0)
#121 yy(0,           117,    0,      0,      0,      0,      0)
#122 yy(0,           118,    0,      0,      0,      0,      0)
#123 yy(0,           119,    0,      0,      0,      0,      0)
#124 yy(0,           120,    0,      0,      0,      0,      0)
#125 yy(0,           121,    0,      0,      0,      0,      0)
#126 yy(0,           122,    0,      0,      0,      0,      0)
#127 yy(0,           123,    0,      0,      0,      IF,     "{")
#128 yy(0,           124,    6,      BOR,    bittree,'|',    "|")
#129 yy(0,           125,    0,      0,      0,      '}',    "}")
#130 yy(0,           126,    0,      BCOM,   0,      ID,     "~")
#131 xx(EOI,         127,    0,      0,      0,      EOI,    "end of input")
#132 #undef xx
#133 #undef yy
上面的构造了一个表格,这样可以灵活地构造任何的对应关系的表格。
因此生成的kind[]数组如下:
kind[] {0CHAR,CHAR,…, EOI};
 
再回过头来分析program,因此第12行和第13行用kind[t]就是判断是否关键字开始,并判断记号t是否ID,或者’*’,或者’(‘开始。如果是合法的记号开始,接着在第16行就会调用函数decl来分析声明。第18行到第22行是删除分配内存空间。
24行到第28行是取得记号t为分号,那这种情况是出错的,发出警告给软件开发人员,说明写了一行空行代码,接着获取下一个记号。
29行到第33行是处理错误的声明,因为不能处理这种记号开始的声明。
36行到第39行是当整个文件没有写一行代码的情况给出警告。
这样就可以循环地分析所有源程序,把所有声明和语义都分析出来,并且在遇到函数的定义时,就会调用后端生成汇编代码。LCC编译器是一遍编译器,当语法和语义没有错误的情况下,一次分析就会生成所有的代码。
这样就开始了语法分析,下一步会调用函数decl来分析声明的开始,由于C程序是采用所有变量和函数都要先声明再使用的规则。
 
原创粉丝点击