【问题标题】:JavaCC: How can I specify which token(s) are expected in certain context?JavaCC:如何指定在特定上下文中需要哪些令牌?
【发布时间】:2010-04-29 06:40:15
【问题描述】:

我需要让 JavaCC 了解上下文(当前父令牌),并且根据该上下文,预计会出现不同的令牌。

考虑以下伪代码:

TOKEN <abc> { "abc*" } // recognizes "abc", "abcd", "abcde", ...
TOKEN <abcd> { "abcd*" } // recognizes "abcd", "abcde", "abcdef", ...

TOKEN <element1> { "element1" "[" expectOnly(<abc>) "]" }
TOKEN <element2> { "element2" "[" expectOnly(<abcd>) "]" }
...

因此,当生成的解析器在“内部”一个名为 "element1" 的令牌并且遇到 "abcdef" 时,它会将其识别为 &lt;abc&gt;,但是当它“内部”一个名为 "element2" 的令牌时,它会识别与&lt;abcd&gt;.

element1 [ abcdef ] // aha! it can only be <abc>
element2 [ abcdef ] // aha! it can only be <abcd>

如果我没记错的话,它的行为类似于更复杂的 XML 文件的 DTD 定义。

那么,如何指定在哪个“上下文”中哪个令牌是有效/预期的?

注意:对于我的实际案例来说,定义一种令牌的“层次结构”是不够的,因此“abcdef”总是首先与&lt;abcd&gt; 匹配,而不是&lt;abc&gt; .我真的需要上下文感知令牌。

【问题讨论】:

  • 我发现JavaCC 允许指定词法上下文。可悲的是,当您有 许多 令牌而其中大多数都需要 自己的 状态时,这会使一切变得非常复杂。我已经开发了一个确定性状态机解析器,目前正在对其进行增强以接受非确定性状态机。我必须承认,我的情况很特别。

标签: java parsing token javacc


【解决方案1】:

好的,看来您需要一种称为前瞻的技术。这是一个非常好的教程: Lookahead tutorial

那时我的第一次尝试是错误的,但由于它适用于定义上下文的不同标记,我将把它留在这里(也许它对某人有用;o))。


假设我们想要某种标记语言。我们想要“标记”的只是:

  • 由字母 (abc...zABC...Z) 和空格组成的表达式 --> 单词
  • 由数字 (0-9) 组成的表达式 --> 数字

我们希望将单词包含在标签中,将数字包含在标签中。因此,如果我说对了,这就是您想要做的:如果您在单词上下文中(在单词标签之间),编译器应该期望字母和空格,在数字上下文中它期望数字。

我创建了 WordNumber.jj 文件,它定义了要生成的语法和解析器:

options
{
    LOOKAHEAD= 1;

    CHOICE_AMBIGUITY_CHECK = 2;
    OTHER_AMBIGUITY_CHECK = 1;
    STATIC = true;
    DEBUG_PARSER = false;
    DEBUG_LOOKAHEAD = false;
    DEBUG_TOKEN_MANAGER = false;
    ERROR_REPORTING = true;
    JAVA_UNICODE_ESCAPE = false;
    UNICODE_INPUT = false;
    IGNORE_CASE = false;
    USER_TOKEN_MANAGER = false;
    USER_CHAR_STREAM = false;
    BUILD_PARSER = true;
    BUILD_TOKEN_MANAGER = true;
    SANITY_CHECK = true;
    FORCE_LA_CHECK = false;
}

PARSER_BEGIN(WordNumberParser)

/** Model-tree Parser */
public class WordNumberParser
{
    /** Main entry point. */
    public static void main(String args []) throws ParseException
    {
        WordNumberParser parser = new WordNumberParser(System.in);
        parser.Input();
    }
}

PARSER_END(WordNumberParser)

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

TOKEN :
{
    < WORD_TOKEN : (["a"-"z"] | ["A"-"Z"] | " " | "." | ",")+ > |
    < NUMBER_TOKEN : (["0"-"9"])+ >
}


/** Root production. */
void Input() :
{}
{
    ( WordContext() | NumberContext() )* < EOF >
}

/** WordContext production. */
void WordContext() :
{}
{
    "<WORDS>" (< WORD_TOKEN >)+ "</WORDS>"
}

/** NumberContext production. */
void NumberContext() :
{}
{
    "<NUMBER>" (< NUMBER_TOKEN >)+ "</NUMBER>"
}

你可以用这样的文件来测试它:

<WORDS>This is a sentence. As you can see the parser accepts it.</WORDS>
<WORDS>The answer to life, universe and everything is</WORDS><NUMBER>42</NUMBER>
<NUMBER>This sentence will make the parser sad. Do not make the parser sad.</NUMBER>

最后一行将导致解析器抛出如下异常:

Exception in thread "main" ParseException: Encountered " <WORD_TOKEN> "This sentence will make the parser sad. Do not make the parser sad. "" at line 3, column 9. Was expecting: <NUMBER_TOKEN> ...

那是因为解析器没有找到它所期望的。

希望对你有帮助。

干杯!

P.S.:解析器不能“存在”在令牌内,因为令牌是一个终端符号(如果我错了,请纠正我),它不能被生产规则进一步替换。因此,所有上下文方面都必须放在生产规则(非终端)中,例如我的示例中的“WordContext”。

【讨论】:

  • 非常感谢您提供的示例,但这不是我的问题。当使用的令牌可以区分时完全没有问题,就像您的情况一样(一个包含在&lt;NUMBER&gt;...&lt;/NUMBER&gt; 中,另一个包含在&lt;WORD&gt;...&lt;/WORD&gt; 中)。与此相反,在我的例子中,我有 both 匹配某些输入的标记。
  • @java.is.for.desktop:好的,抱歉。我认为你可以使用“前瞻”。检查我编辑的帖子以获取链接;o)
【解决方案2】:

您需要使用词法分析器状态。你的例子变成了这样:

令牌:{ : IN_ELEMENT1 }
TOKEN: { : IN_ELEMENT2 }
TOKEN: { : DEFAULT }
TOKEN: { : DEFAULT }

请注意,(...)* 不是正确的 JavaCC 语法,但您的示例也不是,所以我只能猜测。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-08-29
    • 1970-01-01
    • 2015-09-16
    • 1970-01-01
    • 2022-11-25
    • 2016-03-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多