JavaCC、解析树和 XQuery 语法

JavaCC详解 绝对干货 考虑到在优快云,没有搜索到很多关于javacc的东西,绝对干货。javacc概述javacc概述  JavaCC 是一个词法分析生成器和语法分析生成器。 词法分析和语法分析是处理输入字符序列的软件构件, 编译器和解释器协同词法分析和语法分析来“解密” 程序文件。  javacc的获取:https://java.net/projects/javacc/downloads从此处下载了javacc 5. 阅读详情

 第 1 部分

使用 BNF 和 JavaCC 构建定制的解析器

在简要讨论了语法、解析器和 BNF 后,本文将介绍 JavaCC,这是一个流行的解析器生成器工具。您将开发使用 JavaCC 的样本代码来构建定制的解析器,先从语法的 BNF 描述开始。第 2 部分接着将演示如何使用辅助工具 ― JJTree 来构建同一解析的解析树表示,以及如何在运行时遍历该树,以发现其状态信息。文章将以开发构建和遍历解析树的样本代码作为结束,该解析树是您为一小部分 XQuery 语法生成的。

要完成最简单的日常解析任务,您不需要使用象自动化解析器生成器那样复杂的任何东西。例如,同“梳理”CSV(逗号分割的值,Comma-Separated-Value)文件的各部分同样简单的编程练习需要了解文件的结构,可能还需要了解如何使用 Java StringTokenizer 。另外,CSV 练习还需要了解很少的解析理论知识或者将自动化工具应用于任务的需求。

但是,一旦正式描述它的某种语言和语法变得复杂,那么语言中的有效表达式数量将迅速增加。而能够手工处理将任意表达式解析成其组成部分(这或多或少是解析更简明的定义)所需的代码将变得越来越困难。自动化解析器生成器减轻了这种困难。其他程序员或许也可以将您生成的解析器用于他们自己的用途。

从 BNF 开始

复杂语言的语法通常都是使用 BNF(巴科斯-诺尔范式,Backus-Naur Form)表示法或者其“近亲”― EBNF(扩展的 BNF)描述的。自动化工具可以使用那些描述(我将使用通用的术语 BNF来指代这两种变体)或与它们近似的描述来为您生成解析代码。本文就描述了这样的一种解析器-生成器工具,称为 JavaCC。我将简要地研究一下 JavaCC 的基本知识,并在结束的时候花些时间研究一下它的一个辅助工具 ― JJTree,但是在讨论中不会介绍太多的理论知识,以免偏离主题。本文力图阐明我的理念:您并不需要了解很多有关正规的解析理论就能进行解析!

为什么使用 JavaCC 呢?有几个原因:我对 XQuery 有着强烈的兴趣,而 W3C 的 XML Query 工作组恰好使用 JavaCC 来构建并测试 XQuery 语法的版本,并且构建和测试它与 XSL 组共享的 XPath 语法。我还使用 JavaCC 来提供 XQEngine 中的查询-解析代码,XQEngine 是我自己的开放源码 XQuery 实现(请参阅 参考资料)。

最后一点(但不是最不重要的),价格是完全合适的:尽管 JavaCC 不是开放源码,但它是完全免费的。(请参阅 参考资料以了解有关如何获得 JavaCC 的信息)。





解析 101

在我开始介绍一些实际的 XQuery 语法之前,让我们先从一个非常简单的 BNF 开始,它描述了一种语言,该语言仅由两个只对整数进行运算的算术运算符构成。我称这种语言为 SimpleLang :


simpleLang     ::=   integerLiteral ( ( "+" | "-" ) integerLiteral )?
integerLiteral ::=   [ 0-9 ]+

该语法中的每个规则都是一个 结果(production) ,其中左边的项(结果的名称)是依据语法中的其它结果描述的。最上面的结果 simpleLang 表明,该语言中有效的(或合法的)表达式是这样构成的,一个整数值,可以任意选择其后跟一个加号(+)或减号(-)以及另一个整数值或不跟任何东西。按照这种语法,单个整数“42”是有效的,同样,表达式“42 + 1”也是有效的。第二个结果以类 regex 的方式更特定地描述了一个整数值看上去象什么:一个或多个数字的连续序列。

该语法描述了两个结果 simpleLang 和 integerLiteral 之间存在的抽象关系。它还详细描述了三个 记号(加号、减号和整数)组合的具体项,解析器在扫描整个输入流时希望遇到这些项。解析器中负责该任务的部件称为 扫描器(scanner)或 记号赋予器(tokenizer) 一点也不稀奇。在该语言中, simpleLang 是 非终端(non-terminal) 符号的一个示例,它对其它结果进行引用;另一方面,规则 integerLiteral 描述了 终端(terminal)符号:这是一种不能进一步分解成其它结果的符号。

