项目的完整代码在 C2j-Compilerjava
有关符号表的文件都在symboltable包里git
前面咱们经过完成一个LALR(1)有限状态自动机和一个reduce信息来构建了一个语法解析表,正式完成了C语言的语法解析。接下来就是进入语义分析部分,和在第二篇提到的同样,语义分析的主要任务就是生成符号表来记录变量和变量的类型,而且发现不符合语义的语句github
在C语言里对变量声明定义里,主要有两种描述编程
说明符(Specifier)数组
说明符也就是对应C语言的一些描述变量类型或者像static,extern的关键字(像extern这些关键词在此次实现的编译器里并无用到,由于extern可能还要涉及到多个源文件的编译和连接)修饰符(Declarator)数据结构
修饰符则是由变量名或者表明指针类型的星号,数组的中括号组成,修饰符属于能够复杂的一部分,由于修饰符能够进行组合。因此对于组合的修饰符就能够建立多个Declarator,按顺序连接起来函数
这样就能够完成两个类,这两个类的逻辑都比较简单:学习
public class Declarator { public static int POINTER = 0; public static int ARRAY = 1; public static int FUNCTION = 2; private int declareType; private int numberOfElements = 0; HashMap<Integer, Object> elements = null; public Declarator(int type) { this.declareType = type; } ... }
Specifier的属性会比较多一点,可是在后面编译器可能只支持int, char, void, struct四种类型this
basicType:用来代表当前变量的类型设计
storageClass:表示变量的存储方式(fixed,auto),这里咱们把typedef的信息也放在这里,也就是说若是碰见typedef,那么storageClass会被设置为TYPEDEF
constantValue和vStruct:这两个属于比较特殊的两个属性,它们表示枚举类型和结构体,之因此特殊是由于它们以后要进行特殊处理。若是碰见枚举类型至关于构造一个basicType是CONSTANT的Specifier,对应的值也就是constantValue了
public class Specifier { /** * Variable types */ public static int NONE = -1; public static int INT = 0; public static int CHAR = 1; public static int VOID = 2; public static int STRUCTURE = 3; public static int LABEL = 4; /** * storage */ public static int FIXED = 0; public static int REGISTER = 1; public static int AUTO = 2; public static int TYPEDEF = 3; public static int CONSTANT = 4; public static int NO_OCLASS = 0; public static int PUBLIC = 1; public static int PRIVATE = 2; public static int EXTERN = 3; public static int COMMON = 4; private int basicType; private int storageClass; private int outputClass = NO_OCLASS; private boolean isLong = false; private boolean isSigned = false; private boolean isStatic = false; private boolean isExternal = false; private int constantValue = 0; private StructDefine vStruct = null; }
在前面定义两个描述变量的类,可是仅靠这两个类仍是没法准确的表达一个符号,因此咱们须要包装一下这两个类,让它更具表达力
编程不少时候都是根据特定的需求完成特定的数据结构,符号表在计算机里本质上也只是用来描述变量的数据结构而已
这个数据结构做为符号表有几个基本的条件:
由于学习编译器一直是跟着陈老师的课,因此符号表的设计也沿用老师的设计
为了保证上面两个条件,咱们选用链式哈希表来实现
这张图是我网上找的,实际上没有那么复杂
全部的变量都存储到这个哈希表中,同名变量被哈希会被同一个地方,固然它们要属于不一样做用域,而区分不一样做用域就在于这张图上面一部分,它会把同一个做用域的变量链接起来
这个类用来描述符号表里的一个符号
若是从github下载源文件的话,里面有许可能是在后面代码生成才须要用到的,如今能够忽略
主要属性有:
public class Symbol { String name; String rname; int level; boolean duplicate; Symbol args; Symbol next; }
这时候用Symbol加上以前的Specifier和Declarator就有足够的表达力来描述一个符号,那么就须要把这三个类联系起来,先增长一个TypeLink
TypeLink表示一个Specifier或者一个Declarator,这里用继承来实现可能会显得更好看一点
public class TypeLink { public boolean isDeclarator; /** * typedef int */ public boolean isTypeDef; /** * Specifier or Declarator */ public Object typeObject; private TypeLink next = null; public TypeLink(boolean isDeclarator, boolean typeDef, Object typeObj) { this.isDeclarator = isDeclarator; this.isTypeDef = typeDef; this.typeObject = typeObj; } public Object getTypeObject() { return typeObject; } public TypeLink toNext() { return next; } public void setNextLink(TypeLink obj) { this.next = obj; } }
这样在Symbol里就要加入两个属性
typeLinkBegin和typeLinkEnd就是用来描述变量的说明符和修饰符的整个链表,也就是以前说的把这些修饰符或者说明符按顺序链接起来
public class Symbol { String name; String rname; int level; boolean implicit; boolean duplicate; Symbol args; Symbol next; TypeLink typeLinkBegin; TypeLink typeLinkEnd; }
这样完成以后,例如
long int (*e)[10];
就能够这样表示
Symbol | declartor | declartor | specifer |
---|---|---|---|
name:e | declareType = PONITER | declareType = array | basicType = INT isLong = TRUE |
-> | -> | -> | -> |
StructDefine这个文件还没讲过,这个文件是用来描述结构体的,由于结构体自己的复杂性,因此就须要对它进行特殊处理,可是结构体本质上仍是一堆变量的组合,因此依旧能够用上面的方法描述
public class StructDefine { private String tag; private int level; private Symbol fields; public StructDefine(String tag, int level, Symbol fields) { this.tag = tag; this.level = level; this.fields = fields; } }
看一个结构体定义的例子
struct dejavidwh { int array1[5]; struct dejavudwh *pointer1; } one;
因此最后只须要
private HashMap<String, ArrayList<Symbol>> symbolTable = new HashMap<>(); private HashMap<String, StructDefine> structTable = new HashMap<>();
就能够描述一个符号表
symbolTable里的key至关于变量的名字,然后面的ArrayList存放着同名变量,由于每一个Symbol都有一个next指针来指向同级的其它Symbol,因此这样的结构就至关于开头描述的那个哈希表
这一节主要是描述了符号表的数据结构,两个关键点是
描述变量
因此定义了修饰符和描述符来描述一个变量
关联变量
定义了Symbol链表来串联各个变量
另外个人github博客:https://dejavudwh.cn/