2010-04-29 66 views
1

我需要讓JavaCC知道上下文(當前父令牌),並根據該上下文期望發生不同的令牌。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>匹配。我真的需要上下文感知令牌。

+0

我發現,'JavaCC'允許指定詞法上下文。可悲的是,這使得一切都變得非常複雜,當你有許多令牌時,他們大多數需要自己的狀態。我已經開發了一個確定性狀態機解析器,並且目前正在增強它以接受非確定性狀態機。我必須承認,我的情況非常特別。 – 2010-05-05 18:14:59

回答

2

好吧,看來你需要一種叫做lookahead的技巧。這是一個非常好的教程: Lookahead tutorial

我的第一次嘗試是錯誤的,但因爲它適用於定義上下文的不同標記,所以我會將其留在此處(可能對某人有用; o))。


假設我們想要某種標記語言。我們所想 「標記」 是:

  • 表達式由字母(ABC ... ... ZABC Z)和空格 - >詞
  • 表達式由數字(0-9) - > numbers

我們希望將標籤和數字中的單詞括在標籤中。所以,如果我對你說得對,那就是你想要做的事情:如果你在單詞上下文中(單詞標籤之間),編譯器應該期望字母和空格,在它所期望的數字上下文中。

我創建的文件WordNumber.jj定義的語法和解析器生成:

<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> 

的最後一行:

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>" 
} 

你可以用這樣一個文件來測試它導致解析器拋出如下異常:

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」。

+0

非常感謝您的示例,但這不是我的問題所在。如果使用的令牌是可區分的,那麼根本不存在任何問題(其中包含在' ...'中,另一個包含在' ...'中)。與此相反,在我的情況下,我有令牌,它們都會匹配某些輸入。 – 2010-05-05 18:11:46

+0

@ java.is.for.desktop: 好的,對不起。我認爲你可以使用「向前看」。檢查我的編輯後的鏈接; o) – 2010-05-05 20:49:33

1

您需要使用詞法分析器狀態。你的榜樣變成類似:

<DEFAULT> TOKEN:{<部件1: 「部件1」 >:IN_ELEMENT1}
<DEFAULT> TOKEN:{<元素2: 「在element2」 >:IN_ELEMENT2}
<IN_ELEMENT1> TOKEN:{< ABC: 「ABC」(...)* >:DEFAULT}
<IN_ELEMENT2> TOKEN:{< ABCD: 「ABCD」(...)* >:DEFAULT}

請注意,(...)*不正確的JavaCC語法,但你的例子不是非此即彼,所以我只能猜測。