赵翔鹏的Blog Xiangpeng's Thinkpad

13十一/061

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例子的改进版本。
--------------------eg2.jjt------------------------------
options {
 MULTI=true;  

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

PARSER_BEGIN(eg2)
package test;
class eg2 {
  public static void main(String args[]) {
    System.out.println("Reading from standard input...");
    eg2 t = new eg2(System.in);
    try {
      ASTStart n = t.Start();    // parse结果保存到n里面,这里Start是我们定义的一个非终结符
      for (int i = 0; i < n.jjtGetNumChildren(); i++) {
                Node node = n.jjtGetChild(i); //取得一个child,应该对应一个Expression
                Token t;
                for (t = node.first_token; t != node.last_token; t = t.next) {
                    System.out.print(t.image);    // 输出这个token对应的文本
                }
                System.out.println(t.image); //输出node的最后一个token,并换行
      }
      n.dump("");    // 输出语法树的层次结构。
      System.out.println("Thank you.");
    } catch (Exception e) {
      System.out.println("Oops.");
      System.out.println(e.getMessage());
      e.printStackTrace();
    }
  }
 
  static void jjtreeOpenNodeScope( Node node )
  {
    ((SimpleNode)node).first_token = getToken(1);    //这里的first_token,last_token都要手工加到SimpleNode.java中
  }

  static void jjtreeCloseNodeScope( Node node )
  {
    ((SimpleNode)node).last_token = getToken(0);
  }
}

// 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------------------------------
    
评论 (1) 引用 (0)
  1. //这里的first_token,last_token都要手工加到SimpleNode.java中
    可以说具体一点吗


Leave a comment

(required)

还没有引用.