2010-09-29 3 views
2

Я пытаюсь сопоставлять измерения в текстовом тексте на английском языке, используя Antlr 3.2 и Java1.6. У меня есть лексические правила, как следующее:Соответствующие варианты лексемы с Antlr3

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

MEASUREMENT 
    : MILLIMETRE | CENTIMETRE | ... ; 

Я хотел бы быть в состоянии принять любую комбинацию прописных и строчных ввода и - что более важно - просто возвращают один лексический маркер для всех вариантов миллиметр. Но на данный момент мой АСТ содержит «миллиметр», «миллиметры», «мм» и т. Д., Как и во входном тексте.

После прочтения http://www.antlr.org/wiki/pages/viewpage.action?pageId=1802308, я думаю, что мне нужно сделать что-то вроде следующего:

tokens { 
    T_MILLIMETRE; 
} 

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

Однако, когда я делаю это, я получаю следующие ошибки компиляции в коде Java, порожденного Antlr:

cannot find symbol 
_type = T_MILLIMETRE; 

Я попробовал следующий вместо:

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

но затем ИЗМЕРЕНИЕ больше не сопоставляется.

Более очевидное решение с правилом подстановки:

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 ч соответствует дереву:

alt text

(изображения, созданного http://graph.gafol.net/)

EDIT

Обратите внимание, что следующие:

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

всегда будет печатать true, независимо если «содержимое» (миллиметр) эр) лексемы mm, millimetre, millimetres ...

+0

Спасибо за ваш ответ, Барт. Я знал об этой возможности. Разница в том, что я пытаюсь решить проблему на лексическом уровне, тогда как вы предлагаете синтаксическое правило. Ваш путь, по-видимому, правильный путь Antlr. Мой опыт в этой проблеме заключается в том, что правила перезаписи работают только с синтаксическими правилами, а не с лексическими правилами. Я решаю проблему в своем решении на данный момент, после обработки результатов в моем Java-коде, но я, возможно, должен пересмотреть то, что я делаю в Antlr на лексическом уровне, и то, что я делаю на синтаксическом уровне. –

+0

@ Стефен, хорошо, я понимаю, что вы имеете в виду. Но в моем примере тип (для миллиметра) всегда будет «MilliMeter» (см. Мой ** EDIT **). Поэтому я не совсем уверен, что тебе нужно. –

+0

Ты заставил меня думать, Барт. Я неправильно подходил к проблеме. Я пытался эффективно распознавать снизу вверх, делая лексический анализ контекстно-зависимым. Это означало, что я быстро достиг предела того, что мог сделать Antlr, поскольку это инструмент сверху вниз. Теперь я переместил много анализа в синтаксис (например, в вашем примере), и все становится проще. Я думаю, что нужно очень хорошо понимать разницу между лексическими правилами и синтаксическими правилами в Antlr, даже если они выглядят очень похожими. Не все, что синтаксические правила могут делать, возможно с лексическими. –

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.

Смежные вопросы