⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 parser.java

📁 完成编译器的基本功能,并且有界面,简单好用
💻 JAVA
📖 第 1 页 / 共 3 页
字号:
package compiler.pl0;

/**
 *  语法分析器。这是PL/0分析器中最重要的部分,在语法分析的过程中穿插着语法错误检查和目标代码生成。
 */
public class Parser {
      private Scanner lex; // 对词法分析器的引用
      private Table table; // 对符号表的引用
      private Interpreter interp; // 对目标代码生成器的引用
      String a = "";
      private final int symnum = Symbol.values().length;
      Err err = new Err();
      String ss = "";

      // 表示声明开始的符号集合、表示语句开始的符号集合、表示因子开始的符号集合
      // 实际上这就是声明、语句和因子的FIRST集合
      private SymSet declbegsys, statbegsys, facbegsys;

      /**
       * 当前符号,由nextsym()读入
       * @see #nextSym()
       */
      private Symbol sym;

      /**
       * 当前作用域的堆栈帧大小,或者说数据大小(data size)
       */
      private int dx = 0;

      /**
       * 构造并初始化语法分析器,这里包含了C语言版本中init()函数的一部分代码
       * @param l 编译器的词法分析器
       * @param t 编译器的符号表
       * @param i 编译器的目标代码生成器
       */
      public Parser(Scanner l, Table t, Interpreter i) {
            lex = l;
            table = t;
            interp = i;

            // 设置声明开始符号集
            declbegsys = new SymSet(symnum);
            declbegsys.set(Symbol.constsym);
            declbegsys.set(Symbol.varsym);
            declbegsys.set(Symbol.procsym);

            // 设置语句开始符号集
            statbegsys = new SymSet(symnum);
            statbegsys.set(Symbol.beginsym);
            statbegsys.set(Symbol.callsym);
            statbegsys.set(Symbol.ifsym);
            statbegsys.set(Symbol.whilesym);

            // 设置因子开始符号集
            facbegsys = new SymSet(symnum);
            facbegsys.set(Symbol.ident);
            facbegsys.set(Symbol.number);
            facbegsys.set(Symbol.lparen);

      }

      /**
       * 启动语法分析过程,此前必须先调用一次nextsym()
       * @see #nextSym()
       */
      public void parse() {
            SymSet nxtlev = new SymSet(symnum);
            nxtlev.or(declbegsys);
            nxtlev.or(statbegsys);
            nxtlev.set(Symbol.period);
            parseBlock(0, nxtlev);

            if (sym != Symbol.period) {
                  String ss1 = err.report(9);
                  ss = ss + ss1 + "\n";
            }
      }

      /**
       * 获得下一个语法符号,这里只是简单调用一下getsym()
       */
      public void nextSym() {
            lex.getsym();
            sym = lex.sym;
      }

      /**
       * 测试当前符号是否合法
       *
       * @param s1 我们需要的符号
       * @param s2 如果不是我们需要的,则需要一个补救用的集合
       * @param errcode 错误号
       */
      void test(SymSet s1, SymSet s2, int errcode) {
            // 在某一部分(如一条语句,一个表达式)将要结束时时我们希望下一个符号属于某集合
            //(该部分的后跟符号),test负责这项检测,并且负责当检测不通过时的补救措施,程
            // 序在需要检测时指定当前需要的符号集合和补救用的集合(如之前未完成部分的后跟符
            // 号),以及检测不通过时的错误号。
            if (!s1.get(sym)) {
                  //String ss1 = err.report(errcode);
                  //ss = ss + ss1 + "\n";
                  // 当检测不通过时,不停获取符号,直到它属于需要的集合或补救的集合
                  while (!s1.get(sym) && !s2.get(sym)) {
                        nextSym();
                  }
            }
      }

