我改变了主意。我不想实现200个-一些Node类。相反,我将直接使用ANTLR生成的类,以实现Rubber鸭子代码检查、单元测试方法发现、“代码资源管理器”树视图以及所有其他可能需要一个解析树的类。
不过,Node 已经实现的派生类并没有浪费:我最终将使用它们向VBA本身(通过COM互操作)公开VBA代码的高级视图,这将支持非常酷的内容,比如可以枚举其模块成员的VBA代码。
我将VBParser --我将ParseInternal重命名为Parse (它成为了公共Parse方法的重载),并使其成为public:
public class VBParser : IRubberduckParser
{
public INode Parse(string projectName, string componentName, string code)
{
var result = Parse(code);
var walker = new ParseTreeWalker();
var listener = new VBTreeListener(projectName, componentName);
walker.Walk(listener, result);
return listener.Root;
}
public IParseTree Parse(string code)
{
var input = new AntlrInputStream(code);
var lexer = new VisualBasic6Lexer(input);
var tokens = new CommonTokenStream(lexer);
var parser = new VisualBasic6Parser(tokens);
return parser.startRule();
}
}现在我可以在任何我需要它的地方拥有一个IParseTree。我需要它的一个地方是在active VBA项目中发现测试方法。Rubber鸭测试方法总是公共的、无参数的方法,因此我编写了这个扩展方法/类:
public static class ParseTreeExtensions
{
/// <summary>
/// Finds all public procedures in specified parse tree.
/// </summary>
public static IEnumerable<VisualBasic6Parser.SubStmtContext> GetPublicProcedures(this IParseTree parseTree)
{
var walker = new ParseTreeWalker();
var listener = new PublicSubListener();
walker.Walk(listener, parseTree);
return listener.Members;
}
private class PublicSubListener : VisualBasic6BaseListener
{
private readonly IList<VisualBasic6Parser.SubStmtContext> _members = new List<VisualBasic6Parser.SubStmtContext>();
public IEnumerable<VisualBasic6Parser.SubStmtContext> Members { get { return _members; } }
public override void EnterSubStmt(VisualBasic6Parser.SubStmtContext context)
{
var visibility = context.visibility();
if (visibility == null || visibility.PUBLIC() != null)
{
_members.Add(context);
}
}
}
}下面是它的单元测试:
[TestMethod]
public void GetPublicProceduresReturnsPublicSubs()
{
IRubberduckParser parser = new VBParser();
var code = "Sub Foo()\nEnd Sub\n\nPrivate Sub FooBar()\nEnd Sub\n\nPublic Sub Bar()\nEnd Sub\n\nPublic Sub BarFoo(ByVal fb As Long)\nEnd Sub\n\nFunction GetFoo() As Bar\nEnd Function";
var module = parser.Parse(code);
var procedures = module.GetPublicProcedures().ToList();
var parameterless = procedures.Where(p => p.argList().arg().Count == 0).ToList();
Assert.AreEqual(3, procedures.Count);
Assert.AreEqual(2, parameterless.Count);
}这是可行的,所以我要用它。我不想将ANTLR生成的类公开给COM,所以我将其全部移动到它自己的程序集中,Rubber鸭子将引用它。
在我看来,VBParser在某种程度上混合了抽象级别。但这有多大问题吗?像这样“走”这棵树是个好主意吗?我没有重新解析代码,但是当我运行代码检查时,我可能会多次遍历它,我将不得不实现几个简单树侦听器,以检索“感兴趣的”节点。我应该这么做吗?
你觉得这种方法还有什么奇怪的地方吗?
发布于 2015-02-05 15:51:36
可以预期,返回IParseTree的解析器的方法将被命名为Parse()而不是startRule(),这违反了命名准则。
您应该尽可能地声明变量的用法。您还可以省略对Parse()调用的分配。
public INode Parse(string projectName, string componentName, string code)
{
var walker = new ParseTreeWalker();
var listener = new VBTreeListener(projectName, componentName);
walker.Walk(listener, Parse(code));
return listener.Root;
} VisualBasic6Parser.SubStmtContext.visibility()方法也不符合命名准则。
像这样“走”这棵树是个好主意吗?
说一些看不见的东西是很难的。
在我看来,VBParser在某种程度上混合了抽象级别。但这有多大问题吗?
在Parse()类的VBParser方法中看到一个VisualBasic6Parser类使用的IRubberduckParser接口,这有点让人分心。但我不知道如何解决这个问题,也不知道是否必须解决这个问题。
https://codereview.stackexchange.com/questions/78722
复制相似问题