我正在尝试通过ANTLR预处理我的C ++源文件。我想输出一个输入文件保留原始源文件的所有空白格式,同时在适当的位置插入我自己的一些新的源代码。
我知道保留WS需要这个词法分析器规则:
WS: (' '|'\n'| '\r'|'\t'|'\f' )+ {$channel=HIDDEN;};
有了这个,我的解析器规则将有一个包含所有隐藏WS的$ text属性。但问题是,对于任何解析器规则,其$ text属性仅包括从与规则的第一个标记匹配的位置开始的那些输入文本。例如,如果这是我的输入(请注意令牌之前和之间的格式化WS):
line 1; line 2;
并且,如果我有2个单独的解析器规则匹配
"line 1;"
和
"line 2;"
以上单独但不是整行:
" line 1; line 2;"
,然后领先的WS和"line 1"
和"line 2"
之间的WS丢失(我的任何规则都无法访问)。
如果允许我的解析器规则确定何时在适当的位置添加新代码,我应该怎么做才能保留所有WHITESPACE?
假设每当我的代码包含对 function(1)的调用时,使用1作为参数而不是其他内容,它会在它之前添加 extraFunction():< / p>
void myFunction() {
function();
function(1);
}
变为:
void myFunction() {
function();
extraFunction();
function(1);
}
此预处理输出应保持人类可读,因为人们会继续对其进行编码。对于这个简单的例子,文本编辑器可以处理它。但是有更复杂的案例证明使用ANTLR是合理的。
答案 0 :(得分:2)
另一个解决方案,但也许不太实用(?):你可以向后收集所有空白,类似于这个未经测试的伪代码:
grammar T;
@members {
public printWhitespaceBetweenRules(Token start) {
int index = start.getTokenIndex() - 1;
while(index >= 0) {
Token token = input.get(index);
if(token.getChannel() != Token.HIDDEN_CHANNEL) break;
System.out.print(token.getText());
index--;
}
}
}
line1: 'line' '1' {printWhitespaceBetweenRules($start); };
line2: 'line' '2' {printWhitespaceBetweenRules($start); };
WS: (' '|'\n'| '\r'|'\t'|'\f' )+ {$channel=HIDDEN;};
但你仍然需要改变每条规则。
答案 1 :(得分:1)
我想一个解决方案是通过删除WS
将$channel = HIDDEN;
令牌保留在同一个频道中。这将允许您在解析器中访问WS令牌的信息。
答案 2 :(得分:1)
这是另一种解决方法(至少你发布的例子)。
因此,您希望将...function(1)
替换为...extraFunction();\nfunction(1)
,其中点为缩进,\n
换行。
你能做的就是匹配:
Function1
: Spaces 'function' Spaces '(' Spaces '1' Spaces ')'
;
fragment Spaces
: (' ' | '\t')*
;
并将其替换为匹配的文字,但预先加上额外的方法。然而,词法分析者现在会抱怨它输入如下:
'function()'
(没有 1 作为参数)
或:
' x...'
(缩进不后跟 f f )
因此,您需要在Function1
规则中“分出”,并确保只替换正确的事件。
您还必须注意字符串文字和注释中function(1)
的出现,假设您不希望它们预先加上extraFunction();\n
。
一个小小的演示:
grammar T;
parse
: (t=. {System.out.print($t.text);})* EOF
;
Function1
: indent=Spaces
( 'function' Spaces '(' Spaces ( '1' Spaces ')' {setText($indent.text + "extraFunction();\n" + $text);}
| ~'1' // do nothing if something other than `1` occurs
)
| '"' ~('"' | '\r' | '\n')* '"' // do nothing in case of a string literal
| '/*' .* '*/' // do nothing in case of a multi-line comment
| '//' ~('\r' | '\n')* // do nothing in case of a single-line comment
| ~'f' // do nothing in case of a char other than 'f' is seen
)
;
OtherChar
: . // a "fall-through" rule: it will match anything if none of the above matched
;
fragment Spaces
: (' ' | '\t')* // fragment rules are only used inside other lexer rules
;
您可以使用以下类进行测试:
import org.antlr.runtime.*;
public class Main {
public static void main(String[] args) throws Exception {
String source =
"/* \n" +
" function(1) \n" +
"*/ \n" +
"void myFunction() { \n" +
" s = \"function(1)\"; \n" +
" function(); \n" +
" function(1); \n" +
"} \n";
System.out.println(source);
System.out.println("---------------------------------");
TLexer lexer = new TLexer(new ANTLRStringStream(source));
TParser parser = new TParser(new CommonTokenStream(lexer));
parser.parse();
}
}
如果你运行这个Main类,你会看到以下内容被打印到控制台:
bart@hades:~/Programming/ANTLR/Demos/T$ java -cp antlr-3.3.jar org.antlr.Tool T.g
bart@hades:~/Programming/ANTLR/Demos/T$ javac -cp antlr-3.3.jar *.java
bart@hades:~/Programming/ANTLR/Demos/T$ java -cp .:antlr-3.3.jar Main
/*
function(1)
*/
void myFunction() {
s = "function(1)";
function();
function(1);
}
---------------------------------
/*
function(1)
*/
void myFunction() {
s = "function(1)";
function();
extraFunction();
function(1);
}
我确信这不是万无一失的(我没有考虑到字面意思,但是这可能是解决这个问题的开始,IMO。