项目的完整代码在 C2j-Compiler

前言

有关符号表的文件都在symboltable包里

前面我们通过完成一个LALR(1)有限状态自动机和一个reduce信息来构建了一个语法解析表,正式完成了C语言的语法解析。接下来就是进入语义分析部分,和在第二篇提到的一样,语义分析的主要任务就是生成符号表来记录变量和变量的类型,并且发现不符合语义的语句

描述变量

在C语言里对变量声明定义里,主要有两种描述

  • 说明符(Specifier)

    说明符也就是对应C语言的一些描述变量类型或者像static,extern的关键字(像extern这些关键词在这次实现的编译器里并没有用到,因为extern可能还要涉及到多个源文件的编译和链接)

  • 修饰符(Declarator)

    修饰符则是由变量名或者代表指针类型的星号,数组的中括号组成,修饰符属于可以复杂的一部分,因为修饰符可以进行组合。所以对于组合的修饰符就可以创建多个Declarator,按顺序链接起来

这样就可以完成两个类,这两个类的逻辑都比较简单:

Declarator类

  • declareType:用来表示当前的Declarator是一个指针还是数组或者函数
  • numberOfElements、elements:如果当前的类型是个数组的话,它们就表示数组的元素个数和数组元素
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类

Specifier的属性会比较多一点,但是在后面编译器可能只支持int, char, void, struct四种类型

  • 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;
}

描述符号表

在前面定义两个描述变量的类,但是仅靠这两个类还是无法准确的表达一个符号,所以我们需要包装一下这两个类,让它更具表达力

编程很多时候都是根据特定的需求完成特定的数据结构,符号表在计算机里本质上也只是用来描述变量的数据结构而已

