用于计算class级计数的部分语法

我需要计算正确的C#源文件中的类数。 我写了下面的语法:

grammar CSharpClassGrammar; options { language=CSharp2; } @parser::namespace { CSharpClassGrammar.Generated } @lexer::namespace { CSharpClassGrammar.Generated } @header { using System; using System.Collections.Generic; } @members { private List _classCollector = new List(); public List ClassCollector { get { return _classCollector; } } } /*------------------------------------------------------------------ * PARSER RULES *------------------------------------------------------------------*/ csfile : class_declaration* EOF ; class_declaration : (ACCESSLEVEL | MODIFIERS)* PARTIAL? 'class' CLASSNAME class_body ';'? { _classCollector.Add($CLASSNAME.text); } ; class_body : '{' class_declaration* '}' ; /*------------------------------------------------------------------ * LEXER RULES *------------------------------------------------------------------*/ ACCESSLEVEL : 'public' | 'internal' | 'protected' | 'private' | 'protected internal' ; MODIFIERS : 'static' | 'sealed' | 'abstract' ; PARTIAL : 'partial' ; CLASSNAME : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')* ; COMMENT : '//' ~('\n'|'\r')* {$channel=HIDDEN;} | '/*' ( options {greedy=false;} : . )* '*/' {$channel=HIDDEN;} ; WHITESPACE : ( '\t' | ' ' | '\r' | '\n'| '\u000C' )+ { $channel = HIDDEN; } ; 

此解析器使用空的class-body正确计算空类(以及嵌套类):

 internal class DeclarationClass1 { class DeclarationClass2 { public class DeclarationClass3 { abstract class DeclarationClass4 { } } } } 

我需要计算非空体的类,例如:

 class TestClass { int a = 42; class Nested { } } 

我需要以某种方式忽略所有“不是类声明”的代码。 在上面的例子中忽略

 int a = 42; 

我怎样才能做到这一点? 可能是其他语言的例子?
请帮忙!

如果您只对源文件的某些部分感兴趣,可以在选项{…}部分设置filter=true 。 这将使您只能定义您感兴趣的那些标记,以及您没有定义的标记,被词法分析器忽略。

请注意,这仅适用于词法分析器语法,而不适用于组合(或解析器)语法。

一个小小的演示:

 lexer grammar CSharpClassLexer; options { language=CSharp2; filter=true; } @namespace { Demo } Comment : '//' ~('\r' | '\n')* | '/*' .* '*/' ; String : '"' ('\\' . | ~('"' | '\\' | '\r' | '\n'))* '"' | '@' '"' ('"' '"' | ~'"')* '"' ; Class : 'class' Space+ Identifier {Console.WriteLine("Found class: " + $Identifier.text);} ; Space : ' ' | '\t' | '\r' | '\n' ; Identifier : ('a'..'z' | 'A'..'Z' | '_') ('a'..'z' | 'A'..'Z' | '_' | '0'..'9')* ; 

重要的是你要将Identifier放在那里,因为你不希望Xclass Foo被标记为: ['X', 'class', 'Foo'] 。 使用IdentifierXclass将成为整个标识符。

可以使用以下类测试语法:

 using System; using Antlr.Runtime; namespace Demo { class MainClass { public static void Main (string[] args) { string source = @"class TestClass { int a = 42; string _class = ""inside a string literal: class FooBar {}...""; class Nested { /* class NotAClass {} */ // class X { } class DoubleNested { string str = @"" multi line string class Bar {} ""; } } }"; Console.WriteLine("source=\n" + source + "\n-------------------------"); ANTLRStringStream Input = new ANTLRStringStream(source); CSharpClassLexer Lexer = new CSharpClassLexer(Input); CommonTokenStream Tokens = new CommonTokenStream(Lexer); Tokens.GetTokens(); } } } 

产生以下输出:

 source= class TestClass { int a = 42; string _class = "inside a string literal: class FooBar {}..."; class Nested { /* class NotAClass {} */ // class X { } class DoubleNested { string str = @" multi line string class Bar {} "; } } } ------------------------- Found class: TestClass Found class: Nested Found class: DoubleNested 

请注意,这只是一个快速演示,我不确定我是否在语法中处理了正确的字符串文字(我不熟悉C#),但是这个演示应该给你一个开始。

祝好运!