LCC编译器的源程序分析(11)声明与符号表

前一次已经分析了声明的函数,但还有一个声明函数没有分析的,它就是 dclr 函数,这个函数是大内总管,分别调用前面两个声明函数来处理所有的声明语句,接着又会保存声明的 ID 和属性到符号表,当然它需要调用处理函数定义的函数,接着在那里把函数生成汇编代码并写到输出文件里。
现在就来看代码:
#001 static void decl(Symbol (*dcl)(int, char *, Type, Coordinate *))
#002 {
#003  int sclass;
#004  Type ty, ty1;
#005  static char stop[] = { CHAR, STATIC, ID, 0 };
#006 
#007  //
#008  ty = specifier(&sclass);
#009 
3 行定义保存存储类型的局部变量。
5 行是定义语法出错时停止的条件。
8 行是调用函数 specifier 进行语法的声明处理,前面已经分析这个函数的实现,如果还搞不懂,就需要回过头去看它分析。
 
#010  if (t == ID || t == '*' || t == '(' || t == '[')
#011  {
#012         char *id;
#013         Coordinate pos;
#014         id = NULL;
#015         pos = src;
#016 
#017         if (level == GLOBAL)
#018         {
#019               Symbol *params = NULL;
#020               ty1 = dclr(ty, &id, &params, 0);
#021 
在声明分析之后,也就是识别了说明符之后,记号开始一定是 ID '*' '(' '[' 等几种类型了。如果不是这样的记号,说明语法出错。
10 行就是处理这种情况。
15 行保存源程序分析的位置。
17 行根据当前作用域来分别处理,由于局部声明的作用域是没有参数处理的。
20 行是调声明处理函数 dclr 来分析全局变量和函数的声明。
 
#022               // 判断是否函数定义开始。
#023               if (params && id && isfunc(ty1) &&
#024                    (t == '{' || istypename(t, tsym) ||
#025                    (kind[t] == STATIC && t != TYPEDEF)))
#026               {
#027                    if (sclass == TYPEDEF)
#028                    {
#029                          error("invalid use of `typedef'/n");
#030                          sclass = EXTERN;
#031                    }
#032 
#033                    if (ty1->u.f.oldstyle)
#034                    {
#035                          exitscope();
#036                    }   
#037 
#038                    // 函数定义 , 开始生成代码。
#039                    funcdefn(sclass, id, ty1, params, pos);
#040 
#041                    return;
23 行判断是否函数的声明,如果是函数的声明,就再进一步判断是否函数定义的复合语句。如果有函数定义,在第 39 行开始处理函数定义,并生成汇编代码。
 
#042               }
#043               else if (params)
#044               {
#045                    exitparams(params);
#046               }   
#047         }
#048         else
#049         {
#050               ty1 = dclr(ty, &id, NULL, 0);
#051         }   
#052 
43 行到第 46 行处理参数列表,退出参数的作用域。
 
 
#053 
#054         for (;;)
#055         {
#056               if (Aflag >= 1 && !hasproto(ty1))
#057                    warning("missing prototype/n");
#058              
#059               if (id == NULL)
#060               {
#061                    error("missing identifier/n");
#062               }   
#063               else if (sclass == TYPEDEF)
#064               {
#065                    Symbol p = lookup(id, identifiers);
#066                    if (p && p->scope == level)
#067                          error("redeclaration of `%s'/n", id);
#068 
#069                    p = install(id, &identifiers, level,
#070                          level < LOCAL ? PERM : FUNC);
#071                   
#072                    p->type = ty1;
#073                    p->sclass = TYPEDEF;
#074                    p->src = pos;
#075               }
#076               else
#077               {
#078                    (void)(*dcl)(sclass, id, ty1, &pos);
#079               }   
#080 
#081               if (t != ',')
#082                    break;
#083 
#084               t = gettok();
#085               id = NULL;
#086               pos = src;
#087               ty1 = dclr(ty, &id, NULL, 0);
#088         }
54 行到第 88 行是处理声明变量。
54 行的 for 循环是用来处理声明的变量是逗号表达式时并列处理。
59 行是处理声明没有 ID 的出错情况。
63 行到第 75 行是处理 typedef 定义的类型声明。
65 行从符号表 identifiers 里查找这个 ID 是否已经声明了,如果有声明过并且作用域一样,就表示重复声明了同一个 ID 。它是在第 66 行到第 67 行里处理的。
69 行是保存这个 ID 到符号表 identifiers ,同时在第 72 行保存声明的类型,在第 73 行保存存储的类型,在第 74 行保存源程序位置。
78 行是调用全局函数 dclglobal ,或者局部函数 dcllocal ,或者参数函数 dclparam 来处理变量 ID
 
#089  }
#090  else if (ty == NULL ||
#091         !(isenum(ty) ||
#092         isstruct(ty) && (*unqual(ty)->u.sym->name < '1' ||
#093         *unqual(ty)->u.sym->name > '9')))
#094  {
#095         error("empty declaration/n");
#096  }   
#097 
#098  test(';', stop);
#099 }
90 行到第 96 行都是处理出错的情况。
98 行是测试当前的记号是否语句结束符,如果不是,就会跳过错误直到下一句语句再进行分析处理。
通过上面函数的分析,像语句( typedef unsigned int size_t; )已经处理完成,并保存到 identifiers 符号表里,以便后面使用 size_t 类型来定义新变量时查找类型的属性。所有再遇到像 typedef 的声明,都是这样处理的,通过这个例子来分析 C 编译器的源程序,就会简单很多。有实际的例子并同步跟踪源程序运行的路径,其实就是流程图的表现。像这种简单语法分析,是没有太多递归调用和复杂的处理,后面会跟着例子里其它语句再深入体会 C 编译器是怎么处理它们的,同时明确地理解源程序的作用。
 
 

你可能感兴趣的:(c,汇编,测试,null,存储,编译器)