这个数据结构作为符号表有几个基本的条件:

  1. 速度
    因为符号表需要频繁的插入和查找,所以查询和插入速度必须要足够的快
  2. 灵活
    因为变量的定义的可能会很复杂,比如说多个修饰符再加上指针((long int, long doube *),所以在设计上必须足够灵活

因为学习编译器一直是跟着陈老师的课,所以符号表的设计也沿用老师的设计

为了保证上面两个条件,我们选用链式哈希表来实现

这张图是我网上找的,实际上没有那么复杂

所有的变量都存储到这个哈希表中,同名变量被哈希会被同一个地方,当然它们要属于不同作用域,而区分不同作用域就在于这张图上面一部分,它会把同一个作用域的变量连接起来

symboltable.Symbol

这个类用来描述符号表里的一个符号

如果从github下载源文件的话,里面有许多是在后面代码生成才需要用到的,现在可以忽略

主要属性有:

  • level: 用来表明变量的层次
  • duplicate:是否是一个同名变量
  • args:如果该符号对应的是函数名,那么args指向函数的输入参数符号列表
  • next: 指向下一个同层次的变量符号
public class Symbol {String name;String rname;int level; boolean duplicate; Symbol args; Symbol next;
}

这时候用Symbol加上之前的Specifier和Declarator就有足够的表达力来描述一个符号,那么就需要把这三个类联系起来,先增加一个TypeLink

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这个文件还没讲过,这个文件是用来描述结构体的,因为结构体本身的复杂性,所以就需要对它进行特殊处理,但是结构体本质上还是一堆变量的组合,所以依旧可以用上面的方法描述

  • tag: 结构体的名称
  • level: 结构体的嵌套层次
  • Symbol:对应结构体里的变量
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,所以这样的结构就相当于开头描述的那个哈希表

这一节主要是描述了符号表的数据结构,两个关键点是

  1. 描述变量

    所以定义了修饰符和描述符来描述一个变量

  2. 关联变量

    定义了Symbol链表来串联各个变量

另外我的github博客:https://dejavudwh.cn/

转载于:https://www.cnblogs.com/secoding/p/11373929.html

从零写一个编译器(七):语义分析之符号表的数据结构相关推荐

  1. 从零写一个编译器(完结):总结和系列索引

    前言 这个系列算作我自己的学习笔记,到现在已经有十三篇了,加上这篇一共十四篇.一步一步的从词法分析到语法分析.语义分析,再到代码生成,准备在这一篇做一个总结收尾和一个这个系列以前文章的索引. (另外, ...

  2. 从零写一个编译器(八):语义分析之构造符号表

    项目的完整代码在 C2j-Compiler 前言 在之前完成了描述符号表的数据结构,现在就可以正式构造符号表了.符号表的创建自然是要根据语法分析过程中走的,所以符号表的创建就在LRStateTable ...

  3. 从零写一个编译器(一):输入系统和词法分析

    项目的完整代码在 C2j-Compiler 前言 从半抄半改的完成一个把C语言编译到Java字节码到现在也有些时间,一直想写一个系列来回顾整理一下写一个编译器的过程,也算是学习笔记吧.就从今天开始动笔 ...

  4. 从零写一个编译器(三):语法分析之几个基础数据结构

    项目的完整代码在 C2j-Compiler 写在前面 这个系列算作为我自己在学习写一个编译器的过程的一些记录,算法之类的都没有记录原理性的东西,想知道原理的在龙书里都写得非常清楚,但是我自己一开始是不 ...

  5. 从零写一个编译器(九):语义分析之构造抽象语法树(AST)

    项目的完整代码在 C2j-Compiler 前言 在上一篇完成了符号表的构建,下一步就是输出抽象语法树(Abstract Syntax Tree,AST) 抽象语法树(abstract syntax ...

  6. 从零写一个编译器(十):编译前传之直接解释执行

    项目的完整代码在 C2j-Compiler 前言 这一篇不看也不会影响后面代码生成部分 现在经过词法分析语法分析语义分析,终于可以进入最核心的部分了.前面那部分可以称作编译器的前端,代码生成代码优化都 ...

  7. 从零写一个编译器(十三):代码生成之遍历AST

    项目的完整代码在 C2j-Compiler 前言 在上一篇完成对JVM指令的生成,下面就可以真正进入代码生成部分了.通常现代编译器都是先把生成IR,再经过代码优化等等,最后才编译成目标平台代码.但是时 ...

  8. 从零写一个编译器(六):语法分析之表驱动语法分析

    项目的完整代码在 C2j-Compiler 前言 上一篇已经正式的完成了有限状态自动机的构建和足够判断reduce的信息,接下来的任务就是根据这个有限状态自动机来完成语法分析表和根据这个表来实现语法分 ...

  9. 从零写一个编译器(五):语法分析之自动机的缺陷和改进

    项目的完整代码在 C2j-Compiler 前言 在上一篇,已经成功的构建了有限状态自动机,但是这个自动机还存在两个问题: 无法处理shift/reduce矛盾 状态节点太多,导致自动机过大,效率较低 ...

最新文章

  1. Android TextView 在strings 里面 实现换行
  2. 大型网站系统架构分析--转
  3. 澎思科技马原:AI为基,IoT为翼,加速人工智能普惠丨MEET2021
  4. Vue -渐进式JavaScript框架
  5. day5 Java中的方法与重载
  6. 向前logistic回归与向后筛选出一样的变量_什么泊松分布?泊松回归又能做什么?...
  7. 昨晚学妹参加了B站秋招笔试,还想考考我?
  8. 打印九九乘法表算法-java
  9. 关于网页数据导入excel
  10. 盘点含金量高的几种编程比赛
  11. 什么叫大数据人物画像_【说大数据】人物画像 :大数据时代的心灵捕手
  12. 微信小程序使用canvas绘图
  13. 前端ajax实现分页思路详解
  14. 深度解析C语言文件操作以及常见问题
  15. java进行参数快捷校验,ehi-verification
  16. Qt应用程序缺少DLL
  17. 单招计算机专业考几科,高职单招一般都考什么科目
  18. Sublime配置java运行环境(IntelliJ IDEA也许更好用)
  19. 侠探锦毛鼠之真假白玉堂
  20. TestNG框架自动化简述

热门文章

  1. Spring+SpringMVC+MyBatis深入学习及搭建(二)——MyBatis原始Dao开发和mapper代理开发
  2. sublime text 2中Emmet8个常用的技巧
  3. ios-UIButton-常用方法
  4. Exception in thread main java.lang.NoClassDefFoundError: org/apache/tez/dag/api/TezConfiguration
  5. hive与hbase的以及mongodb和cassandra区别整理
  6. saleor设置braintree支付方式
  7. yelee主题修改博客左侧文章目录的按钮字体
  8. 1.11 多于空间维度的向量组
  9. DLNA, PulseAudio, diffpatch, bash
  10. sql 查出一张表中重复的所有记录数据