如果解析器在其扫描期间发现了除这三个记号外的任何 其它记号,则认为它正在扫描的表达式是无效的。解析器的主要工作之一就是确定您传递给它的任何表达式的有效性,并且让您知道。一旦认为某个表达式是有效的,则它的第二项工作是将输入流分解成其组件块,并以某个有用的方式将它们提供给您。





从 BNF 到 JavaCC

让我们看看如何使用 JavaCC 实现该语法。JavaCC 使用称为 .jj 的文件。该文件中的语法描述是使用非常类似于 BNF 的表示法编写的,这样从一种形式转换到另一种形式通常就相当容易。(该表示法有自己的语法,从而使其在 JavaCC 中是可表达的。)

JavaCC .jj 文件语法和标准的 BNF 之间的主要区别在于:利用 JavaCC 版本,您可以在语法中嵌入操作。一旦成功遍历了语法中的那些部分,则执行这些操作。操作都是 Java 语句,它们是解析器 Java 源代码的一部分,该部分作为解析器生成过程的一部分产生。

(注:除了一条 Java println() 语句外, 清单 1并不包含您需要用来对该语言中的表达式实际求值的嵌入式 Java 代码。当您研究过 JJTree 及其解析树表示后,我将对此做更详细的研究。)


清单 1. 编码 SimpleLang 语法的完整 .jj 脚本


PARSER_BEGIN( Parser_1 )
package examples.example_1;
public class Parser_1 {}
PARSER_END( Parser_1 )

void simpleLang() : {}             { integerLiteral() 
                                   ( ( "+" | "-" ) integerLiteral() )? <EOF> }
void integerLiteral() : {Token t;} { t=<INT> 
                                   { System.out.println("integer = "+t.image); }}

SKIP 	: { " " | "/t" | "/n" | "/r" }
TOKEN	: { < INT : ( ["0" - "9"] )+ > }

请注意有关该文件的下述情形:

  • PARSER_BEGIN 和 PARSER_END 伪指令指定了要生成的 Java 解析器的名称(Parser_1.java),并提供一个位置以便将 Java 语句插入该类。在这个案例中,您正将一个 Java package 语句放置在文件的顶部。该 package 语句也放置在 Java 助手类文件的顶部,该文件是作为生成过程一部分产生的(请参阅 JavaCC 编译过程 )。尽管我在本示例中没有这样做,但是这也是一个声明实例变量的好场所,该实例变量将由您结果中的 Java 语句引用。如果您喜欢,甚至可以在这里插入 Java main() 过程,并且使用它来构建独立的应用程序,以启动和测试您正在生成的解析器。
  • JavaCC 语法看上去象一个过程,而结果看上去非常象进行方法调用。这并非偶然。在 JavaCC 编译这个脚本时产生的 Java 源代码包含与这些结果具有相同名称的方法;这些方法在运行时按照它们在 .jj 脚本中调用的顺序执行。我将在 遍历解析代码中为您演示那是如何工作的。(这里的术语“编译”也不是偶然的 ― 解析器生成器通常也称为“编译器的编译器”。)
  • 花括号({ 和 }) 内描述了结果的主体,并且排除了任何您正在嵌入的 Java 操作。请注意 integerLiteral 规则中用花括号包括的 System.out.println() 语句。该操作作为方法 Parser_1.integerLiteral() 的一部分产生。每当解析器遇到整数时,都执行该操作。
  • 文件结尾的 SKIP 语句表明,在记号之间可以出现空白(空格、跳格、回车和换行),空白将被忽略。
  • TOKEN 语句包含类似 regex 的表达式,该表达式描述了整数记号看起来象什么。在前面的结果中,对这些记号的引用是用尖括号括起来的。
  • 第二个结果 integerLiteral() 声明了类型 Token (JavaCC 的内置类)的局部变量 t 。当在输入流中遇到整数时会 触发 该规则,该整数(象文本一样)的值被赋给实例变量 t.image 。另一个 Token 字段 t.kind 被赋值为一个枚举(enum),表明这个特殊的记号是一个整数,而不是解析器所知的另一种类型的记号。最后,在解析器中生成的 Java System.out.println() 代码可以在解析时在那个记号的内部使用 t.image 进行访问并且打印其文本值。






遍历解析器代码

让我们非常简要地了解一下您生成的解析器的内部原理。出于下面两个原因,稍微了解由特殊的 .jj 语法生成的方法以及其在运行时的执行顺序是很有用的:

  • 有时候(特别是当您第一次做的时候),解析器似乎返回了不同的结果,而您认为是 .jj 文件指示它这样做的。您可以在运行时单步遍历产生的解析器代码,以便查看它到底在做什么,并相应地调整语法文件。我现在还经常这样做。
  • 如果您知道结果/方法的执行顺序,那么您将对在脚本中的什么地方嵌入 Java 操作以获得特定的结果有更好的理解。在第 2 部分谈论有关 JJTree 工具和解析树表示时,我将回过头来更详细地讨论这一内容。

