C# 用于跳过方法体的ANTLR规则

C# 用于跳过方法体的ANTLR规则,c#,antlr,rule,skip,C#,Antlr,Rule,Skip,我的任务是创建ANTLR语法,分析C#源代码文件并生成类层次结构。然后,我将使用它生成类图 我编写了解析名称空间、类声明和方法声明的规则。现在我有跳过方法体的问题。我不需要解析它们,因为身体在我的任务中是无用的 我写了一条简单的规则: body: '{' .* '}' ; 但当方法如下所示时,它无法正常工作: void foo() { ... { ... } ... } 规则首先匹配“ok”,然后匹配 ... { ... 作为“any”(*),然后第三个大括号

我的任务是创建ANTLR语法,分析C#源代码文件并生成类层次结构。然后,我将使用它生成类图

我编写了解析名称空间、类声明和方法声明的规则。现在我有跳过方法体的问题。我不需要解析它们,因为身体在我的任务中是无用的

我写了一条简单的规则:

body:
'{' .* '}'
;
但当方法如下所示时,它无法正常工作:

void foo()
{
  ...
  {
    ...
  }
  ...
}
规则首先匹配“ok”,然后匹配

... 
{
  ...
作为“any”(*),然后第三个大括号作为最后一个大括号,什么不好,规则结束

有人能帮我为方法体编写合适的规则吗?正如我之前所说,我不想解析它们——只想跳过

更新:

这是我的问题的解决方案,强烈地基于Adam12答案

body:
'{' ( ~('{' | '}') | body)* '}'
;

必须使用与括号对匹配的递归规则

rule1 : '(' 
  (
    nestedParan
  | (~')')*
  )
  ')';

nestedParan : '('
  (
    nestedParan
  | (~')')*
  )
  ')';

这段代码假设您在这里使用解析器,因此字符串和注释已经被排除。ANTLR不允许在解析器规则中否定多个备选方案,因此上面的代码依赖于按顺序尝试备选方案的事实。它应该给出一个警告,选项1和2都与“(”匹配,因此选择第一个选项,这是我们想要的。

您可以在lexer中处理(嵌套)块的递归。诀窍是让您的类定义也包括开头
{
这样,类的所有内容都不会被这个递归lexer规则占用

毫无疑问,这是一个不完整的快速演示,但它是“fuzzy parse/lex”Java(或C#,稍作修改)源文件的良好开端:

grammar T;

parse
 : (t=. {System.out.printf("\%-15s '\%s'\n", tokenNames[$t.type], $t.text.replace("\n", "\\n"));})* EOF
 ;

Skip
 : (StringLiteral | CharLiteral | Comment) {skip();}
 ;

PackageDecl
 : 'package' Spaces Ids {setText($Ids.text);}
 ;

ClassDecl
 : 'class' Spaces Id Spaces? '{' {setText($Id.text);}
 ;

Method
 : Id Spaces? ('(' {setText($Id.text);}
              | /* no method after all! */ {skip();}
              )
 ;

MethodOrStaticBlock
 : Block {skip();}
 ;

Any
 : . {skip();}
 ;

// fragments
fragment Spaces 
 : (' ' | '\t' | '\r' | '\n')+
 ;

fragment Ids
 : Id ('.' Id)*
 ;

fragment Id 
 : ('a'..'z' | 'A'..'Z' | '_') ('a'..'z' | 'A'..'Z' | '_' | '0'..'9')*
 ;

fragment Block
 : '{' ( ~('{' | '}' | '"' | '\'' | '/')
       | {input.LA(2) != '/'}?=> '/'
       | StringLiteral
       | CharLiteral
       | Comment
       | Block
       )*
   '}'
 ;

fragment Comment
 : '/*' .* '*/'
 | '//' ~('\r' | '\n')*
 ;

fragment CharLiteral
 : '\'' ('\\\'' | ~('\\' | '\'' | '\r' | '\n'))+ '\''
 ;

fragment StringLiteral
 : '"' ('\\"' | ~('\\' | '"' | '\r' | '\n'))* '"'
 ;
/*
    ... package NO.PACKAGE; ...
*/
package foo.bar;

public final class Mu {

  static String x;

  static {
    x = "class NotAClass!";
  }

  void m1() {
    // {
    while(true) {
      double a = 2.0 / 2;
      if(a == 1.0) { break; } // }
      /* } */
    }
  }

  static class Inner {
    int m2   () {return 42; /*comment}*/ }
  }
}
我针对以下Java源文件运行生成的解析器:

grammar T;

parse
 : (t=. {System.out.printf("\%-15s '\%s'\n", tokenNames[$t.type], $t.text.replace("\n", "\\n"));})* EOF
 ;

Skip
 : (StringLiteral | CharLiteral | Comment) {skip();}
 ;

PackageDecl
 : 'package' Spaces Ids {setText($Ids.text);}
 ;

ClassDecl
 : 'class' Spaces Id Spaces? '{' {setText($Id.text);}
 ;

Method
 : Id Spaces? ('(' {setText($Id.text);}
              | /* no method after all! */ {skip();}
              )
 ;

MethodOrStaticBlock
 : Block {skip();}
 ;

Any
 : . {skip();}
 ;

// fragments
fragment Spaces 
 : (' ' | '\t' | '\r' | '\n')+
 ;

fragment Ids
 : Id ('.' Id)*
 ;

fragment Id 
 : ('a'..'z' | 'A'..'Z' | '_') ('a'..'z' | 'A'..'Z' | '_' | '0'..'9')*
 ;

fragment Block
 : '{' ( ~('{' | '}' | '"' | '\'' | '/')
       | {input.LA(2) != '/'}?=> '/'
       | StringLiteral
       | CharLiteral
       | Comment
       | Block
       )*
   '}'
 ;

fragment Comment
 : '/*' .* '*/'
 | '//' ~('\r' | '\n')*
 ;

fragment CharLiteral
 : '\'' ('\\\'' | ~('\\' | '\'' | '\r' | '\n'))+ '\''
 ;

fragment StringLiteral
 : '"' ('\\"' | ~('\\' | '"' | '\r' | '\n'))* '"'
 ;
/*
    ... package NO.PACKAGE; ...
*/
package foo.bar;

public final class Mu {

  static String x;

  static {
    x = "class NotAClass!";
  }

  void m1() {
    // {
    while(true) {
      double a = 2.0 / 2;
      if(a == 1.0) { break; } // }
      /* } */
    }
  }

  static class Inner {
    int m2   () {return 42; /*comment}*/ }
  }
}
产生了以下输出:

PackageDecl 'foo.bar' ClassDecl 'Mu' Method 'm1' ClassDecl 'Inner' Method 'm2' 包装编号“foo.bar” ClassDecl‘Mu’ 方法‘m1’ 类decl“内部”
方法'm2'您正在执行一项非常困难的任务,您必须接受正文中的{和}对,还必须忽略正文中的注释和字符串内容{something*/对于一些被临时删除的代码,它们会干扰您的规则。@Casperah,您能给我一些如何接受{}对的例子吗?我想我应该在这里使用递归,但仅此而已