【问题标题】:JavaCC Problem - Generated code doesn't find all parse errorsJavaCC 问题 - 生成的代码未找到所有解析错误
【发布时间】:2009-11-12 11:19:40
【问题描述】:

刚开始使用 JavaCC。但我有一个奇怪的行为。我想以标记(字母和数字)的形式验证输入,这些标记与符号(+、-、/)连接,并且可以包含括号。 我希望这是可以理解的:)

main方法中是一个字符串,应该会报错,因为它有一个左括号,两个右括号,但是我没有得到解析异常 --> 为什么?

有人知道为什么我没有得到异常吗?

我在最初的尝试中遇到了左递归和选择冲突,但设法克服了它们。也许是我介绍了问题?!

哦 - 也许我的解决方案不是很好 - 忽略这个事实......或者更好,给一些建议;-)

文件:CodeParser.jj

 options {
   STATIC=false;
 }

 PARSER_BEGIN(CodeParser)

 package com.testing;

 import java.io.StringReader;
 import java.io.Reader;

 public class CodeParser {

     public CodeParser(String s) 
     {
         this((Reader)(new StringReader(s))); 

     }

     public static void main(String args[])
     {
         try
         {
               /** String has one open, but two closing parenthesis --> should produce parse error */
               String s = "A+BC+-(2XXL+A/-B))";
               CodeParser parser = new CodeParser(s);
               parser.expression();
         }
         catch(Exception e)
         {
               e.printStackTrace();
         }
     }
 }
 PARSER_END(CodeParser)

 TOKEN:
 {
  <code : ("-")?(["A"-"Z", "0"-"9"])+ >
  | <op : ("+"|"/") >
  | <not : ("-") >
  | <lparenthesis : ("(") >
  | <rparenthesis : (")") >
 }

 void expression() :
 {
 }
 {
  negated_expression() | parenthesis_expression() | LOOKAHEAD(2) operator_expression() | <code>
 }

 void negated_expression() :
 {
 }
 {
       <not>parenthesis_expression()
 }

 void parenthesis_expression() :
 {
 }
 {
        <lparenthesis>expression()<rparenthesis>
 }

 void operator_expression() :
 {
 }
 {
       <code><op>expression()
 }

编辑 - 2009 年 11 月 16 日

现在我试试 ANTLR。

我更改了一些术语以更好地匹配我的问题域。我想出了以下代码(使用本网站上的答案),现在似乎可以完成工作:

grammar Code;

CODE    :   ('A'..'Z'|'0'..'9')+;
OP  :   '+'|'/';

start   :   terms EOF;
terms   :   term (OP term)*;
term    :   '-'? CODE
    |   '-'? '(' terms ')';

顺便说一句... ANTLRWORKS 是用于调试/可视化的绝佳工具!对我帮助很大。

附加信息
上面的代码匹配如下内容:

(-Z19+-Z07+((FV+((M005+(M272/M276))/((M278/M273/M642)+-M005)))/(FW+(M005+(M273/M278/M642)))))+(-Z19+-Z07+((FV+((M005+(M272/M276))/((M278/M273/M642/M651)+-M005)))/(FW+(M0))))

【问题讨论】:

  • 我无法特别提供帮助,但 JavaCC 相当陈旧且顽固。 ANTLR (antlr.org) 仍在积极维护中,并且使用更广泛。
  • 我认为 JavaCC 比 ANTLR 更直接。我看了一下,但也许我应该再试一次 ANTLR。

标签: java parsing antlr parser-generator javacc


【解决方案1】:

kgregory 说的是正确答案。如果您使用 DEBUG_PARSER 选项构建语法然后运行它,您可以看到这一点:

