C# 用于跳过方法体的ANTLR规则
我的任务是创建ANTLR语法,分析C#源代码文件并生成类层次结构。然后,我将使用它生成类图 我编写了解析名称空间、类声明和方法声明的规则。现在我有跳过方法体的问题。我不需要解析它们,因为身体在我的任务中是无用的 我写了一条简单的规则:C# 用于跳过方法体的ANTLR规则,c#,antlr,rule,skip,C#,Antlr,Rule,Skip,我的任务是创建ANTLR语法,分析C#源代码文件并生成类层次结构。然后,我将使用它生成类图 我编写了解析名称空间、类声明和方法声明的规则。现在我有跳过方法体的问题。我不需要解析它们,因为身体在我的任务中是无用的 我写了一条简单的规则: body: '{' .* '}' ; 但当方法如下所示时,它无法正常工作: void foo() { ... { ... } ... } 规则首先匹配“ok”,然后匹配 ... { ... 作为“any”(*),然后第三个大括号
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,您能给我一些如何接受{}对的例子吗?我想我应该在这里使用递归,但仅此而已