jjTree和javaCC学习笔记

来源:互联网 发布:mac地址含有非法字符 编辑:程序博客网 时间:2024/05/22 15:40

jjTree和javaCC学习笔记

javacc是类似lex/yacc的parser生成器,可以把一段文本转换为抽象语法树(AST)。
一般来说,用户首先要写一个jjtree文件(如eg2.jjt),然后执行jjtree eg2.jjt编译得到eg2.jj,再执行javacc eg2.jj就可以生成所有的.java文件。单独使用javacc也可以,不过基本上不能实用。
 
javacc的教程比较多,官方网站上的教程也很不错。相比之下,jjtree的参考手册里讲了很多细节,比如jjtree里面的堆栈,以及一些条件规则,但其实大部分都用不到。第一次看这个reference的时候感觉比较晕,比javacc的几个教程差多了。如果已经看完了javacc的教程,那接下来看一下这篇introduction,然后结合自带例子和eclipse的调试器来学习jjtree比较好。

我们通过一个例子来研究jjtree。下面的例子是javacc自带的eg2.jjt例子的改进版本。

-------------------SimpleNode.java---------------------

  protected Token first_token;
  protected Token last_token;

  public void jjtOpen() {
   first_token = parser.getToken(1);
  }

  public void jjtClose() {
   last_token = parser.getToken(0);
  }


--------------------eg2.jjt------------------------------
options {
 MULTI=true;  

 // 这句是让jjtree给每个非终结符生成一个对象,比如Expression就生成ASTExpression对象,这样才能通过jjtGetChild取得孩子!!!
  NODE_SCOPE_HOOK = true;  
 // 这个跟下面的jjtreeOpenNodeScope、jjtreeCloseNodeScope函数一起,用于从ASTExpression对象得到相应token。 默认情况下,javacc生成的对象里不保存什么信息,所以这里一定要设置一下。这个技巧参考了前面说的那个很好的introduction
}

PARSER_BEGIN(eg2)
package test;

public class Eg2 {

  /** Main entry point. */
  public static void main(String args[]) {
    System.out.println("Reading from standard input...");
    Eg2 t = new Eg2(System.in);
    try {
      ASTStart n = t.Start();
      for (int i = 0; i < n.jjtGetNumChildren(); i++) {
                Node node = n.jjtGetChild(i); //取得一个child,应该对应一个Expression
                Token t1;
                for (t1 = ((SimpleNode)node).first_token; t1 != ((SimpleNode)node).last_token; t1 = t1.next) {
                    System.out.print(t1.image);    // 输出这个token对应的文本
                }
                System.out.println(t1.image); //输出node的最后一个token,并换行
      }   
      n.dump("");
      System.out.println("Thank you.");
    } catch (Exception e) {
      System.out.println("Oops.");
      System.out.println(e.getMessage());
      e.printStackTrace();
    }
   }
}

 

// token部分略去,没什么意思

ASTStart Start() : {}
{
  Expression() ";"
 { return jjtThis; } //注意这里要写一句return,还有Start的返回值不能是void。
}

void Expression()  : {}
{
  AdditiveExpression()
}

void AdditiveExpression() #void : {}
// 这里可以设置#void,说明不必为此非终结符生成对象,也不会生成ASTAdditiveExpression类。
// 因此,Expression的child会包含MultiplicativeExpression
{                                
  (
    MultiplicativeExpression() ( ( "+" | "-" ) MultiplicativeExpression() )*
  )
}

...
// 后面的很多细节都略去了,请参考自带例子,总之只要会BNF就可以了。

// 其中的LOOKAHEAD细节我没有花太多时间研究。大部分时候,我们只要先找来一个别人写的jjt文件,然后改改就可以了。
--------------------eg2.jjt------------------------------