首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Rubber鸭VBA Parser,第五集:反The反击

Rubber鸭VBA Parser,第五集:反The反击
EN

Code Review用户
提问于 2015-01-27 04:39:04
回答 1查看 487关注 0票数 6

我改变了主意。我不想实现200个-一些Node类。相反,我将直接使用ANTLR生成的类,以实现Rubber鸭子代码检查、单元测试方法发现、“代码资源管理器”树视图以及所有其他可能需要一个解析树的类。

不过,Node 已经实现的派生类并没有浪费:我最终将使用它们向VBA本身(通过COM互操作)公开VBA代码的高级视图,这将支持非常酷的内容,比如可以枚举其模块成员的VBA代码。

我将VBParser --我将ParseInternal重命名为Parse (它成为了公共Parse方法的重载),并使其成为public

代码语言:javascript
复制
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鸭测试方法总是公共的、无参数的方法,因此我编写了这个扩展方法/类:

代码语言:javascript
复制
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);
            }
        }
    }
}

下面是它的单元测试:

代码语言:javascript
复制
    [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在某种程度上混合了抽象级别。但这有多大问题吗?像这样“走”这棵树是个好主意吗?我没有重新解析代码,但是当我运行代码检查时,我可能会多次遍历它,我将不得不实现几个简单树侦听器,以检索“感兴趣的”节点。我应该这么做吗?

你觉得这种方法还有什么奇怪的地方吗?

EN

回答 1

Code Review用户

回答已采纳

发布于 2015-02-05 15:51:36

可以预期,返回IParseTree的解析器的方法将被命名为Parse()而不是startRule(),这违反了命名准则。

您应该尽可能地声明变量的用法。您还可以省略对Parse()调用的分配。

代码语言:javascript
复制
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接口,这有点让人分心。但我不知道如何解决这个问题,也不知道是否必须解决这个问题。

票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/78722

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档