Compiler-建立符号表&错误处理-3

建立符号表和错误处理

类图:

说明:

  1. Compiler是程序的入口

  2. Morpho是词法分析器(ReadFile读入testfile.txt文件)

  3. SyntaxParser是语法分析器

  4. SymbolTable 是符号表类

  5. RParam 是函数实参表类

  6. Error 是错误处理类

  7. Node 是递归下降树构建的结点类

  8. Symbol 是符号类

  9. Token 是词牌类

  10. Tag 是First集类

符号表类

什么时候要创建符号表?

  1. CompUnit时创建一个rootTable,其prev上层符号表为 null

  2. FuncDef 函数定义时

  3. MainFuncDef 进入主函数int main时

  4. Block 扫描到{ ,且当前符号表的defendfalse

说明:

  1. table类型为HashMapkey存储Symbolnamevalue存储对应的Symbol

  2. prev 类型为SymbolTable ,上一层的符号表

  3. isFunc 用于判别此符号表是否为函数定义创建的表

  4. position 用于记录当前符号表在上一层符号表的环境位置,其值为这个符号表创建时,当前符号表curTabletable的size()

    (后面发现似乎并不需要这个属性,因为在错误处理的时候是递归过程中进行的)

  5. existReturn 用于判别当前符号表是函数时,是否存在return 语句。

  6. defend 只有在函数定义创建符号表的时候设为true ,当进入函数定义的Block中时,不必再创建一个符号表了,因为在扫描到函数名称的时候就创建过了,之后设置为false 意味着当前函数符号表已经进入到过其本身的Block中了

  7. funcNamefuncType 用于记录当前符号表(函数定义)的函数名称和函数类型.

符号类

什么时候加入符号?

  1. ConstDef 常量定义

  2. VarDef 变量定义

  3. FuncDef 函数定义,加入到当前符号表中,一般是rootTable

  4. FuncFParam 函数形参,加入到当前函数符号表中

什么时候查询符号?

  1. Stmt 中扫描到LVal 时,是否改变常量

  2. LVal 中查询符号(普通变量,一维数组,二维数组)的维度;

  3. UnaryExp 中查询符号(函数调用)的维度

上述2/3查询的维度最终加入当前函数的实参列表中,用于错误处理,判断函数调用实参个数和类型(维度)是否匹配

错误处理

  1. b类错误名字重定义:

    🧐下面这种也算错误?

    1
    2
    int f(int a) {}
    void f(int a) {}
  2. e类错误: 函数中f(RParam) a[Exp] 所以说

    所以存在a(LVal加入当前函数)[Exp→LVal(加入当前函数)]

    因此在解读LVal时不应该盲目地将当前的LVal加入当前的函数实参paramMap中,例如

    1
    2
    3
    4
    5
    6
    7
    int f(int x) {return 0;}
    int a[2][2] = {{1,2},{3,4}};
    int i = 0;
    f(a[1][2]);
    f(a(0)[(1)b(0)[(1)1(0)](-1)](-2)[(-1)i(0)](-1));
    f(a[f(a[i])][0]); //内层函数调用类型出错

    那是不是也意味着会有

    1
    2
    const int n = 0;
    a[f(n)];

    但是这种没要求检测类型不匹配的问题;要求检测函数类型不匹配。

    解决方法:每当进入'['中,就会有widthreal++,说明在[]内时widthreal不为0,则只有当widthreal为零的时候才加入curParam的map中

    上述解决方法不行,如果将widthReal设置成全局的,则嵌套调用时上一层的widthReal会丢失,所以想到的解决办法是,每当调用LVal的时候,就新建一个存储其...

    其实发现第一种解决方法也是可行的?:

    每次扫描到LVal的IDENFR就设置为0,扫描到[就++,扫描到]就减一,但是如果inArray = 0 时扫描到]不要减一了,要保证其永远大于等于0,因为a[b[1]]此处inArray = -1当a[b[1]][此处inArray为零,不正确了i]

    总结:很简单的思路,当前curParam不等于null,[时inArray加一,]时inArray减一,只有当 inArray = 0 时加入当前的curParam的实参列表中即可.(inArray为全局变量.)

    上面的还是不对....例如 f(a[1][sum(a[0])]) 在sum函数中的a[0]就加入不到sum的实参列表中了,所以在RParam中加一个私有参数inArray,而不是全局!

    测试用例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    int a[2][2] = {{1,2},{3,4}};
    int i = 0;
    int f(int x) {return 0;}
    int sum(int a) {return 0;}
    void h(){;}
    int main(){
    int v[3], i = 0;
    if( f(a[1][sum(a[0])])) {}; //e错误
    if( f(a[1][sum(a[0][1])])) {}; //正确
    f(h());//e错误
    f(a[i]);//e错误
    f(a[i][i+1]);//正确
    return 0;
    }
  3. 注意在构造测试用例的时候:Decl和FuncDef是先后顺序。

  4. 对最终输出排序输出用TreeMap

    1
    2
    TreeMap sotrdMap = new TreeMap<>(errMap);

  5. 对于循环嵌套,多个while需要判断是否是错误的。