JavaCC解析XQuery 代码

 本文来自于 http://www.360doc.com/content/07/0625/16/13829_578971.shtml

我将向您演示一段非常小的 XQuery 的 BNF 子集,这是 XML 的查询语言的 W3C 规范(请参阅 参考资料)。我在这里所说的大多数内容也适用于 XPath,因为这两者共享了许多相同的语法。我还将简要地研究运算符优先级的问题,并将树遍历代码推广到成熟的递归例程中,该例程可以处理任意复杂的解析树。

清单 3:一部分 XQuery 语法

[21]  Query              ::= QueryProlog QueryBody
    ...
[23]  QueryBody          ::= ExprSequence?
[24]  ExprSequence       ::= Expr ( "," Expr )*
[25]  Expr               ::= OrExpr
    ...
[35]  RangeExpr          ::= AdditiveExpr ( "to"  AdditiveExpr )*
[36]  AdditiveExpr       ::= MultiplicativeExpr (("+" | "-") MultiplicativeExpr )*
[37]  MultiplicativeExpr ::= UnionExpr (("*" | "div" | "idiv" | "mod") UnaryExpr )*
      ...

清单 4 显示了 .jjt 脚本。请注意该文件顶部的 options{} 块。这些选项(还有许多其它可用选项开关)指定了其中树构建在本例中是以 多重 方式运行的,即节点构造器用于显式地命名所生成节点的类型。备用方法(不在这里研究)是结果只将 SimpleNode 节点提供给解析树,而不是它的子类。如果您想要避免扩散节点类,那么该选项很有用。

请注意原始的 XQuery BNF 经常将多个运算符组合到同一个结果中。在 清单 4中,我已经将这些运算符分离到 JJTree 脚本中的单独结果中,因为这让客户机端的代码更简单。要进行组合,只需存储已扫描的运算符的值,就象对整数所进行的操作一样。

清单 4:清单 3 中的 XQuery 语法的 JJTree 脚本

options {
   MULTI=true;
   NODE_DEFAULT_VOID=true;
   NODE_PREFIX="";
}
PARSER_BEGIN( XQueryParser )
package examples.example_2;
public class XQueryParser{}
PARSER_END( XQueryParser )
SimpleNode query()     #Root      : {} { additiveExpr() <EOF> { return jjtThis; }}
void additiveExpr()               : {} { subtractiveExpr()
                                       ( "+" subtractiveExpr() #Add(2) )* }
void subtractiveExpr()            : {} { multiplicativeExpr()
                                       ( "-" multiplicativeExpr() #Subtract(2) )* }
void multiplicativeExpr()         : {} { divExpr() ( "*" divExpr() #Mult(2) )* }
void divExpr()                    : {} { integerLiteral()
                                       ( "div" integerLiteral() #Div(2) )* }
void integerLiteral() #IntLiteral :    { Token t; }
                                       { t=<INT> { jjtThis.setText(t.image); }}
SKIP  : { " " | "\t" | "\n" | "\r" }
TOKEN : { < INT : ( ["0" - "9"] )+ > }

该 .jjt 文件引入了几个新的特性。例如,该语法中的运算结果现在是 迭代的 :通过使用 * (零次或多次)发生指示符来表示它们的可选的第二项,这与 清单 2 中的 ? (零次或一次)表示法相反。该脚本所提供的解析器可以解析任意长的表达式,如“1 + 2 * 3 div 4 + 5”。

遍历解析树客户机端

就象我曾答应的,我将用客户机端代码的清单作为总结,该清单将调用该解析器并遍历它生成的解析树,它使用简单而功能强大的递归 eval() 函数对树遍历时遇到的每个节点执行正确操作。 清单 5中的注释提供了关于内部 JJTree 工作的附加详细信息。



清单 5. 可容易泛化的 eval() 例程

   // return the arithmetic result of evaluating ‘query‘
    public int parse( String query )
    //------------------------------
    {
        SimpleNode root = null;
     // instantiate the parser
        XQueryParser parser = new XQueryParser( new StringReader( query ));
            try {
            // invoke it via its topmost production
            // and get a parse tree back
            root = parser.query();
            root.dump("");
        }
        catch( ParseException pe ) {
            System.out.println( "parse(): an invalid expression!" );
        }
        catch( TokenMgrError e )  {
            System.out.println( "a Token Manager error!" );
        }
        // the topmost root node is just a placeholder; ignore it.
        return eval( (SimpleNode) root.jjtGetChild(0) );
    }
    int eval( SimpleNode node )
    //-------------------------
    {
        // each node contains an id field identifying its type.
        // we switch on these. we could use instanceof, but that‘s less efficient
        // enum values such as JJTINTLITERAL come from the interface file
        // SimpleParserTreeConstants, which SimpleParser implements.
        // This interface file is one of several auxilliary Java sources
        // generated by JJTree. JavaCC contributes several others.
        int id = node.id;
        // eventually the buck stops here and we unwind the stack
        if ( node.id == JJTINTLITERAL )
            return Integer.parseInt( node.getText() );
        SimpleNode lhs =  (SimpleNode) node.jjtGetChild(0);
        SimpleNode rhs =  (SimpleNode) node.jjtGetChild(1);
        switch( id )
        {
            case JJTADD :       return eval( lhs ) + eval( rhs );
            case JJTSUBTRACT :  return eval( lhs ) - eval( rhs );
            case JJTMULT :      return eval( lhs ) * eval( rhs );
            case JJTDIV :       return eval( lhs ) / eval( rhs );
            default :
                throw new java.lang.IllegalArgumentException(
                                  "eval(): invalid operator!" );
        }
    }

如果您想要查看可以处理许多实际 XQuery 语法的功能更丰富的 eval() 函数版本,欢迎下载我的开放源码 XQuery 实现(XQEngine)的副本(请参阅 参考资料 )。它的 TreeWalker.eval() 例程 例举了 30 多种 XQuery 节点类型。还提供了一个 .jjt 脚本。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值