1

我需要让 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"它时,它会将其识别为<abc>,但是当它的“内部”一个名为的标记时"element2",它会将相同的字符串识别为<abcd>.

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

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

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

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

4

2 回答 2

2

好的,看来您需要一种称为前瞻的技术。这是一个非常好的教程: 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> ...

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

我希望这会有所帮助。

干杯!

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

于 2010-05-05T11:47:55.187 回答
1

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

<DEFAULT> TOKEN:{ <ELEMENT1: "element1">: IN_ELEMENT1 }
<DEFAULT> TOKEN: { <ELEMENT2: "element2">: IN_ELEMENT2 }
<IN_ELEMENT1> TOKEN: { <ABC: "abc" (...)* >:默认 }
<IN_ELEMENT2> 令牌:{ <ABCD:“abcd”(...)*>:默认 }

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

于 2011-02-08T17:05:27.580 回答