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编译器是怎么处理它们的,同时明确地理解源程序的作用。
 
 
发布了2060 篇原创文章 · 获赞 574 · 访问量 767万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览