ANTLR语法在保留WhiteSpace格式的同时预处理源文件

时间:2011-09-16 11:15:14

标签: parsing compiler-construction antlr grammar

我正在尝试通过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是合理的。

3 个答案:

答案 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。