0
假設我是ANTLR來解析某些文本以產生可被使用的只讀對象模型。許多對象引用對象模型中的其他對象。如何在解析ANTLR語法後有效地解析對象模型中的引用
我目前採取的步驟是:
- 使用ANTLR 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;
}
}