2

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

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

我写了简单的规则:

body:
'{' .* '}'
;

但它不能正常工作,当方法看起来像:

void foo()
{
  ...
  {
    ...
  }
  ...
}

规则匹配第一个大括号没问题,然后匹配

... 
{
  ...

作为 'any'(.*) 然后第三个大括号作为最后一个大括号,什么是不正确的,规则结束。

任何人都可以帮助我为方法主体编写适当的规则吗?正如我之前所说,我不想解析它们 - 只是跳过。

更新:

这是基于 Adam12 答案的我的问题的解决方案

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

2 回答 2

1

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

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

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

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

于 2012-09-10T00:45:06.637 回答
0

您可以在词法分析器中处理(嵌套)块的递归。诀窍是让你的类定义也包括开头{,这样就不会被这个递归词法分析器规则吞噬掉类的全部内容。

一个快速演示,毫无疑问是不完整的,但它是“模糊解析/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'))* '"'
 ;

我针对以下 Java 源文件运行生成的解析器:

/*
    ... 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'
方法'm1'
ClassDecl '内部'
方法'm2'
于 2012-09-10T12:38:06.023 回答