我正在尝试使用Antlr 3.2和Java1.6匹配英文输入文本中的测量值。我有类似以下的词法规则:
fragment
MILLIMETRE
: 'millimetre' | 'millimetres'
| 'millimeter' | 'millimeters'
| 'mm'
;
MEASUREMENT
: MILLIMETRE | CENTIMETRE | ... ;
我希望能够接受大写和小写输入的任意组合,更重要的是,只需为MILLIMETER的所有变体返回单个词法标记。但目前,我的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设置为解析器规则给了我可怕的“以下令牌定义永远不能匹配,因为先前的令牌匹配相同的输入”错误。
创建解析器规则
measurement : T_MILLIMETRE | ...
我收到警告“没有与令牌对应的词法分析器规则:T_MILLIMETRE”。虽然Antlr运行,但它仍然在AST中提供输入文本而不是T_MILLIMETRE。
我显然还没有像Antlr那样看世界。请问有人给我任何提示或建议吗?
史蒂夫
答案 0 :(得分: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
,millimetre
,...... ,始终会打印millimetres
答案 1 :(得分: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
类型。