0

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

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

  1. 使用 ANTLR 4 将源解析成树(它生成)
  2. 遍历树以构建临时对象模型(使用字符串作为引用)
  3. 遍历临时对象模型并创建公共模型

这种方法的问题在于,随着语法的增长,类型和映射会爆炸式增长。编译器和其他解析器采用什么方法来构建对象模型和解析内部引用?

资源

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

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;
    }
}
4

1 回答 1

0

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

于 2013-07-06T07:28:20.410 回答