2013-07-06 43 views
0

假設我是ANTLR來解析某些文本以產生可被使用的只讀對象模型。許多對象引用對象模型中的其他對象。如何在解析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; 
    } 
} 

回答

0

看起來像你可以避免有一個臨時模型,如果你走樹兩次。在第一次行走時,只創建其名稱集的實例並將其添加到字典(類名稱,IClass)。在第二次迭代中,使用第一步中的字典設置它們之間的引用。