尽管深入研究已生成解析器的详细信息超越了本文的范围,但是 清单 2 还是显示了为方法 Parser_1.integerLiteral() 生成的代码。这可能会让您对最终代码看起来象什么有一些了解。特别需要注意方法中的最后一条语句: System.out.println( "integer = "+t.image) 。该语句作为嵌入 .jj 脚本的 Java 操作发挥作用。


清单 2. Parser_1 中生成的方法


  static final public void integerLiteral() throws ParseException {
                            Token t;
    t = jj_consume_token(INT);
    System.out.println( "integer = "+t.image);
  }

以下高级、详尽的描述说明了这个解析器将做什么:

  1. 最上面的方法 simpleLang() 调用 integerLiteral() 。
  2. integerLiteral() 希望在输入流中立即遇到一个整数,否则该表达式将无效。为了验证这一点,它调用记号赋予器(Tokenizer.java)以返回输入流中的下一个记号。记号赋予器穿过输入流,每次检查一个字符,直到它遇到一个整数或者直至文件结束。如果是前者,则以 <INT> 记号将值“包”起来;如果是后者,则当作 <EOF> ;并将记号返回给 integerLiteral() 做进一步处理。如果记号赋予器未遇到这两个记号,则返回词法错误。
  3. 如果记号赋予器返回的记号不是整数记号或 <EOF> ,那么 integerLiteral() 抛出 ParseException ,同时解析完成。
  4. 如果它是整数记号,表达式仍然可能是有效的, integerLiteral() 再次调用记号赋予器以返回下一个记号。如果返回 <EOF> ,则由单个整数构成的整个表达式都是有效的,解析器将控制返还给调用应用程序。
  5. 如果记号赋予器返回加号或减号记号,则表达式仍然是有效的, integerLiteral() 将最后一次调用记号赋予器,以寻找另一个整数。如果遇到一个整数,则表达式是有效的,解析器将完成工作。如果下一个记号不是整数,则解析器抛出异常。

注:如果解析器失败了,则抛出 ParseException 或 TokenMgrError 。任何一种异常都表明您的表达式是无效的。

这里的 要点 是,只有当解析器成功地遍历了嵌入 Java 操作的那部分结果后,才能执行嵌入到这两个结果中的任何 Java 操作。如果将表达式“42 + 1”传递给该解析器,则语句 integer = 42 将被打印到控制台,后跟 integer = 1 。如果运行无效的表达式“42 + abc”,则产生消息 integer = 42 ,后跟 catch 块消息 a Token Manager error! 。在后一种情形中,解析器只成功地遍历了 simpleLang 结果中的第一个 integerLiteral() 项,而未成功遍历第二项:


void simpleLang() : {} { integerLiteral() ( ("+" | "-") integerLiteral() )? <EOF> }

换而言之,第二个 integerLiteral() 方法未被执行,因为未遇到希望的整数标记。






JavaCC 编译过程

当您对 .jj 文件运行 JavaCC 时,它会生成许多 Java 源文件。其中一个是主解析代码 Parser_1.java,当您有一个要解析的表达式时,您将从您的应用程序调用该代码。JavaCC 还创建了其它六个由解析器使用的辅助文件。JavaCC 总共生成了以下七个 Java 文件。前三个是特定于这个特殊语法的;后四个是通用的助手类 Java 文件,无论语法是怎么样的,都会生成这几个文件。

  • Parser_1.java
  • Parser_1Constants.java
  • Parser_1TokenManager.java
  • ParseException.java
  • SimpleCharStream.java
  • Token.java
  • TokenMgrError.java

一旦 JavaCC 生成了这七个 Java 源文件,则可以编译它们并将它们链接到您的 Java 应用程序中。然后可以从您的应用程序代码调用新的解析器,从而将表达式传递给它进行求值。下面是一个样本应用程序,它实例化您的解析器,并且为它提供了一个硬连接在应用程序顶部的表达式。


清单 3:调用第一个解析器


package examples.example_1;

import examples.example_1.Parser_1;
import examples.example_1.ParseException;
import java.io.StringReader;

public class Example_1
{
    static String expression = "1 + 42";
    
    public static void main( String[] args )
    //--------------------------------------
    {
        new Example_1().parse( expression );
    }

    void parse( String expression )
    //-----------------------------
    {
        Parser_1 parser = new Parser_1( new StringReader( expression ));
        try 
        {
            parser.simpleLang();
        }
        catch( ParseException pe )  {
            System.out.println( "not a valid expression" ); 
        }
        catch( TokenMgrError e ) {
            System.out.println( "a Token Manager error!" );
        }
    }
}

这里有什么是值得注意的呢?

  1. 要调用 Parser_1 中的解析代码,需要调用该类中的方法 simpleLang() 。.jj 文件中的结果顺序通常是无关的,而本案例除外,在本案例中,语法中最顶部的结果名称用于调用解析器。
  2. 如果正在传递给解析器代码的表达式不能根据语法合法地构造,则将抛出 ParseException 或 LexicalError 。
  3. 如果表达式是有效的,则执行嵌入语法各部分的任何 Java 操作,这些语法部分都被成功遍历,就象 遍历解析器代码结尾描述的一样。





