Parsing 具有歧义替代项的ANTLR语法
我的语法产生了意想不到的结果。我不确定这是我的错误还是ANTLR处理逻辑不明确的问题 这是我的语法:Parsing 具有歧义替代项的ANTLR语法,parsing,antlr,grammar,Parsing,Antlr,Grammar,我的语法产生了意想不到的结果。我不确定这是我的错误还是ANTLR处理逻辑不明确的问题 这是我的语法: grammar PPMacro; options { language=Java; backtrack=true; } file: (inputLines)+ EOF; inputLines : ( preprocessorLineSet | oneNormalInputLine ) ; oneNormalInputLine @after{System.ou
grammar PPMacro;
options {
language=Java;
backtrack=true;
}
file: (inputLines)+ EOF;
inputLines
: ( preprocessorLineSet | oneNormalInputLine ) ;
oneNormalInputLine @after{System.out.print("["+$text+"]");}
: (any_token_except_crlf)* CRLF ;
preprocessorLineSet
: ifPart endifLine;
ifPart: ifLine inputLines* ;
ifLine @after{System.out.print("{"+$text+"}" );}
: '#' IF (any_token_except_crlf)* CRLF ;
endifLine @after{System.out.print("{"+$text+"}" );}
: '#' ENDIF (any_token_except_crlf)* CRLF ;
any_token_except_crlf: (ANY_ID | WS | '#'|IF|ENDIF);
// just matches everything
CRLF: '\r'? '\n' ;
WS: (' '|'\t'|'\f' )+;
Hash: '#' ;
IF : 'if' ;
ENDIF : 'endif' ;
ANY_ID: ( 'a'..'z'|'A'..'Z'|'0'..'9'| '_')+ ;
说明:
< P> >解析C++,如果…endif块
我试图识别嵌套的#if#endif块。这是由我的预处理器行集完成的。它包含支持嵌套块的递归定义。oneNormalInputLine用于处理任何不属于#if形式的内容。此规则是“匹配任何内容”规则,实际上与#if行匹配。但我故意把它放在输入行中的预处理行之后。我希望这种排序可以阻止它匹配#if或#endif行。使用catch规则的原因是,我希望一个规则接受任何其他C++语法,并简单地将它们返回到输出。
我检查了一下,我把所有的东西都打印出来。由preprocessorLineSet匹配的行应该被{}包围,而由oneNormalInputLine匹配的行应该被[]包围
样本输入:
#if s
s
#if a
s
s
#endif
#endif
还有这个
#if
abc
#endif
[#if
][abc
][#endif
]
相应的产出:
[#if s
][s
][#if a
][s
][s
][#endif
][#endif
]
还有这个
#if
abc
#endif
[#if
][abc
][#endif
]
问题:
包括#if#endif在内的所有输出行都被[]包围,这意味着它们仅由一个normalInputLine匹配!但我没想到会这样。预处理器线性集应该能够匹配#if行。为什么我会得到这个结果
该行包含不明确的备选方案:
inputLines : ( preprocessorLineSet | oneNormalInputLine );
因为两者都可以匹配#if和#endif。但我希望第一种选择应该使用,而不是后一种。还请注意,回溯处于启用状态
编辑
我的oneNormalInputLine规则接受一切的原因是,很难将没有特定模式的东西表示为#如果模式可能相当复杂:
/***
comments
*/ # /***
comments
*/ if
这是一个有效的模式。编写一个没有这种模式的规则似乎很困难。你的方法不是很稳健-我建议你保持简单,使用实际的语言规则,它说以
开头的每一行都是预处理器指令,而不是以
开头的每一行都不是。使用这个规则,语法中不会有歧义,理解起来也会简单得多
为什么你的语法不好用?问题是您的预处理线性集
规则无法匹配任何内容
preprocessorLineSet
: ifPart endifLine;
ifPart: ifLine inputLines* ;
它以#if…
开头,然后应该匹配其他行,当第一个匹配的#endif
出现时,它应该匹配并结束。然而,它实际上并没有做到这一点<代码>输入行可以匹配几乎任何一行(几乎-它不会匹配,例如C++的运算符和其他非标识符),包括所有预处理器指令。这意味着ifPart
规则将与输入的末尾匹配,并且将不会剩下endifLine
。请注意,回溯对此没有影响,因为一旦ANTLR与规则匹配(在本例中为ifPart
,它将在整个剩余输入上成功,因为*
是贪婪的),它将永远不会回溯到该规则中。ANTLR的回溯规则很复杂
请注意,如果您使
oneNormalLine
与预处理器指令不匹配(例如,它类似于(nonHash any*|)CRLF
,它将开始工作。您的任何除\u CRLF
之外的\u标记都会导致歧义。您需要修复它(并删除backtrack=true;
)通过让该规则与以下内容匹配:
- 太空船
- a
后跟除“#”
、'if'
和换行符以外的任何字符'endif'
- 除
“#”和换行符外的任何字符,后跟
或'if'
'endif'
- 标识符
语法;
选择权{
输出=AST;
}
代币{
文件
}
文件
:行+EOF->^(文件行+)
;
线
:if_stat
|法线
;
如果统计
:HASH IF normal\u line*HASH ENDIF->^(IF normal\u line*)
;
法线
:非特殊*CRLF->非特殊*
;
非特殊
:WS
|HASH~(IF | ENDIF | CRLF)
|~(HASH | CRLF)(IF | ENDIF)
|身份证
;
CRLF:'\r'?'\n';
WS:(“|”\t“|”\f')+;
散列:“#”;
如果:‘如果’;
ENDIF:‘ENDIF’;
ID:(‘a’、‘z’|‘a’、‘z’|‘0’、‘9’|’)+;
这可以通过以下类进行测试:
import org.antlr.runtime.*;
导入org.antlr.runtime.tree.*;
导入org.antlr.stringtemplate.*;
公共班机{
公共静态void main(字符串[]args)引发异常{
PPMacroLexer lexer=新的PPMacroLexer(新的ANTLRFileStream(“test.cpp”);
PPMacroParser parser=newPPMacroparser(newCommonTokenStream(lexer));
CommonTree=(CommonTree)parser.file().getTree();
DOTTreeGenerator gen=新的DOTTreeGenerator();
StringTemplate st=gen.toDOT(树);
系统输出打印LN(st);
}
}
以及test.cpp
文件,该文件可能如下所示:
ab
#如果是
T
#如果
U
v
#恩迪夫
#恩迪夫
C
D
将生成以下AST:
编辑
我刚才看到,您需要考虑#
和if
(和endif
)之间的多行注释和空格。您可以在lexer中最好地处理这种情况,如下所示:
语法;
选择权{
输出=AST;
}
代币{
文件
ENDIF;
}
文件
:行+EOF->^(文件行+)
;
线
:if_stat
|法线
;
如果统计
:IF normal_line*ENDIF->^(IF normal_line*)
;
法线
:非特殊*CRLF->非特殊*
;
非特殊
:WS
|身份证
;
IF:“#”噪声*(“IF”|“endif”{$type=endif;});
CRLF:'\r'?'\n';
WS:(“|”\t“|”\f')+;
ID:('a'..'z'|'a'..'z'|'0'..'9'|'|')+;
注释:'/*'.'*/'{skip();};
碎片噪声
: '/*' .* '*/'
|WS
;
片段ENDIF:;
wh