所以我的语法有些问题,我似乎无法弄明白。我不确定问题究竟是什么或它在哪里,但我会尽力解释发生了什么。
就像一点背景,这样语法可能会更有意义,这里的目标是采用伪abc-notation编写的输入程序,并使用java库jMusic播放它。
另外,值得注意的是我使用的是自定义AST代码,而不是ANTLR内置的AST构建器。
在解析'header'中的'fields'时,问题似乎首先发生,但我也尝试完全从语法中删除标题,试图缩小可能出现问题的地方,但是我遇到了类似的问题当试图解析“笔记”时,我觉得这里有一个更普遍的问题。
这是我的语法:
grammar simpleABC;
// grammar rules
options{
backtrack = true;
}
@members{
// override the default error reporting functions
public void reportError(RecognitionException e) {
// call the Parser member function to report the error
displayRecognitionError(this.getTokenNames(), e);
// exit with error
System.exit(1);
}
}
program returns [AST ast]
: { $ast = new Program(); }
(header {$ast.addAST($header.ast);})
(score {$ast.addAST($score.ast);})
;
header returns [Header ast]
: 'header' '{'
(field {$ast.addAST($field.ast);})+
'}'
;
field returns [Field ast]
: 'X:' x=NUM { $ast = (new ReferenceNumber($x.text)); }
| 'T:' t=STRING { $ast = (new Title($t.text)); }
| 'C:' c=STRING { $ast = (new Composer($c.text)); }
| 'M:' (b=NUM '/' l=NUM) { $ast = (new Meter($b.text, $l.text)); }
| 'M:' 'C' { $ast = (new Meter("4", "4")); }
| 'K:' k=PITCH { $ast = (new Key($k.text)); }
| 'L:' l=NUM { $ast = (new Length($l.text)); }
| 'L:' '/' l=NUM { $ast = (new Length("/" + $l.text)); }
| 'tempo:' tempo=NUM { $ast = (new Tempo($tempo.text)); }
;
score returns [Score ast]
: 'score' '{'
(part {$ast.addAST($part.ast);})+
'}'
;
part returns [Part ast]
: instrument=STRING id=VAR '{' {$ast = new Part($instrument.text, $id.text);}
(phrase {$ast.addAST($phrase.ast);})+
'}'
;
phrase returns [Phrase ast]
: '|' (b1=bar {$ast.addAST($b1.ast);} '|')+ // typical bar
/* | '|:' (b2=bar {$ast.addAST($b2.ast);} '|')+ (b3=bar {$ast.addAST($b3.ast);} ':|') // single repeat
| '|:' (b4=bar {$ast.addAST($b4.ast);} '|')+ // multi-ending repeat
'1' (b5=bar {$ast.addAST($b5.ast);} )+ ':|' // 1st ending
'2' (b6=bar {$ast.addAST($b6.ast);} )+ // 2nd ending
TODO: fix repeats
*/
;
bar returns [Bar ast]
: (note {$ast.addAST($note.ast);})+
;
note returns [Note ast]
: pitch=PITCH length=NUM { $ast = new Note($pitch.text, $length.text); }
| pitch=PITCH { $ast = new Note($pitch.text); }
;
// lexical analysis stuff
NUM : ('0'..'9')+
;
VAR : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*
;
PITCH : (('A'..'G') | ('a'..'g')) (',' | '\'')?
;
COMMENT : '//' ~('\n'|'\r')* '\r'? '\n' {$channel=HIDDEN;}
;
WS : ( ' ' | '\t' | '\r' | '\n' ) {$channel=HIDDEN;}
;
STRING : '"' ( ESC_SEQ | ~('\\'|'"') )* '"'
;
fragment
ESC_SEQ : '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\')
;
这是我一直使用的简单输入程序:
header {
X:1
T:"Notes"
M:C
K:C
L:1/4
}
score {
PIANO pianoPart {
|C, D, E, F,|G, A, B, C|D E F G|A B c d| e f g a|b c' d' e'|f' g' a' b'|
}
}
当我运行此代码时,出现以下错误:
Exception in thread "main" java.lang.NullPointerException
at simpleABCParser.header(simpleABCParser.java:151)
at simpleABCParser.program(simpleABCParser.java:87)
at simpleABCParser.main(simpleABC.java:32)
好的,所以它告诉我解析器中有一个空指针异常。以下是这个空指针发生的位置。
// $ANTLR start "header"
// simpleABC.g:25:1: header returns [Header ast] : 'header' '{' ( field )+ '}' ;
public final Header header() throws RecognitionException {
Header ast = null;
Field field3 =null;
try {
// simpleABC.g:26:2: ( 'header' '{' ( field )+ '}' )
// simpleABC.g:26:4: 'header' '{' ( field )+ '}'
{
match(input,19,FOLLOW_19_in_header67); if (state.failed) return ast;
match(input,22,FOLLOW_22_in_header69); if (state.failed) return ast;
// simpleABC.g:27:4: ( field )+
int cnt1=0;
loop1:
while (true) {
int alt1=2;
int LA1_0 = input.LA(1);
if ( ((LA1_0 >= 13 && LA1_0 <= 18)||LA1_0==21) ) {
alt1=1;
}
switch (alt1) {
case 1 :
// simpleABC.g:27:5: field
{
pushFollow(FOLLOW_field_in_header75);
field3=field();
state._fsp--;
if (state.failed) return ast;
if ( state.backtracking==0 ) {ast.addAST(field3);}
}
break;
default :
if ( cnt1 >= 1 ) break loop1;
if (state.backtracking>0) {state.failed=true; return ast;}
EarlyExitException eee = new EarlyExitException(1, input);
throw eee;
}
cnt1++;
}
match(input,24,FOLLOW_24_in_header83); if (state.failed) return ast;
}
}
catch (RecognitionException re) {
reportError(re);
recover(input,re);
}
finally {
// do for sure before leaving
}
return ast;
}
// $ANTLR end "header"
似乎行field3=field();
返回null,所以让我们看一下field()
函数。这很长,但我会发布它只是为了彻底。
// $ANTLR start "field"
// simpleABC.g:31:1: field returns [Field ast] : ( 'X:' x= NUM | 'T:' t= STRING | 'C:' c= STRING | 'M:' (b= NUM '/' l= NUM ) | 'M:' 'C' | 'K:' k= PITCH | 'L:' l= NUM | 'L:' '/' l= NUM | 'tempo:' tempo= NUM );
public final Field field() throws RecognitionException {
Field ast = null;
Token x=null;
Token t=null;
Token c=null;
Token b=null;
Token l=null;
Token k=null;
Token tempo=null;
try {
// simpleABC.g:32:2: ( 'X:' x= NUM | 'T:' t= STRING | 'C:' c= STRING | 'M:' (b= NUM '/' l= NUM ) | 'M:' 'C' | 'K:' k= PITCH | 'L:' l= NUM | 'L:' '/' l= NUM | 'tempo:' tempo= NUM )
int alt2=9;
switch ( input.LA(1) ) {
case 18:
{
alt2=1;
}
break;
case 17:
{
alt2=2;
}
break;
case 13:
{
alt2=3;
}
break;
case 16:
{
int LA2_4 = input.LA(2);
if ( (LA2_4==12) ) {
alt2=5;
}
else if ( (LA2_4==NUM) ) {
alt2=4;
}
else {
if (state.backtracking>0) {state.failed=true; return ast;}
int nvaeMark = input.mark();
try {
input.consume();
NoViableAltException nvae =
new NoViableAltException("", 2, 4, input);
throw nvae;
} finally {
input.rewind(nvaeMark);
}
}
}
break;
case 14:
{
alt2=6;
}
break;
case 15:
{
int LA2_6 = input.LA(2);
if ( (LA2_6==NUM) ) {
alt2=7;
}
else if ( (LA2_6==11) ) {
alt2=8;
}
else {
if (state.backtracking>0) {state.failed=true; return ast;}
int nvaeMark = input.mark();
try {
input.consume();
NoViableAltException nvae =
new NoViableAltException("", 2, 6, input);
throw nvae;
} finally {
input.rewind(nvaeMark);
}
}
}
break;
case 21:
{
alt2=9;
}
break;
default:
if (state.backtracking>0) {state.failed=true; return ast;}
NoViableAltException nvae =
new NoViableAltException("", 2, 0, input);
throw nvae;
}
switch (alt2) {
case 1 :
// simpleABC.g:32:4: 'X:' x= NUM
{
match(input,18,FOLLOW_18_in_field99); if (state.failed) return ast;
x=(Token)match(input,NUM,FOLLOW_NUM_in_field103); if (state.failed) return ast;
if ( state.backtracking==0 ) { ast = (new ReferenceNumber((x!=null?x.getText():null))); }
}
break;
case 2 :
// simpleABC.g:33:4: 'T:' t= STRING
{
match(input,17,FOLLOW_17_in_field115); if (state.failed) return ast;
t=(Token)match(input,STRING,FOLLOW_STRING_in_field119); if (state.failed) return ast;
if ( state.backtracking==0 ) { ast = (new Title((t!=null?t.getText():null))); }
}
break;
case 3 :
// simpleABC.g:34:4: 'C:' c= STRING
{
match(input,13,FOLLOW_13_in_field130); if (state.failed) return ast;
c=(Token)match(input,STRING,FOLLOW_STRING_in_field134); if (state.failed) return ast;
if ( state.backtracking==0 ) { ast = (new Composer((c!=null?c.getText():null))); }
}
break;
case 4 :
// simpleABC.g:35:4: 'M:' (b= NUM '/' l= NUM )
{
match(input,16,FOLLOW_16_in_field145); if (state.failed) return ast;
// simpleABC.g:35:9: (b= NUM '/' l= NUM )
// simpleABC.g:35:10: b= NUM '/' l= NUM
{
b=(Token)match(input,NUM,FOLLOW_NUM_in_field150); if (state.failed) return ast;
match(input,11,FOLLOW_11_in_field152); if (state.failed) return ast;
l=(Token)match(input,NUM,FOLLOW_NUM_in_field156); if (state.failed) return ast;
}
if ( state.backtracking==0 ) { ast = (new Meter((b!=null?b.getText():null), (l!=null?l.getText():null))); }
}
break;
case 5 :
// simpleABC.g:36:4: 'M:' 'C'
{
match(input,16,FOLLOW_16_in_field166); if (state.failed) return ast;
match(input,12,FOLLOW_12_in_field168); if (state.failed) return ast;
if ( state.backtracking==0 ) { ast = (new Meter("4", "4")); }
}
break;
case 6 :
// simpleABC.g:37:4: 'K:' k= PITCH
{
match(input,14,FOLLOW_14_in_field180); if (state.failed) return ast;
k=(Token)match(input,PITCH,FOLLOW_PITCH_in_field184); if (state.failed) return ast;
if ( state.backtracking==0 ) { ast = (new Key((k!=null?k.getText():null))); }
}
break;
case 7 :
// simpleABC.g:38:4: 'L:' l= NUM
{
match(input,15,FOLLOW_15_in_field195); if (state.failed) return ast;
l=(Token)match(input,NUM,FOLLOW_NUM_in_field199); if (state.failed) return ast;
if ( state.backtracking==0 ) { ast = (new Length((l!=null?l.getText():null))); }
}
break;
case 8 :
// simpleABC.g:39:4: 'L:' '/' l= NUM
{
match(input,15,FOLLOW_15_in_field211); if (state.failed) return ast;
match(input,11,FOLLOW_11_in_field213); if (state.failed) return ast;
l=(Token)match(input,NUM,FOLLOW_NUM_in_field217); if (state.failed) return ast;
if ( state.backtracking==0 ) { ast = (new Length("/" + (l!=null?l.getText():null))); }
}
break;
case 9 :
// simpleABC.g:40:4: 'tempo:' tempo= NUM
{
match(input,21,FOLLOW_21_in_field228); if (state.failed) return ast;
tempo=(Token)match(input,NUM,FOLLOW_NUM_in_field232); if (state.failed) return ast;
if ( state.backtracking==0 ) { ast = (new Tempo((tempo!=null?tempo.getText():null))); }
}
break;
}
}
catch (RecognitionException re) {
reportError(re);
recover(input,re);
}
finally {
// do for sure before leaving
}
return ast;
}
// $ANTLR end "field"
在我看来,第二个switch-case语句中的match()
函数返回null,我认为这意味着它不会将输入标记与预期的标记匹配。下一个代码片段位于上面的field()
函数中,但是如果你不想对所有这些内容进行筛选,那么这就是我所说的片段:
switch (alt2) {
case 1 :
// simpleABC.g:32:4: 'X:' x= NUM
{
match(input,18,FOLLOW_18_in_field99); if (state.failed) return ast;
x=(Token)match(input,NUM,FOLLOW_NUM_in_field103); if (state.failed) return ast;
if ( state.backtracking==0 ) { ast = (new ReferenceNumber((x!=null?x.getText():null))); }
}
break;
所以我认为我已经尽可能地追溯了这一点,而没有深入了解ANTLR。对我来说,证据表明语法有问题。我已经多次检查并试图找出问题,但我没有运气。
这一定是令人难以置信的愚蠢,我只是缺少,或者此时超出我对ANTLR的理解,所以任何建议,猜测或解决方案都是受欢迎的。如果您有任何其他信息需要我提供,请告知我们,并感谢所有通过这篇文章发表的信息哈哈。