结束语

这篇文章结束后还有第 2 部分。您将从类似的样本代码开始着手,学习如何使用 JavaCC 的“伙伴”工具 JJTree 来创建在运行时构建解析的解析树表示的解析器,而不是执行嵌入 .jj 脚本的操作。正如您将看到的,这有很多优点。

第 2 部分

本文的第 1 部分简要讨论了语法、解析器和 BNF。然后它介绍了 JavaCC,一个流行的解析器生成器。第 2 部分演示了如何修改第 1 部分中的样本代码,这样就可以使用附加工具 JJTree 来构建相同解析的解析树表示。您将探索这种方法的优点,并研究如何编写 Java 代码在运行时遍历该解析树以便恢复其状态信息,并对正在解析的表达式求值。本文结尾将演示如何开发通用例程,用于遍历从一小部分 XQuery 语法生成的解析树,并对其求值。

使用 JavaCC 解析器生成器有一个严重缺点:许多或大多数客户机端 Java 代码需要嵌入到 .jj 语法脚本中,该脚本编码了您的 BNF(巴科斯-诺尔范式,Backus-Naur Form)。这意味着您失去了在开发周期中合适的 Java IDE 可以向您提供的许多优点。

开始使用 JJTree 吧,它是 JavaCC 的伙伴工具。JJTree 被设置成提供一个解析器,该解析器在运行时的主要工作不是执行嵌入的 Java 操作,而是构建正在解析的表达式的独立解析树表示。这样,您就可以独立于生成该解析树的解析代码,捕捉在运行时易于遍历和查询的单个树中的解析会话的状态。使用解析树表示还会使调试变得更容易,并缩短开发时间。JJTree 是作为 JavaCC 分发版(distribution)的一部分发布的(请参阅 参考资料)。

在我们继续之前,我要特别提一下,术语 解析树和 抽象语法树(或 AST)描述了非常相似的语法结构。严格地讲,对于我在下面提到的解析树,语言理论家更精确地把它称作 AST。

要使用 JJTree,您需要能够:

  1. 创建 JJTree 作为输入获取的 .jjt 脚本
  2. 编写客户机端代码以遍历在运行时生成的解析树并对其求值

本文演示了如何执行这两种操作。它并没有涵盖所有内容,但肯定能带您入门。

JJTree 基础知识

JJTree 是一个预处理器,为特定 BNF 生成解析器只需要简单的两步:

  1. 对所谓的 .jjt 文件运行 JJTree;它会产生一个中间的 .jj 文件
  2. 用 JavaCC 编译该文件( 第 1 部分中讨论了这个过程)

幸运的是,.jjt 文件的结构只是我在第 1 部分中向您显示的 .jj 格式的较小扩展。主要区别是 JJTree 添加了一个新的语法 node-constructor构造,该构造可以让您指定在解析期间在哪里以及在什么条件下生成解析树节点。换句话说,该构造管理由解析器构造的解析树的形状和内容。

清单 1 显示了一个简单的 JavaCC .jj 脚本,它类似于您在第 1 部分中看到的脚本。为简便起见,我只显示了结果。


清单 1. simpleLang 的 JavaCC 语法

void simpleLang()   : {}    { addExpr() <EOF> }

void addExpr()      : {}    { integerLiteral() ( "+" integerLiteral() )? }

void integerLiteral()   : {}    { <INT> }

SKIP  : { " " | "/t" | "/n" | "/r" }

TOKEN : { < INT : ( ["0" - "9"] )+ > }


该语法说明了该语言中的合法表达式包含:

  1. 单个整数文字,或
  2. 一个整数文字,后面跟一个加号,再跟另一个整数文字。

对应的 JJTree .jjt 脚本(再次声明,略有简化)看上去可能如下:


清单 2. 等价于清单 1 中的 JavaCC 语法的 JJTree

SimpleNode simpleLang() : #Root       {}  { addExpr() <EOF> { return jjtThis; }}