$ javacc -debug_parser -output_directory=com/testing/ CodeParser.jj && javac com/testing/*.java && java -cp 。 com.testing.CodeParser Java Compiler 编译器版本 5.0(解析器生成器) (键入“javacc”,不带参数寻求帮助) 从文件 CodeParser.jj 中读取。 . . 正在重建文件“TokenMgrError.java”。 正在重建文件“ParseException.java”。 正在重建文件“Token.java”。 正在重建文件“SimpleCharStream.java”。 解析器生成成功。 调用:表达式 调用:operator_expression 使用的令牌:: "A" at line 1 column 1> 使用的令牌:: "+" at line 1 column 2> 调用:表达式 调用:operator_expression 使用的令牌:: "BC" at line 1 column 3> 使用的令牌:: "+" at line 1 column 5> 调用:表达式 调用:否定表达式 使用的令牌: 调用:括号表达式 使用的令牌: 调用:表达式 调用:operator_expression 使用的令牌:: "2XXL" at line 1 column 8> 使用的令牌:: "+" at line 1 column 12> 调用:表达式 调用:operator_expression 使用的令牌:: "A" at line 1 column 13> 使用的令牌:: "/" at line 1 column 14> 调用:表达式 使用的令牌:: "-B" at line 1 column 15> 返回:表达式 返回:operator_expression 返回:表达式 返回:operator_expression 返回:表达式 使用的令牌: 返回:括号表达式 返回:否定表达式 返回:表达式 返回:operator_expression 返回:表达式 返回:operator_expression 返回:表达式

看到了吗?最后消耗的令牌是倒数第二个字符 - 倒数第二个右括号。

再次,如果您想要例外,就像 kgregory 所说,您可以添加一个名为“file”或“data”或其他东西的新顶级生产,并以令牌结束。这样任何像这样的悬空括号都会导致错误。这是一个这样做的语法:

选项 { 静态=假; } PARSER_BEGIN(代码解析器) 包 com.testing; 导入 java.io.StringReader; 导入java.io.Reader; 公共类 CodeParser { 公共代码解析器(字符串) { this((Reader)(new StringReader(s))); } 公共静态无效主(字符串参数 []) { 尝试 { /** 字符串有一个左括号,但有两个右括号 --> 应该会产生解析错误 */ 字符串 s = "A+BC+-(2XXL+A/-B))"; CodeParser 解析器 = 新的 CodeParser(s); 解析器.file(); } 捕获(异常 e) { e.printStackTrace(); } } } PARSER_END(代码解析器) 代币: { | | | | } 无效文件():{} { 表达式() } 无效表达式(): { } { 否定表达式() |括号表达式() | LOOKAHEAD(2) 运算符_表达式() | } 无效否定表达式(): { } { 括号表达式() } 无效括号表达式(): { } { 表达式() } 无效操作员表达式(): { } { 表达式() }

还有一个示例运行:

