我正在尝试使用 ANTLR 和 Java 创建一个编译器。我有这个问题,我有一个规则,但我不能只使用它的一部分。我有一个命令,例如0:HALT 0,0,0 之后我想忽略其他一切。
例如 0:HALT 0,0,0 blah blah blah,我想忽略 blah blah blah
我的规则是:
rule returns [String value]
:
INTEGER':' ro=rocommand i1=INTEGER',' i2=INTEGER ',' i3=INTEGER rest {$value = $ro.text+" "+$i1.text+","+$i2.text+","+$i3.text; }
| INTEGER':' rm=rmcommand j1=INTEGER ',' j2=INTEGER '('j3=INTEGER')' rest {$value = $rm.text+" "+$j1.text+","+$j2.text+"("+$j3.text+")"; }
;
我的代码是:
CharStream charStream = new ANTLRStringStream(strLine);
simulatorLexer lexer = new simulatorLexer(charStream);
TokenStream tokenStream = new CommonTokenStream(lexer);
simulatorParser parser = new simulatorParser(tokenStream);
System.out.println(parser.rule());
我得到的是:
0: rule:IN 0,0,0
1: rule:LDC 1,1,0
line 1:15 no viable alternative at character 'r'
line 1:18 no viable alternative at character '='
line 1:15 no viable alternative at character 'i'
正文:
0: rule:IN 0,0,0
1: rule:LDC 1,1,0 r1=0
所以它应该正确解析第一行和第二行直到 0。然后它应该忽略 r1=0。到目前为止,它工作正常,但它显示了一些错误,我想摆脱它们。请帮助我!
编辑
我发布了整个语法,以便您能更好地帮助我。我只想识别规则部分。
program:
rule+
;
rocommand:
'HALT'|'IN'|'OUT'|'ADD'|'SUB'|'MUL'|'DIV'|'LDC'
;
rmcommand:
'LD'|'LDA'|'LDC'|'ST'|'JLT'|'JLE'|'JGE'|'JGT'|'JEQ'|'JNE'
;
rest:
~('\n'|'\r')* '\r'? ('\n'|EOF)
;
rule returns [String value]
:
INTEGER':' ro=rocommand i1=INTEGER',' i2=INTEGER ',' i3=INTEGER rest {$value = $ro.text+" "+$i1.text+","+$i2.text+","+$i3.text; }
| INTEGER':' rm=rmcommand j1=INTEGER ',' j2=INTEGER '('j3=INTEGER')' rest {$value = $rm.text+" "+$j1.text+","+$j2.text+"("+$j3.text+")"; }
;
WS : (' '|'\r'|'\t'|'\u000C'|'\n') {$channel=HIDDEN;};
INTEGER : '0'..'9'+;
IGNORELINE : '*' ~('\n'|'\r')* '\r'? '\n' {$channel=HIDDEN;};
最佳答案
这条规则有几个问题:
rest:
~('\n'|'\r')* '\r'? ('\n'|EOF)
;
在解析器规则中,~
否定词法分析器产生的整个标记集。所以 ~('\n'|'\r')
不 不 匹配除 '\n'
或 之外的单个字符>'\r'
。它匹配除匹配 \r
或 \n
的标记之外的任何标记。
此外,由于您的词法分析器将 '\n'
和 '\r'
放在隐藏 channel 上,因此这些标记在您的解析器中将不可用。这意味着永远无法匹配 rest
规则中的 '\n'
。
简而言之:您不能“告诉”您的解析器一行的结尾是什么,因为这些字符会被您的 WS
规则丢弃。这意味着您无法正确编写这样的 rest
解析器规则。
供您输入:
0: IN 0,0,0
1: LDC 1,1,0 r1=0
(注意我删除了 'rule:'
's)
以下标记由您的词法分析器生成:
token[type=INTEGER text='0']
token[type=':' text=':']
token[type='IN' text='IN']
token[type=INTEGER text='0']
token[type=',' text=',']
token[type=INTEGER text='0']
token[type=',' text=',']
token[type=INTEGER text='0']
token[type=INTEGER text='1']
token[type=':' text=':']
token[type='LDC' text='LDC']
token[type=INTEGER text='1']
token[type=',' text=',']
token[type=INTEGER text='1']
token[type=',' text=',']
token[type=INTEGER text='0']
token[type=INTEGER text='1']
token[type=INTEGER text='0']
所以这些是您的解析器规则中可用的标记。
请注意,以下两个字符:'='
和 'r'
无法被词法分析器匹配,您可以通过查看错误看到:
line 2:13 no viable alternative at character 'r'
line 2:15 no viable alternative at character '='
一个可能的解决方案是创建一个匹配整数和冒号的词法分析器规则:
START : INTEGER ':';
并让您的规则
以此标记开始:
rule
: START ro=rocommand i1=INTEGER ',' i2=INTEGER ',' i3=INTEGER rest ...
| ...
;
这样,您的rest
可以匹配除 START
token 之外的零个或多个 token :
rest
: ~START*
;
要捕获 '='
和 'r'
字符,请创建一个 ANY
规则并将此规则放在您的末尾词法分析器规则:
ANY : . ; // match any char
这样,解析器将创建以下解析树:
另一种解决方案是创建一个 LINE_BREAK
token :
LINE_BREAK : '\r'? '\n' | '\r';
(当然是从 WS
中删除 \r
和 \n
!)
然后做这样的事情:
rule
: INTEGER ':' ro=rocommand i1=INTEGER ',' i2=INTEGER ',' i3=INTEGER rest LINE_BREAK ...
| ...
;
rest
: ~LINE_BREAK*
;
关于Java ANTLR 如何忽略部分规则?忽略子规则后的部分,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12114429/