2

私の仕事は、ANTLR 文法を作成し、C# ソース コード ファイルを分析し、クラス階層を生成することです。次に、それを使用してクラス図を生成します。

名前空間、クラス宣言、およびメソッド宣言を解析するためのルールを作成しました。メソッド本体のスキップに問題があります。私の仕事では体は役に立たないので、それらを解析する必要はありません。

簡単なルールを書きました:

body:
'{' .* '}'
;

ただし、メソッドが次のように見える場合、正しく機能しません。

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

ルールは最初のブレースに一致し、OK であり、次に一致します

... 
{
  ...

'any'(.*) として、次に 3 番目のブレースを最後のブレースとして、何が問題であり、ルールが終了します。

メソッド本体の適切なルールを書くのを手伝ってくれる人はいますか? 前に言ったように、私はそれらを解析したくありません - スキップするだけです。

アップデート:

ここにAdam12の回答に強く基づいた私の問題の解決策があります

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

2 に答える 2

0

レクサーで (ネストされた) ブロックの再帰を処理できます。{秘訣は、クラスの内容全体がこの再帰的レクサー規則によって飲み込まれないように、クラス定義にもオープニングを含めることです。

間違いなく完全ではない簡単なデモですが、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 'ムー'
メソッド「m1」
ClassDecl 'インナー'
メソッド「m2」
于 2012-09-10T12:38:06.023 に答える