首页 > 解决方案 > ANTLR4 - VisitChildren 返回 null,即使孩子返回某个对象

问题描述

我一直在尝试实现访问者模式,将一些特定的 SQL 语句解析为由 TableDefinition 和 ColumnDefinition 对象组成的内部对象结构。

这是语法中的一小部分(精简):

column_definition
 : column_name datatype? column_constraint*
 ;
 
column_constraint
 : ( K_CONSTRAINT name )?
   ( K_PRIMARY K_KEY ( K_CLUSTERED | K_NONCLUSTERED )? ( K_ASC | K_DESC )? K_AUTOINCREMENT?
   | K_AUTOINCREMENT
   | K_NOT? K_NULL
   )
 ;

datatype
 : K_CHAR ( '(' unsigned_integer ')' )?                                     #char
 | K_DATE                                                                   #date
 ;

这是派生的 BaseVisitor 之一,它旨在返回 ColumnDefinitions:

namespace SqlParser.Visitor
{
    public class DataTypeVisitor: SqlAnywhereParserBaseVisitor<ColumnDefinition>
    {
        public override ColumnDefinition VisitColumn_definition([NotNull] SqlAnywhereParser.Column_definitionContext context)
        {
            var res = VisitChildren(context);
            var constraint = (SqlAnywhereParser.Column_constraintContext[])context.column_constraint();

            if (res != null) // Add NULL attributes
            {
                if (constraint.Any(c => c.K_NULL() != null && c.K_NOT() == null))
                    res.IsNullable = true;

                if (constraint.Any(c => c.K_NULL() != null && c.K_NOT() != null))
                    res.IsNullable = false;
            }

            return res;
        }

        public override ColumnDefinition VisitChar([NotNull] SqlAnywhereParser.CharContext context)
        {
            return new ColumnDefinition()
            {
                DataType = DbType.StringFixedLength,
                Length = int.Parse(context.unsigned_integer()?.GetText() ?? "1") 
            };
        }
   }
}

当我调试该过程时,我可以观察到对 VisitChildren 的调用如何进入返回 ColumnDefinition 对象的 VisitChar。当 VisitChar 完成并且光标跳回以在 VisitColumn_definition 中继续时,变量 res 为空。

我错过了一些重要的事情还是我误解了访问者模式?在我尝试 VisitChildren 之前,我曾经使用 base.VisitColumn_definition(context) 调用,它基本上只调用 VisitChildren。

有没有人有提示,我犯了哪些错误?为什么我在 VisitChar 叶创建的 ColumnDefinition 结果没有冒泡?

下面是我的测试输入:

CREATE TABLE "DBA"."pbcattbl" (
    "pbt_tnam"                       char(129) NOT NULL
   ,"pbt_tid"                        char(5) NULL
);

标签: c#antlrantlr4antlr4cs

解决方案


我找到了一个解决方案:

protected override List<ColumnDefinition> AggregateResult(List<ColumnDefinition> aggregate, List<ColumnDefinition> nextResult)
{
    if (aggregate != null && nextResult != null) aggregate.AddRange(nextResult);
    return aggregate ?? nextResult;
}

我将结果转换为 List<ColumnDefinition> 并向 AggregateResult 添加了适当的覆盖。

感谢@kaby76 用您的评论为我指明了正确的方向。也感谢所有其他人的反馈和快速响应!


推荐阅读