3

Antlr 3.2とJava1.6を使用して、英語の入力テキストの測定値を一致させようとしています。次のような字句規則があります。

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

MEASUREMENT
    :   MILLIMETRE | CENTIMETRE | ... ;

大文字と小文字の入力の任意の組み合わせを受け入れ、さらに重要なことに、MILLIMETREのすべてのバリアントに対して単一の字句トークンを返すことができるようにしたいと思います。しかし、現時点では、私のASTには、入力テキストと同じように、「ミリメートル」、「ミリメートル」、「mm​​」などが含まれています。

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をパーサールールにすると、「以前のトークンが同じ入力に一致するため、次のトークン定義は一致しません」という恐ろしいエラーが発生します。

パーサールールを作成する

measurement :  T_MILLIMETRE | ...

「トークンに対応するレクサールールがありません:T_MILLIMETRE」という警告が表示されます。Antlrは実行されますが、T_MILLIMETREではなくASTの入力テキストが表示されます。

私は明らかに、Antlrのように世界をまだ見ていません。誰かが私に何かヒントやアドバイスを教えてもらえますか?

スティーブ

4

2 に答える 2

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"

}

これは次のツリーに対応します。

代替テキスト

( http://graph.gafol.net/で作成された画像)

編集

次の点に注意してください。

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

true(ミリメートル) トークンの「内容」mmが、、、...millimetremillimetres

于 2010-09-29T11:08:40.417 に答える
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は存在しません。それらはすべて typeMEASUREMENTです。

于 2010-09-30T14:34:57.597 に答える