2010-09-29 5 views
2

J'essaie de faire correspondre les mesures dans le texte d'entrée en anglais, en utilisant Antlr 3.2 et Java1.6. J'ai des règles lexicales comme ce qui suit:Variantes lexème correspondantes avec Antlr3

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

MEASUREMENT 
    : MILLIMETRE | CENTIMETRE | ... ; 

Je voudrais être en mesure d'accepter toute combinaison d'entrée en minuscules et majuscules et - plus important encore - il suffit de retourner un seul jeton lexical pour toutes les variantes de MILLIMÈTRE. Mais pour l'instant, mon AST contient «millimètre», «millimètre», «mm», etc., tout comme dans le texte d'entrée.

Après avoir lu http://www.antlr.org/wiki/pages/viewpage.action?pageId=1802308, je pense que je dois faire quelque chose comme ce qui suit:

tokens { 
    T_MILLIMETRE; 
} 

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

Cependant, quand je le fais, je reçois les erreurs du compilateur suivantes dans le code Java généré par Antlr:

cannot find symbol 
_type = T_MILLIMETRE; 

J'ai essayé ce qui suit à la place:

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

mais alors MEASUREMENT ne correspond plus.

La solution la plus évidente avec une règle de réécriture:

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

provoque une NPE:

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

Faire MESURE en règle de l'analyseur me donne la redoutée « Les définitions symboliques suivantes ne pourra jamais égaler car les jetons antérieurs correspondent à la même erreur "d'entrée".

En créant une règle d'analyseur

measurement : T_MILLIMETRE | ... 

je reçois l'avertissement "pas de règle de lexer correspondant à jeton: T_MILLIMETRE". Antlr fonctionne bien, mais il me donne toujours le texte d'entrée dans l'AST et non pas T_MILLIMETRE. Je ne vois évidemment pas encore le monde comme le fait Antlr. Quelqu'un peut-il me donner des conseils ou des conseils s'il vous plaît?

Steve

Répondre

1

Voici une façon de le faire:

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

Il peut être testé avec la classe suivante:

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); 
    } 
} 

qui produit le fichier DOT suivant:

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 correspond à l'arbre:

alt text

(image créée par http://graph.gafol.net/)

EDIT

Notez que les éléments suivants:

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

toujours imprimer true, quel que soit si le "contenu" du (millimetre er) jetons sont mm, millimetre, millimetres ...

+0

Merci pour votre réponse, Bart. J'étais au courant de cette possibilité. La différence est que j'essaie de résoudre le problème au niveau lexical, alors que vous proposez une règle syntaxique. Votre chemin est probablement la bonne façon Antlr. Mon expérience avec ce problème est que les règles de réécriture ne fonctionnent qu'avec des règles syntaxiques, et non avec des règles lexicales. Je suis en train de résoudre le problème dans ma solution en post-traitant les résultats dans mon code Java, mais je devrais peut-être reconsidérer ce que je fais dans Antlr au niveau lexical et ce que je fais au niveau syntaxique. –

+0

@Stephen, ah d'accord, je vois ce que tu veux dire. Mais dans mon exemple, le type (pour le millimètre) sera toujours 'MilliMeter' (voir mon ** EDIT **). Donc, je ne suis pas tout à fait sûr de ce que vous cherchez. –

+0

Vous m'avez fait réfléchir, Bart. J'abordais le problème de la mauvaise façon. J'essayais de faire une reconnaissance ascendante efficace en rendant l'analyse lexicale sensible au contexte. Cela a signifié que j'ai rapidement atteint les limites de ce qu'Antlr pouvait faire, puisqu'il s'agit d'un outil top-down. J'ai transféré beaucoup de l'analyse dans la syntaxe maintenant (comme dans votre exemple), et tout devient plus facile. Je pense qu'il faut être très conscient de la différence entre les règles lexicales et les règles syntaxiques dans Antlr, même si elles sont très similaires. Tout ce que les règles syntaxiques peuvent faire n'est pas possible avec les règles lexicales. –

0

Notez que fragment règles que « en direct » à l'intérieur du lexer et cessent d'exister dans l'analyseur. Par exemple:

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

avec le texte d'entrée:

"millimeters mm" 

imprimera:

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

en d'autres termes: le type MILLIMETRE n'existe pas, ils sont tous de type MEASUREMENT.