$ javacc -debug_parser -output_directory=com/testing/ CodeParser.jj && javac com/testing/*.java && java -cp 。 com.testing.CodeParser Java Compiler 编译器版本 5.0(解析器生成器) (键入“javacc”,不带参数寻求帮助) 从文件 CodeParser.jj 中读取。 . . 正在重建文件“TokenMgrError.java”。 正在重建文件“ParseException.java”。 正在重建文件“Token.java”。 正在重建文件“SimpleCharStream.java”。 解析器生成成功。 调用:文件 调用:表达式 调用:operator_expression 使用的令牌:: "A" at line 1 column 1> 使用的令牌:: "+" at line 1 column 2> 调用:表达式 调用:operator_expression 使用的令牌:: "BC" at line 1 column 3> 使用的令牌:: "+" at line 1 column 5> 调用:表达式 调用:否定表达式 使用的令牌: 调用:括号表达式 使用的令牌: 调用:表达式 调用:operator_expression 使用的令牌:: "2XXL" at line 1 column 8> 使用的令牌:: "+" at line 1 column 12> 调用:表达式 调用:operator_expression 使用的令牌:: "A" at line 1 column 13> 使用的令牌:: "/" at line 1 column 14> 调用:表达式 使用的令牌:: "-B" at line 1 column 15> 返回:表达式 返回:operator_expression 返回:表达式 返回:operator_expression 返回:表达式 使用的令牌: 返回:括号表达式 返回:否定表达式 返回:表达式 返回:operator_expression 返回:表达式 返回:operator_expression 返回:表达式 返回:文件 com.testing.ParseException:在第 1 行第 18 列遇到“”)“”)“”。 期待: 在 com.testing.CodeParser.generateParseException(CodeParser.java:354) 在 com.testing.CodeParser.jj_consume_token(CodeParser.java:238) 在 com.testing.CodeParser.file(CodeParser.java:34) 在 com.testing.CodeParser.main(CodeParser.java:22)

瞧!一个例外。

【讨论】:

  • 我尝试了 ANTLR 并遇到了同样的问题。使用 ANTLR,我尝试了这个解决方案(添加 EOF)。这解决了问题:-)太好了!谢谢大家!
【解决方案2】:

来自Java CC FAQ

4.7 我添加了一个 LOOKAHEAD 规范,警告消失了;这是否意味着我解决了问题?

没有。如果您使用 LOOKAHEAD 规范,JavaCC 将不会报告选择冲突警告。 没有警告并不意味着你已经正确解决了问题,它只是意味着你添加了一个 LOOKAHEAD 规范

我会首先尝试在不使用前瞻的情况下消除冲突。

【讨论】:

  • 前瞻不是决定使用哪个“东西”吗? <op>expression() 都以相同的标记开头。所以前瞻只是选择正确的“东西”……这东西叫“生产”吗?! :)</op>
  • 使用前瞻意味着您不再收到错误,而不是它实际上已修复。您可以使用&lt;code&gt; (&lt;op&gt; expression() )? 来消除对前瞻的需要吗?
  • 在这种情况下,仍然有两个以 开头的产生式。如果没有前瞻,JavaCC 仍然无法决定选择哪一个,而无需再读取一个令牌。还是我误会了你,你的意思是别的?问候 - 凯
  • &lt;code&gt; 被使用后,解析器查看下一个标记,如果它的&lt;op&gt; 也将使用表达式()。当&lt;code&gt; 已经被使用时,&lt;op&gt; 被解释,所以不需要前瞻。 ( xxx )? 我的意思是“可选 xxx”。
  • 但是使用 是主要问题。为了使用 ,解析器必须决定他是从 expression() 还是 operator_expression() 中选择 。这应该是幻灯片 11 中描述的问题:<a href="/default/index/tourl?u=aHR0cDovL3d3dy5jcy5zanN1LmVkdS9%2BbWFrL2xlY3R1cmVzL0NTMTUzLTA5MTAyOS5wcHQ%3D" rel="nofollow" target="_blank">cs.sjsu.edu/~mak/lectures/CS153-091029.ppt</a> 除此之外,我尝试了您的解决方案以优化整个事情......换一种方式......但我被卡住了。现在我正在看一下ANTLR。 JavaCC 工作,除了上面描述的问题。这就是我尝试 ANTLR 的原因。但是 JavaCC 看起来使用起来更舒服 :)
【解决方案3】:

问题是您在使用解析器时没有收到错误,对吗?并不是解析器生成器声称语法不正确(这似乎是另一个答案中的讨论)。

如果是这种情况,那么我怀疑您看到了问题,因为解析器正确匹配 表达式 产生式,然后忽略后续输入。好久没用JavaCC了,但是iirc并没有因为没有到达end-of-stream而报错。

大多数语法都有一个显式的顶级产生式来匹配整个文件,看起来像这样(我确定语法是错误的,正如我所说,已经很久了):

input : ( expression ) *

或者,如果您只想处理单个表达式,则可能有一个 EOF 标记可供您使用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-13
    • 1970-01-01
    • 2019-02-10
    • 1970-01-01
    • 2013-09-07
    • 1970-01-01
    相关资源
    最近更新 更多