用于计算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']
。 使用Identifier
, Xclass
将成为整个标识符。
可以使用以下类测试语法:
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#),但是这个演示应该给你一个开始。
祝好运!