【问题标题】:JavaCC lexer doesn't work as expected (whitespace not ignored)JavaCC 词法分析器无法按预期工作(不忽略空格)
【发布时间】:2013-02-20 11:13:11
【问题描述】:

我正在尝试为下面列出的示例文件实现解析器。我想将它们之间带有'+' 的带引号的字符串识别为单个标记。所以我创建了一个 jj 文件,但它不匹配这样的字符串。我的印象是 JavaCC 应该匹配每个令牌规范的最长匹配。但对我来说似乎不是这样。

我在这里做错了什么?为什么我的<STRING> 令牌与'+' 不匹配,即使它已在其中指定?为什么空白不被忽略?

options {
  TOKEN_FACTORY = "Token";
}

PARSER_BEGIN(Parser)

package com.example.parser;

public class Parser {

  public static void main(String args[]) throws ParseException {

      ParserTokenManager manager = new ParserTokenManager(new SimpleCharStream(Parser.class.getResourceAsStream("example")));
      Token token = manager.getNextToken();
      while (token != null && token.kind != ParserConstants.EOF) {
          System.out.println(token.toString() + "[" + token.kind + "]");
          token = manager.getNextToken();
      }

      Parser parser = new Parser(Parser.class.getResourceAsStream("example"));
      parser.start();
  }

}

PARSER_END(Parser)

// WHITE SPACE
<DEFAULT, IN_STRING_KEYWORD>
SKIP :
{
  " " // <-- skipping spaces
| "\t"
| "\n"
| "\r"
| "\f"
}

// TOKENS
TOKEN :
{
< KEYWORD1 : "keyword1" > : IN_STRING_KEYWORD
}

<IN_STRING_KEYWORD>
TOKEN : {<STRING : <CONCAT_STRING> | <UNQUOTED_STRING> > : DEFAULT 
| <#CONCAT_STRING : <QUOTED_STRING> ("+" <QUOTED_STRING>)+ >
// <-- CONCAT_STRING never matches   "+" part when input is "'smth' +", because whitespace is not ignored!?
| <#QUOTED_STRING : <SINGLEQUOTED_STRING> | <DOUBLEQUOTED_STRING> >
| <#SINGLEQUOTED_STRING : "'" (~["'"])* "'" >
| <#DOUBLEQUOTED_STRING : 
    "\""
      (
        (~["\"", "\\"]) |
        ("\\" ["n", "t", "\"", "\\"])
      )* 
    "\""
  >
| <#UNQUOTED_STRING : (~[" ","\t", ";", "{", "}", "/", "*", "'", "\"", "\n", "\r"] | "/" ~["/", "*"] | "*" ~["/"])+ >
}

void start() :
{}
{
  (<KEYWORD1><STRING>";")+ <EOF>
}

这是一个应该被解析的示例文件:

keyword1 "foo" + ' bar';

我想将第一个 keyword1 的参数匹配为单个 &lt;STRING&gt; 令牌。

当前输出:

keyword1[6]
Exception in thread "main" com.example.parser.TokenMgrError: Lexical error at line 1, column 15.  Encountered: " " (32), after : "\"foo\""
    at com.example.parser.ParserTokenManager.getNextToken(ParserTokenManager.java:616)
    at com.example.parser.Parser.main(Parser.java:12)

我正在使用 JavaCC 5.0。

【问题讨论】:

  • 这似乎是 unanswered question 的副本。仍然会很感激一个答案。或者如果这是一个错误的解决方法。

标签: java parsing javacc


【解决方案1】:

STRING 正在扩展到可以匹配的最长序列,即错误指示的"foo"。右双引号后的空格不是私有令牌CONCAT_STRING 定义的一部分。跳过标记不适用于其他标记的定义,因此您必须将空格直接合并到定义中,在 + 的任一侧。

顺便说一句,我建议有一个像这样的最终令牌定义:

<each-state-in-which-the-empty-string-cannot-be-recognized>
TOKEN : {
    < ILLEGAL : ~[] >
}

这可以防止TokenMgrErrors 被抛出并使调试更容易一些。

【讨论】:

  • 你是说每当定义一个新令牌时,我不应该期望通过 SKIP 自动处理其中的空格?如果我不能在我的作品中利用它,那么 SKIP 的目的是什么...您能否将我介绍给 JavaCC 文档中说明您所声称内容的部分?
  • @predi:没错,跳过标记不适用于其他标记的定义。跳过标记的目的是定义在匹配 BNF 规则时应该忽略的标记(有效地在其他标记之间,但不在其他标记内)。但是,您可以将您的跳过标记合并到其他标记的定义中,就像您可以使用任何其他标记一样。例如,为您的跳过令牌命名,例如 WS。然后将CONCAT_STRING重新定义为... (&lt;WS&gt;)? ("+" (&lt;WS&gt; ...)+
  • 糟糕,应该是... (&lt;WS&gt;)? ("+" (&lt;WS&gt;)? ...)+。忘记了一些元字符。
  • 嗯.. 现在我想起来了。仅跳过“顶级”标记周围的空格是有道理的。毕竟,在我的情况下,在引号内时,所有其他空格可能很重要(我什至发布了一个示例)。 JavaCC FAQ 3.10 暗示可能会或可能不会应用 SKIP。但没有提及何时何地。我接受这个答案。
  • Predi:最好不要考虑“顶级”令牌。所有代币都处于同一级别。 “私有正则表达式”的名称(带有#s 的那些)实际上只是宏。选择是否应用跳过的令牌定义的规则与常规令牌的规则相同,并在前面的常见问题解答中介绍。
猜你喜欢
  • 2017-12-24
  • 1970-01-01
  • 2023-03-17
  • 2012-04-20
  • 1970-01-01
  • 2012-12-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多