void addExpr()          :             {}  { integerLiteral()

                                          ( "+" integerLiteral() #Add(2) )? }

void integerLiteral()   : #IntLiteral {}  { <INT> }

SKIP  : { " " | "/t" | "/n" | "/r" }

TOKEN : { < INT : ( ["0" - "9"] )+ > }


该脚本对您已经看到的脚本添加了一些新的语法特性。现在,我们只讨论突出显示的部分。以后,我会详细说明。






逐句说明 JJTree 语法

首先请注意,最顶层的 simpleLang() 结果的 JavaCC 的过程性语法现在指定了一个返回类型: SimpleNode 。它与嵌入的 Java 操作 return jjtThis (有一点为 JJTree 虚张声势)一起指定了从应用程序代码调用解析器的 simpleLang() 方法将返回解析树的根,然后这个根将用于树遍历。

在 JavaCC 中,解析器调用看上去如下:

   SimpleParser parser = new SimpleParser(new StringReader( expression ));

    parser.simpleLang();


而现在看上去象下面这样:

   SimpleParser parser = new SimpleParser(new StringReader( expression ));

    SimpleNode rootNode = parser.simpleLang();


注:所抓取的根节点并不仅仅是 SimpleNode 类型。它其实是 Root 类型,正如 清单 2 中的 #Root 伪指令所指定的(虽然您不会在上述调用代码中那样使用)。 Root 是 SimpleNode 子代,就象 JJTree 生成的解析器构造的每个节点一样。我将在下面向您显示 SimpleNode 的一些内置方法。

addExpr() 结果中的 #Add(2) 构造与上述的 #Root 伪指令不同,体现在以下几方面;

  • 它是参数化的。树构建器在构造树期间使用节点堆栈;没有参数的节点构建器的缺省行为是将自己放在正在构造的解析树的顶部,将所有节点弹出在同一个 节点作用域 中创建的节点堆栈,并把自己提升到那些节点父代的位置。参数 2 告诉新的父节点(在此示例中是一个 Add 节点)要恰好采用 两个子节点,它们是 下一段文字 中描述的两个 IntLiteral 子节点。JJTree 文档更详细地描述了这个过程。使用好的调试器在运行时遍历解析树是另一个宝贵的辅助方法,它有助于理解树构建在 JJTree 中是如何工作的。
  • 将 #Root 伪指令放在其结果的主体之外表示 每次 遍历该结果时都会生成一个 Root 节点(而在此特定示例中,只允许发生一次),这一点具有同等的重要性 。但是,将 #Add(2) 伪指令放在可选的“零或一”项中表示仅当在解析期间遍历包含它的选择子句时 ― 换句话说,当该结果表示一个真的加法操作时 ― 才 有条件地 生成一个 Add 节点。当发生这种情况时,会遍历 integerLiteral() 两次,每次调用时都将一个 IntLiteral 节点添加到树上。这两个 IntLiteral 节点都成为调用它们的 Add 节点的子节点。但是,如果正在解析的表达式是单个整数,那么作为结果的 IntLiteral 节点将直接成为 Root 的一个子节点。

一图胜千言(引用一句古老的谚语)。以下是由上述语法生成的两种类型的解析树的图形表示:


图 1:单个整数表达式的解析树
单个整数表达式的解析树的图片

图 2:加法操作的解析树
加法操作的解析树的图片

让我们更详细地研究 SimpleNode 的类层次结构。





使用解析树

在 .jjt 脚本中声明的每个节点都指示解析器生成 JJTree SimpleNode 的一个子类。接下来, SimpleNode 又实现名为 Node 的 Java 接口。这两个类的源文件都是由 JJTree 脚本和定制 .jj 文件一起自动生成的。 清单 1 显示了定制 .jj 文件的当前示例。在当前示例中,JJTree 还提供了您自己的 Root 、 Add 和 IntLiteral 类以及没有在这里看到的一些附加的助手类的源文件。

所有 SimpleNode 子类都继承了有用的行为。 SimpleNode 方法 dump() 就是这样一个例子。它还充当了我以前的论点(使用解析树使调试更容易,从而缩短开发时间)的示例。以下三行客户机端代码的代码片段实例化了解析器、调用解析器、抓取所返回的解析树,并且将一个简单的解析树的文本表示转储到控制台:

   SimpleParser parser = new SimpleParser(new StringReader( expression ));

    SimpleNode rootNode = parser.simpleLang();

    rootNode.dump();


图 2中的树的调试输出是:

   Root

        Add

            IntLiteral

            IntLiteral







辅助导航

另一个有用的内置 SimpleNode 方法是 jjtGetChild(int) 。当您在客户机端向下浏览解析树,并且遇到 Add 节点时,您会要抓取它的 IntLiteral 子节点、抽取它们表示的整数值,并将这些数字加到一起 ― 毕竟,那是用来练习的。假设下一段代码中显示的 addNode 是表示我们感兴趣的 Add 类型节点的变量,那我们就可以访问 addNode 的两个子节点。( lhs 和 rhs 分别是 左边(left-hand side)和 右边(right-hand side)的常用缩写。)

    SimpleNode lhs = addNode.jjtGetChild( 0 );

     SimpleNode rhs = addNode.jjtGetChild( 1 );


即使到目前为止您已经执行了所有操作,但您仍然没有足够的信息来计算该解析树表示的算术运算的结果。您的当前脚本已经省略了一个重要的细节:树中的两个 IntLiteral 节点实际上不包含它们声称要表示的整数。那是因为当记号赋予器(tokenizer)在输入流中遇到它们时,您没有将它们的值保存到树中;您需要修改 integerLiteral() 结果来执行该操作。您还需要将一些简单的存取器方法添加到 SimpleNode 。





保存和恢复状态

要将所扫描的记号的值存储到适当的节点中,将以下修改添加到 SimpleNode :

   public class SimpleNode extends Node

    {

        String m_text;

        public void   setText( String text ) { m_text = text; }

        public String getText()              { return m_text; }

        ...

    }


将 JJTree 脚本中的以下结果:

   void integerLiteral() : #IntLiteral {} <INT> }


更改成:

   void integerLiteral() : #IntLiteral { Token t; }

                                        { t=<INT> { jjtThis.setText( t.image );} }


该结果抓取它刚在 t.image 中遇到的整数的原始文本值,并使用您的 setText() setter 方法将该字符串存储到当前节点中。 清单 5 中的客户机端 eval() 代码显示了如何使用相应的 getText() getter 方法。

可以很容易地修改 SimpleNode.dump() ,以提供任何节点的 m_text 值供其在解析期间存储 ― 我把这作为一个众所周知的练习留给您来完成。这将让您更形象地理解在进行调试时解析树看起来是什么样子。例如,如果您解析了“42 + 1”,略经修改的 dump() 例程可以生成以下有用的输出:

   Root

        Add

            IntLiteral[42]

            IntLiteral[1]






组合:XQuery 的 BNF 代码片段

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

清单 3显示了您要使用的 XQuery 语法片段。这段 BNF 摘自 2002 年 11 月 15 日的工作草案:


清单 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 )*

      ...


您将要构建一个刚好适合的 JJTree 语法脚本来处理结果 [36] 和 [37] 中的 + 、 - 、 * 和 div 运算符,而且简单地假设该语法所知道的唯一数据类型是整数。该样本语法 非常小,并不能妥善处理 XQuery 支持的丰富的表达式和数据类型。但是,如果您要为更大、更复杂的语法构建解析器,它应该能给您使用 JavaCC 和 JJTree 的入门知识。

清单 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”。

实现优先级

该语法还知道 运算符优先级。例如,您期望乘法的优先级比加法高。在实际例子中,这表示诸如“1 + 2 * 3”这样的表达式将作为“1 + ( 2 * 3 )”进行求值,而不是“( 1 + 2 ) * 3”。

优先级是通过使用级联样式实现的,其中每个结果会调用紧随其后的较高优先级的结果。级联样式和节点构造的位置和格式保证了以正确的结构生成解析树,这样树遍历可以正确执行。用一些直观图形也许更易于理解这一点。

图 3 显示了由此语法生成的解析树,它可以让您正确地将“1 + 2 * 3”当作“1 + ( 2 * 3 )”进行求值。请注意, Mult 运算符与它的项之间的联系比 Plus 更紧密,而这正是您希望的:


图 3. 结构正确的树
结构正确的树的图

而 图 4显示的树(该语法 不会生成这样的树)表示您(错误地)想要将该表达式当作“(1 + 2) * 3”求值。


图 4. 结构不正确的树
结构不正确的树的图




遍历解析树客户机端

就象我曾答应的,我将用客户机端代码的清单作为总结,该清单将调用该解析器并遍历它生成的解析树,它使用简单而功能强大的递归 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 脚本。

关于作者

Howard Katz 居住在加拿大温哥华,他是 Fatdog Software 的唯一业主,该公司专门致力于开发搜索 XML 文档的软件。在过去的大约 35 年里,他一直是活跃的程序员(一直业绩良好),并且长期为计算机贸易出版机构撰写技术文章。Howard 是 Vancouver XML Developer's Association 的共同主持人,还是 Addison Wesley 即将出版的书籍 The Experts on XQuery的编辑,该书由 W3C 的 Query 工作组成员合著,概述了有关 XQuery 的技术前景。他和他的妻子夏天去划船,冬天去边远地区滑雪。可以通过 howardk@fatdog.com与 Howard 联系。

CMM语言解释器构造实践(二)——JavaCC等编译工具的使用 上篇博文中我们已经简要介绍了CMM语言的基础特点,以及整个编译器项目的基本开发流程。下面就对开发的第一个阶段“编译工具的使用”详细说明。编译工具的发展 阅读详情

相关推荐

JavaCC语法文件中文版 V2.0_静水71

JavaCC 语法文件 中文版本 翻译的英文,并做了整理,形成了标题,便于查看。 只用于学习交流,如需发布,请不要修改其内容。谢谢!

javaCC教程-1、简单语法解析案例

javaCC入门教程-1、匹配括号 1、配置javacc环境变量 将javacc的路径添加到系统变量Path,D:\java源码包\javacc\javacc-5.0\bin 测试javacc命令 cmd模式下j输入javacc测试 Simple1.jj 文件内容如下: options { LOOKAHEAD = 1; CHOICE_AMBIGUITY_CHECK = 2; O...

Brave Soul Blog 3773

Groovy和Xquery脚本JSON和XML解析工具

脚本解析工具,可使用Groovy或Xquery解析JSON报文或XML报文

java jj_javacc jjtree 写法 以及 jj写法 基本语法 以及应用

/***********************************************************/>我使用的测试jjt,jj文件来自于javacc5.0版本>dir_hier/javacc-5.0/javacc-5.0/examples/JJTreeExamplesSimpleExamples/**********************************...

weixin_33270404的博客 1428

JavaCC语法分析器 基于Eclipse插件 能从文件读取源代码输出语法树

在学编译原理,自己写的,基于我的另一个词法分析器资源的进一步实现,测试类为src/parser/Test.java,能输出源代码的语法树,希望对你有所帮助

从零开始深入理解 AI Agent(完整版)

AI Agent(智能体)是一个能够自主感知环境、做出决策、执行行动并记忆经验的智能系统。特性传统程序AI Agent决策方式固定逻辑(if-else)动态推理(LLM 思考)灵活性需要重新编程自然语言描述即可能力边界明确限定可通过工具扩展学习能力无可通过记忆积累经验│ 感知 │ ───→ │ 推理 │ ───→ │ 行动 │↑ │ ││ ↓ ││ 记忆 │ ←─── │ 反思 │ ←─────────┘感知(Perception)

qq_20096229的博客 754

javacc jjtree 写法 以及 jj写法 基本语法 以及应用

/***********************************************************/ >我使用的测试jjt,jj文件来自于javacc5.0版本 >dir_hier/javacc-5.0/javacc-5.0/examples/ JJTreeExamples

zyb243380456的专栏 1万+

Shader之——花草动画shader

今天 ,接到了个需求,花草的动画。谈到花草动画,立马就想到顶点动画,让顶点动起来就可以实现了。但是有个问题,所有的草都往一个方向运动,这个就麻烦了。因为在shader里没有随机这种概念,提到最多的是用一张噪声图来模拟。在网上搜索了一下,发现有更好的办法,就是用每个顶点到一个点的距离来控制动画的开始,毕竟每个顶点到同一个点的距离很少是一样的。 float dis = distance ...

baicaishisan的博客 2182

JAVACC使用总结(四):LOOKAHEAD解决语法选择冲突的利刃

语法冲突 LOOKHEAD

qq_15089775的博客 1978

ComfyUI的G-Dino模型加载器报错解决方案:从崩溃到重生

遇到G-Dino模型加载器报错不要慌,按照本文的步骤操作,99%的问题都能解决。模型文件正确下载到指定位置segment_anything节点安装完整且无冲突需要补充更多细节或故障排除方法吗?可以随时告诉我。

2301_78150483的博客 4020

《二、企业级监控系统之Prometheus监控node_exporter》

文章目录一、部署node_exporter服务二、部署prometheus服务2.1、部署node_exporter服务三、grafana之添加数据源四、grafana之导入监控模板五、grafana之修改模板变量六、grafana之修改仪表板变量总结 > 这里是引用 一、部署node_exporter服务 docker run -d --restart=always --name node_exporter --net=“host” --pid=“host” -v “/:/host:ro,

东城绝神 1万+

JavaCC、解析树和 XQuery 语法,第 2 部分

JavaCC、解析树和 XQuery 语法,第 2 部分 使用 JJTree 构建和遍历定制解析树

石头记 1434

SI案例分享--考虑ESD保护二极管的差分线阻抗仿真

PCB设计中的cut out参考面早已是SI评估中常用的阻抗优化方式,通过合理的仿真建模,可以提供合适的设计参考,并保持与测试结果的高度一致性。

一只豌豆象的博客 1436

JavaCC、解析树和 XQuery 语法第 1 部分

JavaCC、解析树和 XQuery 语法,第 1 部分 使用 BNF 和 JavaCC 构建定制的解析器

石头记 1075

软考中级网络工程师通关指南:从学习到实战

本文是软考中级网络工程师备考实用指南,先介绍备考前准备,包括考试时间、形式、分值等基本信息,及引导读者评估自身基础以制定学习计划;再推荐官方教材、辅导书籍、在线课程平台和学习社区等优质资源。随后梳理网络协议、设备配置、安全、规划设计等重点知识,剖析企业网络故障排查与园区网络设计典型案例。还分享真题使用、解题技巧、错题本建立等习题解答方法,给出分阶段备考、时间安排与总结反思的备考计划,最后提供考场答题顺序、检查及心态调整策略,助力考生高效备考、顺利通关。

邓邓子的博客 1851

论文阅读:2024 ACM CCS “Do Anything Now”: Characterizing and Evaluating In-The-Wild Jailbreak Prompts on

大语言模型(LLMs)的滥用问题引起了公众和大语言模型供应商的高度关注。有一种特殊的对抗性提示,叫做越狱提示,它已成为绕过防护措施、让大语言模型生成有害内容的主要攻击手段。在这篇论文中,作者使用新框架JailbreakHub,对2022年12月至2023年12月期间的1405个越狱提示进行了全面分析。作者识别出131个越狱提示社区,发现了越狱提示的独特特征以及主要攻击策略,比如提示注入和权限提升。

CSPhD-winston的博客 1410

JJTree

本文的第 1 部分简要讨论了语法、解析器和 BNF。然后它介绍了 JavaCC,一个流行的解析器生成器。第 2 部分演示了如何修改第 1 部分中的样本代码,这样就可以使用附加工具 JJTree 来构建相同解析的解析树表示。您将探索这种方法的优点,并研究如何编写 Java 代码在运

月亮的技术博客 4131

Authentication Assertion Tickets,SAP 系统到系统单点登录里的短命凭证

SAP 系统间的身份传递机制存在 Authentication Assertion Ticket 和 SAP Logon Ticket 两种易混淆的凭证。前者用于系统间短时身份委托(有效期2分钟,通过HTTP头传输),后者用于浏览器SSO(可配置有效期,通过Cookie传输)。Assertion Ticket依赖签发系统的数字签名和严格的时间同步,适用于ABAP/Java系统间的后台作业调用。现代SAP架构已转向OAuth 2.0等标准协议,但老系统中仍需注意两种票据的关键差异:传输方式、有效期、接收方限制

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 601

JavaCC解析XQuery 代码

本文来自于http://www.360doc.com/content/07/0625/16/13829_578971.shtml 我将向您演示一段非常小的 XQuery 的 BNF 子集,这是 XML 的查询语言的 W3C 规范(请参阅 参考资料)。我在这里所说的大多数内容也适用于 XPath,因为这两者共享了许多相同的语法。我还将简要地研究运算符优先级的问题,并将树遍历代码推广到成熟的递归

xuanmu2011的专栏 501

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

javaCC

javaCCJAVA版 yacc lex 啊,lucene 使用这个来做 query 解析.官方网站https://javacc.dev.java.net/ibm DW介绍JavaCC、解析树和 XQuery 语法http://www-900.ibm.com/developerWorks/cn/xml/x-javacc/part1/index.shtml现在你可以定义自己的语言!http://ww

debren的专栏 2615

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

jjTree和javaCC学习笔记

jjTree和javaCC学习笔记javacc是类似lex/yacc的parser生成器,可以把一段文本转换为抽象语法树(AST)。一般来说,用户首先要写一个jjtree文件(如eg2.jjt),然后执行jjtree eg2.jjt编译得到eg2.jj,再执行javacc eg2

月亮的技术博客 7951

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

JavaCC学习笔记

JavaCC学习笔记   编译原理和解释器构造是很重要的课程,它几乎是计算机行业的一项最为关键的技术,所以这学期下决心要把这门课程学好,学得更加熟练一些。大二的时候我们的编译原理课程是由李莉老师教授的,而在上课期间,我也自己用c++实现了一个编译器,这个编译器是基于Kenneth C. Louden编写的编译器,我在上面添加了数组,指针,函数的定义,以及浮点数的使用和声明语句。并且自主实现了词

Yelbosh的专栏 8711

javacc安装与使用

首先,下载javacc-5.0,里面有bin文件夹、doc文件夹、examples文件夹。将其放在d盘下。         其次,配置环境变量。PATH=D:\javacc-5.0\bin         再次,cmd打开dos命令窗口,找到所要编译文件所在的文件夹             最后,输入命令:javacc NutchAnalysis.jj。即可对文件NutchAnaly

xiaoyu714543065的专栏 8106

Javacc入门笔记

一、什么是Javacc 1.1 简介 Java Compiler Compiler (JavaCC) is the most popular parser generator for use with Java applications. Java编译器编译器(JavaCC)是最流行的用于Java应用程序的解析器生成器。解析器生成器是一种工具,用于读取语法规范并将其转换为可以识别与语法匹配的J...

天涯遍地是小草的博客 2452

Spring AI + GPT-5.5 + Ollama 配置教程:Base URL、超时与流式调用

由于不同 Spring AI、Spring Boot 小版本之间仍可能存在差异,创建项目时应检查 Spring Initializr、Spring AI BOM 和官方兼容矩阵,不要直接复制旧教程中的 Starter 名称和版本号。多人项目最好隔离不同客户端,并在脱敏前提下记录最终 URL、状态码、请求耗时、请求 ID 和模型 ID。Spring AI 接入模型 API,至少要同时确认 Starter、Base URL、请求路径、API Key、模型 ID、连接超时、读取超时和流式客户端这 8 项。

2601_95893075的博客 214
上一篇: 由于项目原因又用到了以前没接触到的东东(JAVACC,ASTParser,Java Development Tooling (JDT) )
下一篇: Java功底之static、final、this、super
超级谷歌
博客等级 码龄20年 2271粉丝 286原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值