2012-2-7阅读2279 评论0java
/***********************************************************/ >我使用的测试jjt,jj文件来自于javacc5.0版本 >dir_hier/javacc-5.0/javacc-5.0/examples/ JJTreeExamples SimpleExamples /***********************************************************/ /***********************************************************/ 0.javacc(the java Compiler Compiler)概要 >Abstract summary: javacc通常用于编写某种语言的书写规则,如java的语法规则 变量必须以字母或者$开头或者以数字结尾等,这是一种书写规范, 书写规范是这门语言特有的,他必须知足,那么他是怎么去判断 书写的字符窜符合了规则了,那就须要进行验证,javacc就是 一种用于定义某种特定输入格式字符窜规则的工具,也是验证这种 特定格式字符窜的验证者。定义字符窜输入格式,以及验证输入的 字符窜是javacc的最基本做用。 javacc语法规则描述文件的书写方式有两种一种是一种是.jj文件, 另一种是.jjt文件,两种文件的书写方式差别性不大,可是jjt文件 比jj文件表达语法规则更加容易,jj文件的可选参数项和jjt有很大的 不一样,这是他们最大的区别。 /***********************************************************/ /***********************************************************/ 1.javacc语法规则书写操做基本代码 >如何在生产表达式中写条件结构 ( term = < TERM > | term = < STAR > | term = < PREFIXTERM > | term = < WILDTERM > | term = < NUMBER > ) >生成后的代码对应到 分析: 由于在javacc中(<A> | <B>)被表示成了在两个规则中任意选择一个的意思。 switch(tokenkind){ case TERM: /** doSomthing...*/ break; case STAR: /** doSomthing...*/ break; ... } void ifExp():{int k=0;/** java局部变量声明处*/}{ {/*在{}中写任意的java代码而且是以面向对象的方式*/} ( { if(k==2){break;} System.out.println("k="+k); k++; } )* } >如何在生产表达式中写循环结构 ->( { if(k==2){break;} System.out.println("k="+k); k++; } ->)* >生成对应的代码 while(true){ if(k==2){ break; } System.out.println("k="+k); k++; } >在生产表达式中调用其余的生产表达式 对应到java中的方法调用 void B(): { StringBuffer sb = new StringBuffer(); Token t = null; }{ t = A /** 这里对应生成的java代码t=jj_consume_token(A); * 由于等于就是要消费一个记号把消费的这记号的应用给t变量 */ } void A():{}{} >javacc中的词法状态介绍 <*>TOKEN:{} <DEFAULT>TOKEN:{<A:"2001 TO 2002">:DateRange | <B:"anything"> } <DateRange>TOKEN:{} 解析:<*>,<DEFAULT>,<DateRange>是词法状态 定义词法状态的好处是,若是在生产表达式中 正好有一个字符窜匹配到了2001 TO 2002这个字符窜 那么他就会立刻把下一个要匹配的模式字符窜定义 为DateRange这种词法状态中定义的Token >有了循环有了条件结构有了定义变量的方式 javacc语法文件就至关完美了。 /***********************************************************/ /***********************************************************/ 2.JJT语法规则描述文件书写方法 >jjt概要 jjt可以很清晰的表达出语法分析的思路, 而且他把每个生产表达式都做为一个节点来表示, 将这些节点的执行顺序有效的组织成了一颗语法分析树 >.options >BUILD_NODE_FILES (default: true) 为SimpleNode以及语法中使用的其它节点建立样本实现。 >MULTI(default: false) 建立多模式解析树。 此选项默认为False,生成一个单一模式解析树。 >NODE_DEFAULT_VOID (default: false) 此选项设置为True时, 不在使每一个非包装产生式定义一个节点,取而代之为空。 >NODE_FACTORY (default: false) 用下面的方式使用一个工厂方法建立一个节点: public static Node jjtCreate(int id) >NODE_PACKAGE (default: "") 被放进生成节点类里的包。默认为解析器的包。 >NODE_PREFIX (default: "AST") AST意思为抽象节点 在多模式中,前缀用来从节点标志符构造节点类名字。 默认前缀为” AST” >NODE_SCOPE_HOOK (default: false) 在节点做用域入口和出口处插入调用用户自定义的解析方法。 在节点的生命开始和结束以前须要调用的方法被成为钩子。 参见:节点做用域钩子。 >NODE_USES_PARSER (default: false) 是否将当前解析器对象也出入到节点对象的属性中 JJTree会使用一个选择的形式将解析对象传给构造函数。例如: public static Node MyNode.jjtCreate(MyParser p, int id); MyNode(MyParser p, int id); >STATIC (default: true) 为静态解析器生成代码。 选项默认为True。 这必须一致的经过等效的JavaCC选项被使用。 选项的值发布于JavaCC的源码中。 >VISITOR (default: false) 在节点类中插入jjtAccept()方法, 为语法中使用的每一个节点类型产生一个访问者实现。 >VISITOR_EXCEPTION (default: "") 若是这个选项被设置,它将使用jjtAccept()和visit()方法的形式。 注意:这个选项将会在之后的某个JJTree版本中删除。若是不影响你请不要使用它。 >JJTREE_OUTPUT_DIRECTORY (default: use value of OUTPUT_DIRECTORY) 默认状况下,在全局OUTPUT_DIRECTORY设置中指定JJTree生成的输出目录。 明确的设置这个选项容许用户从树文件中分离解析器。 /***********************************************************/ /***********************************************************/ 3.语法分析树节点(Node) >javacc把每个生产表达式都看做为一个简单节点(SimpleNode)在默认 MULTI(default: false)的状况下,javacc已经提供了这个SimpleNode类的简单实现。 >MULTI(default: true)将为每个节点都按照[NODE_PREFIX_生产表达式的名字]这样 一种形式来提供默认的简单实现类。 >定义节点的方式 明肯定义:一个以指定子节点数建立的节点 void AdditiveExpression() #void : {} { ( MultiplicativeExpression() ( ( "+" | "-" )MultiplicativeExpression() )* )#Add(3) } >语法分析树参考结果: Add Integer Integer Integer >#void不会为这个生产表达式生成对应的节点 >#Add表示在循环执行过程当中生成的全部节点中的前三个节点做为 以Add为命名的儿子节点。 按照条件定义: void AdditiveExpression() #void : {} { ( MultiplicativeExpression() ( ( "+" | "-" ) MultiplicativeExpression() )* ) #Add(>3) } >#Add(>1)另一种写法#Add(jjtree.arity() > 1) 生成的源码: jjtree.closeNodeScope(jjtn001, jjtree.nodeArity() > 1); 在执行循环过程当中建立的节点个数大于1的话就会将全部的节点 作为Add的子节点而后将Add节点添加到堆栈中,若是条件不知足 那么建立的全部节点将会保留在栈中默认会在作为上一个节点的 子节点 >语法分析树参考结果: Start /** 由于在执行循环过程当中建立的节点数没有达到指定的个数因此是start*/ Integer Integer >notes: ( ... ) #N ( a() ) 上面表达式逻辑不清, 你必须明确的使用条件式: ( ... ) #N(true) ( a() ) >为产生式的节点指定名称 void ProductExp() #MyNode:{} {//doAnything} #MyNode为这个生产表达式所对应的节点的名称 >特殊应用 void P3():{} { P4() ( P5() )+ #ListOfP5s P6() } #name=jjtree.closeNodeScope(jjtn001, true); #name(3)=jjtree.closeNodeScope(jjtn001, 3); #name(>3)=jjtree.closeNodeScope(jjtn001, jjtree.nodeArity() > 1); >什么是jjThis ASTStart Start() : {} { Expression() ";" { return jjtThis; } } 分析: jjThis表明了当前节点的引用地址, 就好像ASTStart Start()这个生产表达式对应到Start这个 节点那么jjtThis就指向了这个对象, 在其内部的实现代码为: ASTStart jjtn000 = new ASTStart(JJTSTART); >什么是NODE_SCOPE_HOOK="true"(默认为true表示有钩子) 这个名气叫的颇有味,钩子,正好是在某个节点被建立的 时候调用指定的钩子方法,在节点添加到栈的时候要调用的方法 钩子在open的时候能够作一些预备工做,结束能够作一些后续工做 示例代码: static final public void AdditiveExpression() throws ParseException { ASTAdd jjtn001 = new ASTAdd(JJTADD);// 建立节点 boolean jjtc001 = true; jjtree.openNodeScope(jjtn001); // 这就是钩子 try { //do something... } finally { if (jjtc001) { jjtree.closeNodeScope(jjtn001, 3);// 这就是钩子 } } } /***********************************************************/ 4.jjt文件生成的文件解析 SimpleQueryParser.jjt SimpleQueryParser.jj JJTSimpleQueryParserState.java Node.java ParseException.java SimpleCharStream.java SimpleNode.java SimpleQueryParser.java SimpleQueryParserConstants.java SimpleQueryParserTokenManager.java SimpleQueryParserTreeConstants.java Token.java TokenMgrError.java >jjt文件能够生成.jj文件 >SimpleQueryParser这个名字在不少类名中都以重复的字眼出现,这是经过 Parser_Begin(SimpleQueryParser)...Parser_End(SimpleQueryParser) 指定的。 >JJTSimpleQueryParserState.java用于记录节点的组织状况 这个类有单独的说明,在JJTSimpleQueryParserState.java 中,他是一个以堆栈形式设计的类。 >Node.java是一个接口jjt中节点必须实现他,在选项MULTI(default: false) 的时候javacc编译器已经帮咱们简单的作了实现这个类为SimpleNode.java。 >ParseException.java在语法分析的时候遇到不符合规则的字符时抛出的异常 >SimpleQueryParserConstants.java这个类主要用于关联定义的Token >SimpleQueryParserTreeConstants.java主要用于关联定义的节点 若是没有给生产表达式命名那么他默认就是用生产表达式的名字作为节点的 名字 >SimpleQueryParserTokenManager.java这是最重要的类他用于生产Token对象 在getNextToken()中体现了这么一点 >Token.java这个对象做为最小的记号单元用于封装SimpleQueryParserTokenManager 生成的记号 >SimpleQueryParser.java将类综合起来提供语法分析服务,他是消费Token对象的, 在jj_consume_Token(token_kind)中能够体现这一点,传入一个记号的类型返回这个 类型的一个token对象 >TokenMgrError.java在生产Token对象的过程当中遇到错误 >SimpleCharStream.java用于封装输入的字符窜 >SimpleQueryParserTokenManager.java/**用于产生Token*/ SimpleQueryParserTokenManager.getNextToken()/** 生产Token的方法*/ /** 消费Token的SimpleQueryParser利用这些Token生成特定的逻辑对象*/ SimpleQueryParser.java SimpleQueryParser.jj_consume_token(int kind);/** 根据指定的类型消费一个token*/ SimpleQueryParser.jj_ntk()/** 若是当前jj_ntk变量为-1 说明当前token对像的下一个token没有找到须要接着要到tokenmanage里面 拿一个token出来做为当前token的next*/ /***********************************************************/ /***********************************************************/ 5.jj文件写法解析 >options >STATIC=false; 意思全部的生产表达式对应到java代码的时候 不是静态的 /***********************************************************/