代码之家  ›  专栏  ›  技术社区  ›  java.is.for.desktop

javacc:如何指定在特定上下文中需要哪些令牌?

  •  1
  • java.is.for.desktop  · 技术社区  · 16 年前

    我需要让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”总是首先与 <abcd> <ABC> . 我真的需要上下文感知标记。

    2 回复  |  直到 11 年前
        1
  •  2
  •   das_weezul    16 年前

    好吧,看来你需要一种叫做“向前看”的技术。这里有一个很好的教程: 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> ...

    这是因为解析器没有找到预期的结果。

    我希望这会有帮助。

    干杯!

    注意:解析器不能“在”令牌内,因为令牌是一个终端符号(如果我错了,请纠正我),它不能再被生产规则取代。因此,所有上下文方面都必须放在生产规则(非终端)中,就像我的示例中的“wordcontext”。

        2
  •  1
  •   Laurent Pireyn    15 年前

    你需要使用lexer状态。你的例子变得像:

    <默认标记:{<element1:“element1”>:位于元素1}
    <default>标记:{<element2:“element2”>:在元素2中}
    <in_element1>标记:{<abc:“abc”(…)*>:默认值}
    <in-element2>标记:{<abcd:“abcd”(…)*>:默认值}

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