      /**
       * 分析<分程序>
       *
       * @param lev 当前分程序所在层
       * @param fsys 当前模块后跟符号集
       */
      public void parseBlock(int lev, SymSet fsys) {
            // <分程序> := [<常量说明部分>][<变量说明部分>][<过程说明部分>]<语句>

            int dx0, tx0, cx0; // 保留初始dx,tx和cx
            SymSet nxtlev = new SymSet(symnum);

            dx0 = dx; // 记录本层之前的数据量(以便恢复)
            dx = 3;
            tx0 = table.tx; // 记录本层名字的初始位置(以便恢复)
            table.get(table.tx).adr = interp.cx;

            interp.gen(Fct.JMP, 0, 0);

            if (lev > PL0.levmax) {
                  String ss1 = err.report(32);
                  ss = ss + ss1 + "\n";
            }

            // 分析<说明部分>
            do {
                  // <常量说明部分>
                  if (sym == Symbol.constsym) {
                        nextSym();
                        // the original do...while(sym == ident) is problematic, thanks to calculous
                        // do
                        parseConstDeclaration(lev);
                        while (sym == Symbol.comma) {
                              nextSym();
                              parseConstDeclaration(lev);
                        }

                        if (sym == Symbol.semicolon) {
                              nextSym();
                        }
                        else {
                              String ss1 = err.report(5); // 漏掉了逗号或者分号
                              ss = ss + ss1 + "\n";
                        }
                        // } while (sym == ident);
                  }

                  // <变量说明部分>
                  if (sym == Symbol.varsym) {
                        nextSym();
                        // the original do...while(sym == ident) is problematic, thanks to calculous
                        // do {
                        //TODO 变量和数组
                        parseVarDeclaration(lev);

                        while (sym == Symbol.comma) {
                              nextSym();
                              parseVarDeclaration(lev);
                        }

                        if (sym == Symbol.semicolon) {
                              nextSym();
                        }
                        else {
                              String ss1 = err.report(5); // 漏掉了逗号或者分号
                              ss = ss + ss1 + "\n";
                        }
                        // } while (sym == ident);
                  }

                  // <过程说明部分>
                  while (sym == Symbol.procsym) {
                        nextSym();
                        if (sym == Symbol.ident) {
                              table.enter(sym, Objekt.procedure, lev, dx);
                              nextSym();
                        }
                        else {
                              String ss1 = err.report(4); // procedure后应为标识符
                              ss = ss + ss1 + "\n";
                        }

                        if (sym == Symbol.semicolon) {
                              nextSym();
                        }
                        else {
                              String ss1 = err.report(5); // 漏掉了分号
                              ss = ss + ss1 + "\n";
                        }

                        nxtlev = (SymSet) fsys.clone();
                        nxtlev.set(Symbol.semicolon);
                        parseBlock(lev + 1, nxtlev);

                        if (sym == Symbol.semicolon) {
                              nextSym();
                              nxtlev = (SymSet) statbegsys.clone();
                              nxtlev.set(Symbol.ident);
                              nxtlev.set(Symbol.procsym);
                              test(nxtlev, fsys, 6);
                        }
                        else {
                              String ss1 = err.report(5); // 漏掉了分号
                              ss = ss + ss1 + "\n";
                        }
                  }

                  nxtlev = (SymSet) statbegsys.clone();
                  nxtlev.set(Symbol.ident);
                  test(nxtlev, declbegsys, 7);
            }
            while (declbegsys.get(sym)); // 直到没有声明符号

            // 开始生成当前过程代码
            Table.Item item = table.get(tx0);
            interp.code[item.adr].a = interp.cx;
            item.adr = interp.cx; // 当前过程代码地址
            item.size = dx; // 声明部分中每增加一条声明都会给dx增加1,
            // 声明部分已经结束,dx就是当前过程的堆栈帧大小
            cx0 = interp.cx;
            interp.gen(Fct.INT, 0, dx); // 生成分配内存代码

            table.debugTable(tx0);

            // 分析<语句>
            nxtlev = (SymSet) fsys.clone(); // 每个后跟符号集和都包含上层后跟符号集和,以便补救
            nxtlev.set(Symbol.semicolon); // 语句后跟符号为分号或end
            nxtlev.set(Symbol.endsym);
            parseStatement(nxtlev, lev);
            interp.gen(Fct.OPR, 0, 0); // 每个过程出口都要使用的释放数据段指令

            nxtlev = new SymSet(symnum); // 分程序没有补救集合
            test(fsys, nxtlev, 8); // 检测后跟符号正确性

            interp.listcode(cx0);

            dx = dx0; // 恢复堆栈帧计数器
            table.tx = tx0; // 回复名字表位置
      }

      /**
       * 分析<常量说明部分>
       * @param lev 当前所在的层次
       */
      void parseConstDeclaration(int lev) {
            if (sym == Symbol.ident) {
                  nextSym();
                  if (sym == Symbol.eql || sym == Symbol.becomes) {
                        if (sym == Symbol.becomes) {
                              String ss1 = err.report(1); // 把 = 写成了 :=
                              ss = ss + ss1 + "\n";
                        }
                        nextSym();
                        if (sym == Symbol.number) {
                              table.enter(sym, Objekt.constant, lev, dx);
                              nextSym();
                        }
                        else {
                              String ss1 = err.report(2); // 常量说明 = 后应是数字
                              ss = ss + ss1 + "\n";
                        }
                  }
                  else {
                        String ss1 = err.report(3); // 常量说明标识后应是 =
                        ss = ss + ss1 + "\n";
                  }
            }
            else {
                  String ss1 = err.report(4); // const 后应是标识符
                  ss = ss + ss1 + "\n";
            }
      }

      /**
       * 分析<变量说明部分>
       * @param lev 当前层次
       */
      void parseVarDeclaration(int lev) {
            if (sym == Symbol.ident) {

                  // 填写名字表并改变堆栈帧计数器
                  int startid = 0, endid = 0;

                  table.enter(sym, Objekt.variable, lev, dx);
                  dx++;
                  nextSym();
                  if (sym == Symbol.lparen) {
                        nextSym();
                        switch (sym) {
                              case ident:
                                    startid = table.get(table.position(lex.id)).
                                        val;

                                    break;

                              case number:
                                    startid = PL0.lex.num;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -