解析ANTLR语法后如何有效地解析对象模型中的引用

时间:2013-07-06 07:23:08

标签: c# antlr4

让我们假设我是ANTLR来解析一些文本以生成可以使用的只读对象模型。许多对象引用对象模型中的其他对象。

我目前正在采取的步骤是:

  1. 使用ANTLR 4将源解析为树(生成)
  2. 遍历树以构建临时对象模型(使用字符串作为引用)
  3. 走临时对象模型并创建公共模型
  4. 这种方法的问题在于随着语法的增长,类型和映射都会爆炸式增长。编译器和其他解析器采用什么方法来构建对象模型并解析内部引用?

    来源

    以下是正在解析的源代码的摘录。它简化了以说明挑战。

    class Class1 : Class2, Class4
    {
    
    }
    
    class Class2 : Class3
    {
    
    }
    
    class Class3
    {
    
    }
    
    class Class4
    {
    
    }
    

    公共对象模型

    这是解析结果的公共对象模型。

    public class ModelFactory
    {
        public IModel Create()
        {
            /* Some magic */
        }
    }
    
    public interface IModel
    {
        IClass CreateClass(string name);
    
        IEnumerable<IClass> Classes
        {
            get;
        }
    }
    
    public interface IClass
    {
        void CreateGeneralization(IClass superClass);
    
        IEnumerable<IClass> SubClasses
        {
            get;
        }
    
        IEnumerable<IClass> SuperClasses
        {
            get;
        }
    
        IModel Model
        {
            get;
        }
    
        string Name
        {
            get;
        }
    }
    

    测试

    我写的一个测试是为了验证我做对了:

    [Test]
    public void ParseTest()
    {
        // Arrange
        const string path = "MultipleInheritance.txt";
        var target = new ModelParser();
    
        // Act
        var model = target.Parse(path);
    
        // Assert
        Assert.IsNotNull(model);
        Assert.IsNotNull(model.Classes);
        var class1 = model.Classes.FirstOrDefault(c => c.Name == "Class1");
        var class2 = model.Classes.FirstOrDefault(c => c.Name == "Class2");
        var class3 = model.Classes.FirstOrDefault(c => c.Name == "Class3");
        var class4 = model.Classes.FirstOrDefault(c => c.Name == "Class4");            
        Assert.IsNotNull(class1);
        Assert.IsNotNull(class2);
        Assert.IsNotNull(class3);
        Assert.IsNotNull(class4);
    
        Assert.IsTrue(class1.SuperClasses.Any(c => c == class2));
        Assert.IsTrue(class1.SuperClasses.Any(c => c == class4));
        Assert.IsTrue(class2.SuperClasses.Any(c => c == class3));
        Assert.IsEmpty(class3.SuperClasses);
        Assert.IsEmpty(class4.SuperClasses);
    
        Assert.IsTrue(class4.SubClasses.Any(c => c == class1));
    }
    

    语法

    语法简化以说明问题。

    grammar Model;
    
    /*
     * Parser Rules
     */
    
    model
        :   classDeclaration*
        |   EOF
        ;
    
    classDeclaration
        :   'class' name=Identifier  (':' generalizations=typeList)?
            '{' 
            /* attributeDeclaration* */
            '}'
        ;
    
    typeList
        :   type (',' type)*
        ;
    
    type
        :   name=Identifier
        ;
    
    /*
     * Lexer Rules
     */
    
    Identifier
        :   Letter (Letter|IdentifierDigit)*
        ;
    
    fragment
    Letter
        :   '\u0024' 
        |   '\u0041'..'\u005a' 
        |   '\u005f' 
        |   '\u0061'..'\u007a' 
        |   '\u00c0'..'\u00d6' 
        |   '\u00d8'..'\u00f6' 
        |   '\u00f8'..'\u00ff' 
        |   '\u0100'..'\u1fff' 
        |   '\u3040'..'\u318f' 
        |   '\u3300'..'\u337f' 
        |   '\u3400'..'\u3d2d' 
        |   '\u4e00'..'\u9fff' 
        |   '\uf900'..'\ufaff'
        ;
    
    fragment
    IdentifierDigit
        :   '\u0030'..'\u0039' 
        |   '\u0660'..'\u0669' 
        |   '\u06f0'..'\u06f9' 
        |   '\u0966'..'\u096f' 
        |   '\u09e6'..'\u09ef' 
        |   '\u0a66'..'\u0a6f' 
        |   '\u0ae6'..'\u0aef' 
        |   '\u0b66'..'\u0b6f' 
        |   '\u0be7'..'\u0bef' 
        |   '\u0c66'..'\u0c6f' 
        |   '\u0ce6'..'\u0cef' 
        |   '\u0d66'..'\u0d6f' 
        |   '\u0e50'..'\u0e59' 
        |   '\u0ed0'..'\u0ed9' 
        |   '\u1040'..'\u1049'
        ;
    
    WS
        :   [ \r\t\n]+ -> skip 
        ;
    

    临时对象模型

    解析之后,我从树中构建了这个模型,然后我会走这条模型来构建公共领域模型。

    public class TempoaryModel
    {
        public TempoaryModel()
        {
            Classes = new List<TemporaryClass>();
        }
    
        public List<TemporaryClass> Classes
        {
            get;
            private set;
        }
    }
    
    public class TemporaryClass
    {
        public TemporaryClass()
        {
            SuperClasses = new List<string>();
        }
    
        public List<string> SuperClasses
        {
            get;
            private set;
        }
    
        public string Name
        {
            get;
            set;
        }
    }
    

1 个答案:

答案 0 :(得分:0)

如果你走两次树,看起来你可以避免使用临时模型。在第一次行走时,创建仅设置其名称的类的实例,并将它们添加到字典(类名,IClass)。在第二次迭代中,使用第一步中的字典设置它们之间的引用。