假设我是 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;
}
}