2010-09-29 32 views
2

我試圖匹配英文輸入文本中的測量,使用Antlr 3.2和Java1.6。我有詞法規則如下所示:Antlr3匹配lexeme變體

fragment 
MILLIMETRE 
    : 'millimetre' | 'millimetres' 
    | 'millimeter' | 'millimeters' 
    | 'mm' 
    ; 

MEASUREMENT 
    : MILLIMETRE | CENTIMETRE | ... ; 

我希望能接受大寫和小寫輸入的任意組合 - 更重要的是 - 只返回一個單一的詞彙令牌的所有變種毫米。但目前,我的AST包含「毫米」,「毫米」,「毫米」等,就像在輸入文本中一樣。

閱讀http://www.antlr.org/wiki/pages/viewpage.action?pageId=1802308後,我想我需要做類似如下:

tokens { 
    T_MILLIMETRE; 
} 

fragment 
MILLIMETRE 
    : ('millimetre' | 'millimetres' 
    | 'millimeter' | 'millimeters' 
    | 'mm') { $type = T_MILLIMETRE; } 
    ; 

然而,當我這樣做,我得到了ANTLR的生成的Java代碼以下編譯器錯誤:

cannot find symbol 
_type = T_MILLIMETRE; 

我嘗試以下代替:

MEASUREMENT 
    : MILLIMETRE { $type = T_MILLIMETRE; } 
    | ... 

但之後MEASUREMENT不再匹配。

與重寫規則更明顯的解決方案:

MEASUREMENT 
    : MILLIMETRE -> ^(T_MILLIMETRE MILLIMETRE) 
    | ... 

導致NPE:

java.lang.NullPointerException at org.antlr.grammar.v2.DefineGrammarItemsWalker.alternative(DefineGrammarItemsWalker.java:1555). 

使測量到解析器規則給了我可怕的「下面的標記定義不能匹配因爲之前的令牌匹配相同的輸入「錯誤。

通過創建一個解析器規則

measurement : T_MILLIMETRE | ... 

我得到警告「對應令牌的詞法規則:T_MILLIMETRE」。雖然Antlr運行,但它仍然給我在AST中的輸入文本,而不是T_MILLIMETRE。

我顯然還沒有像Antlr那樣看世界。任何人都可以給我任何提示或建議嗎?

史蒂夫

回答

1

這裏有一個辦法做到這一點:

grammar Measurement; 

options { 
    output=AST; 
} 

tokens { 
    ROOT; 
    MM; 
    CM; 
} 

parse 
    : measurement+ EOF -> ^(ROOT measurement+) 
    ; 

measurement 
    : Number MilliMeter -> ^(MM Number) 
    | Number CentiMeter -> ^(CM Number) 
    ; 

Number 
    : '0'..'9'+ 
    ; 

MilliMeter 
    : 'millimetre' 
    | 'millimetres' 
    | 'millimeter' 
    | 'millimeters' 
    | 'mm' 
    ; 

CentiMeter 
    : 'centimetre' 
    | 'centimetres' 
    | 'centimeter' 
    | 'centimeters' 
    | 'cm' 
    ; 

Space 
    : (' ' | '\t' | '\r' | '\n'){$channel=HIDDEN;} 
    ; 

它可以與下面的類測試:

import org.antlr.runtime.*; 
import org.antlr.runtime.tree.*; 
import org.antlr.stringtemplate.*; 

public class Main { 
    public static void main(String[] args) throws Exception { 
     ANTLRStringStream in = new ANTLRStringStream("12 millimeters 3 mm 456 cm"); 
     MeasurementLexer lexer = new MeasurementLexer(in); 
     CommonTokenStream tokens = new CommonTokenStream(lexer); 
     MeasurementParser parser = new MeasurementParser(tokens); 
     MeasurementParser.parse_return returnValue = parser.parse(); 
     CommonTree tree = (CommonTree)returnValue.getTree(); 
     DOTTreeGenerator gen = new DOTTreeGenerator(); 
     StringTemplate st = gen.toDOT(tree); 
     System.out.println(st); 
    } 
} 

