我正在尝试创建一个接受任何字符或数字或几乎任何东西的语法,前提是它的长度等于1。
是否有检查长度的功能?
让我用一个例子来说明问题。 我写了以下代码:
grammar first;
tokens {
SET = 'set';
VAL = 'val';
UND = 'und';
CON = 'con';
ON = 'on';
OFF = 'off';
}
@parser::members {
private boolean inbounds(Token t, int min, int max) {
int n = Integer.parseInt(t.getText());
return n >= min && n <= max;
}
}
parse : SET expr;
expr : VAL('u'('e')?)? String |
UND('e'('r'('l'('i'('n'('e')?)?)?)?)?)? (ON | OFF) |
CON('n'('e'('c'('t')?)?)?)? oneChar
;
CHAR : 'a'..'z';
DIGIT : '0'..'9';
String : (CHAR | DIGIT)+;
dot : .;
oneChar : dot { $dot.text.length() == 1;} ;
Space : (' ' | '\t' | '\r' | '\n') {$channel=HIDDEN;};
我希望我的语法能够做到以下几点:
在我的语法中,我上面列出的所有三个要求都没有正常工作。不知道为什么。
答案 0 :(得分:7)
你的语法中有一些错误和/或不良做法:
以下不是验证谓词:
{$dot.text.length() == 1;}
ANTLR中的正确验证谓词最后有一个问号,内部代码末尾没有半冒号。所以它应该是:
{$dot.text.length() == 1}?
代替。
您不应该处理这些替代命令:
expr
: VAL('u'('e')?)? String
| UND('e'('r'('l'('i'('n'('e')?)?)?)?)?)? (ON | OFF)
| CON('n'('e'('c'('t')?)?)?)? oneChar
;
在解析器规则中。你应该让lexer处理这个。这样的事情会做到:
expr
: VAL String
| UND (ON | OFF)
| CON oneChar
;
// ...
VAL : 'val' ('u' ('e')?)?;
UND : 'und' ( 'e' ( 'r' ( 'l' ( 'i' ( 'n' ( 'e' )?)?)?)?)?)?;
CON : 'con' ( 'n' ( 'e' ( 'c' ( 't' )?)?)?)?;
(另见#5!)
你的词霸规则:
CHAR : 'a'..'z';
DIGIT : '0'..'9';
String : (CHAR | DIGIT)+;
让你的事情变得复杂。词法分析器可以生成三种不同类型的标记:CHAR
,DIGIT
或String
。理想情况下,您应该只创建String
令牌,因为String
已经可以是单CHAR
或DIGIT
。您可以通过在这些规则之前添加fragment
关键字来实现此目的:
fragment CHAR : 'a'..'z' | 'A'..'Z';
fragment DIGIT : '0'..'9';
String : (CHAR | DIGIT)+;
您的令牌流中现在只有CHAR
和DIGIT
令牌,只有String
令牌。简而言之:fragment
规则仅在词法分析器规则中使用,由其他词法分析器规则使用。它们永远不会是自己的标记(并且因此永远不会出现在任何解析器规则中!)。
规则:
dot : .;
不符合您的想法。它匹配“任何标记”,而不是“任何字符”。在词法分析器规则中,.
匹配任何字符,但在解析器规则中,它匹配任何标记。意识到解析器规则只能使用词法分析器创建的标记。
首先根据词法规则对输入源进行标记。在完成之后,解析器(尽管它的解析器规则)可以对这些令牌(不是字符!!!)进行操作。确保你明白这一点! (如果没有,请要求澄清或拿一本关于ANTLR的书)
采用以下语法:
p : . ;
A : 'a' | 'A';
B : 'b' | 'B';
解析器规则p
现在将匹配词法分析器生成的任何标记:这只是A
- 或B
- 标记。因此,p
只能与其中一个字符'a'
,'A'
,'b'
或'B'
匹配。
以下语法:
prs : . ;
FOO : 'a';
BAR : . ;
词法分析器BAR
匹配\u0000 .. \uFFFF
范围内的任何单个字符,但它永远不会匹配字符'a'
,因为词法分析器FOO
在BAR
之前定义{1}}已经规则并捕获此'a'
。解析器规则prs
再次匹配任何令牌,即FOO
或BAR
。
在解析器规则中放置'u'
之类的单个字符会使词法分析器将u
标记为单独的标记:您不希望这样。此外,通过将它们放在解析器规则中,不清楚哪个令牌优先于其他令牌。您应该将所有这些文字保留在解析器规则之外,并使它们成为明确的词法规则。仅在解析器规则中使用词法分析器规则。
所以,不要这样做:
pRule : 'u' ':' String
String : ...
但是:
pRule : U ':' String
U : 'u';
String : ...
您可以将':'
作为词法分析器规则,但这不太重要。 'u'
也可以是String
,因此它必须在<{strong> String
规则之前显示为词法分析规则。
好的,这些是我想到的最明显的事情。基于它们,这是一个建议的语法:
grammar first;
parse
: (SET expr {System.out.println("expr = " + $expr.text);} )+ EOF
;
expr
: VAL String {System.out.print("A :: ");}
| UL (ON | OFF) {System.out.print("B :: ");}
| CON oneChar {System.out.print("C :: ");}
;
oneChar
: String {$String.text.length() == 1}?
;
SET : 'set';
VAL : 'val' ('u' ('e')?)?;
UL : 'und' ( 'e' ( 'r' ( 'l' ( 'i' ( 'n' ( 'e' )?)?)?)?)?)?;
CON : 'con' ( 'n' ( 'e' ( 'c' ( 't' )?)?)?)?;
ON : 'on';
OFF : 'off';
String : (CHAR | DIGIT)+;
fragment CHAR : 'a'..'z' | 'A'..'Z';
fragment DIGIT : '0'..'9';
Space : (' ' | '\t' | '\r' | '\n') {$channel=HIDDEN;};
可以使用以下类进行测试:
import org.antlr.runtime.*;
public class Main {
public static void main(String[] args) throws Exception {
String source =
"set value abc \n" +
"set underli on \n" +
"set conn x \n" +
"set conn xy ";
ANTLRStringStream in = new ANTLRStringStream(source);
firstLexer lexer = new firstLexer(in);
CommonTokenStream tokens = new CommonTokenStream(lexer);
firstParser parser = new firstParser(tokens);
System.out.println("parsing:\n======\n" + source + "\n======");
parser.parse();
}
}
,在生成词法分析器和解析器之后:
java -cp antlr-3.2.jar org.antlr.Tool first.g javac -cp antlr-3.2.jar *.java java -cp .:antlr-3.2.jar Main
打印以下输出:
parsing:
======
set value abc
set underli on
set conn x
set conn xy
======
A :: expr = value abc
B :: expr = underli on
C :: expr = conn x
line 0:-1 rule oneChar failed predicate: {$String.text.length() == 1}?
C :: expr = conn xy
如您所见,最后一个命令C :: expr = conn xy
会产生错误,如预期的那样。