產生以下DOT文件:

digraph { 

    ordering=out; 
    ranksep=.4; 
    bgcolor="lightgrey"; node [shape=box, fixedsize=false, fontsize=12, fontname="Helvetica-bold", fontcolor="blue" 
     width=.25, height=.25, color="black", fillcolor="white", style="filled, solid, bold"]; 
    edge [arrowsize=.5, color="black", style="bold"] 

    n0 [label="ROOT"]; 
    n1 [label="MM"]; 
    n1 [label="MM"]; 
    n2 [label="12"]; 
    n3 [label="MM"]; 
    n3 [label="MM"]; 
    n4 [label="3"]; 
    n5 [label="CM"]; 
    n5 [label="CM"]; 
    n6 [label="456"]; 

    n0 -> n1 // "ROOT" -> "MM" 
    n1 -> n2 // "MM" -> "12" 
    n0 -> n3 // "ROOT" -> "MM" 
    n3 -> n4 // "MM" -> "3" 
    n0 -> n5 // "ROOT" -> "CM" 
    n5 -> n6 // "CM" -> "456" 

} 

whic H對應於樹:

alt text

(圖片由http://graph.gafol.net/創建)

編輯

注意以下幾點:

measurement 
    : Number m=MilliMeter {System.out.println($m.getType() == MeasurementParser.MilliMeter);} 
    | Number CentiMeter 
    ; 

將始終打印true,無論如果「毫米」的「內容」 ER)令牌mmmillimetremillimetres,...

+0

感謝您的迴應,Bart。我意識到這種可能性。不同之處在於我試圖在詞彙層面解決問題,而您提出了一個語法規則。你的方式可能是正確的Antlr方式。我對這個問題的經驗是重寫規則只適用於句法規則,而不適用於詞法規則。我現在通過在Java代碼中對結果進行後處理來解決問題,但是我應該重新考慮我在詞彙層面所做的工作以及我在語法層面所做的工作。 – 2010-09-30 14:09:33

+0

@Stephen,好吧,我明白你的意思了。但在我的例子中,類型(毫米)總是「MilliMeter」(參見我的**編輯**)。所以我不完全確定你在做什麼。 – 2010-09-30 14:20:19

+0

你讓我覺得,巴特。我以錯誤的方式接近了這個問題。我試圖通過對詞法分析進行上下文敏感來有效地進行自下而上的認識。這意味着我很快達到了Antlr能夠做到的極限,因爲它是一種自上而下的工具。現在我已經將很多分析轉移到了語法中(比如在你的例子中),並且一切都變得更加容易。我認爲人們必須非常清楚Antlr中的詞法規則和語法規則之間的差別,即使它們看起來非常相似。並非所有句法規則都可以用詞彙表達。 – 2010-10-01 15:12:34

0

注意fragment規則只「活」的詞法分析器內停止在解析器存在。例如:

grammar Measurement; 

options { 
    output=AST; 
} 

parse 
    : (m=MEASUREMENT { 
     String contents = $m.text; 
     boolean isMeasurementType = $m.getType() == MeasurementParser.MEASUREMENT; 
     System.out.println("contents="+contents+", isMeasurementType="+isMeasurementType); 
    })+ EOF 
    ; 

MEASUREMENT 
    : MILLIMETRE 
    ; 

fragment 
MILLIMETRE 
    : 'millimetre' 
    | 'millimetres' 
    | 'millimeter' 
    | 'millimeters' 
    | 'mm' 
    ; 

SPACE 
    : (' ' | '\t' | '\r' | '\n'){$channel=HIDDEN;} 
    ; 

與輸入文本:

"millimeters mm" 

會打印:

contents=millimeters, isMeasurementType=true 
contents=mm, isMeasurementType=true 

換句話說:類型MILLIMETRE不存在,他們都MEASUREMENT類型。