将 ST 应用于 Antlr4 生成的解析树
Applying ST to Antlr4-generated parse trees
生成的解析树上下文节点访问器不符合 getProperty()/isProperty()/hasProperty() 标准。因此,ST 不能直接应用于解析树。似乎有 3 种替代方法可以将 ST 应用于生成的解析树:
- 为每个生成的上下文节点创建 ST 模型适配器 类。然后 ST 可以直接应用于生成的解析树。 这里的重复工作是创建模型适配器。
- 为每个解析树节点创建一个符合 getProperty()/isProperty()/hasProperty() 标准的包装器节点。然后 ST 可以应用于包装器节点。 这里的重复工作是创建包装器节点。(在这种情况下甚至不需要解析树;可以关闭自动解析树构造,并且可以在语法中创建包装器 (AST) 节点动作)。
- 创建访客。每个 visit*() 实例化一个特定于被访问的上下文节点的 ST,设置参数(可以是访问子节点或简单字符串返回的 ST)和 returns ST。这是我目前使用的选项。 这里的重复工作是在代码中创建访问者和分配模板参数。
是否有 Antlr4 选项生成符合 getProperty()/isProperty()/hasProperty() 标准的解析树上下文节点的访问器?或者是否有一个 ST4 选项允许它访问 属性() 而不是查找 getProperty()?
最好简单地实例化一个ST模板,以根上下文节点为参数,让ST遍历树。
只是想分享一个几乎可以避免重复工作的解决方案,同时使用我的问题中的方法 #1。
第1步:创建模型适配器,使用反射调用不符合getProperty()/isProperty()/hasProperty()标准的方法。
private static class MyModelAdaptor extends ObjectModelAdaptor {
@Override
public synchronized Object getProperty(Interpreter interp, ST self, Object o, Object property, String propertyName) throws STNoSuchPropertyException {
try {
return super.getProperty(interp, self, o, property, propertyName);
} catch (STNoSuchPropertyException noProperty) {
final Class<?> cls = o.getClass();
try {
return cls.getMethod(propertyName).invoke(o);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
throw noProperty;
}
}
}
}
第 2 步:注册模型适配器
public static STGroup registerAdaptors(STGroup stg) {
final MyModelAdaptor adaptor = new MyModelAdaptor();
for (final Class<?> cls : MyParser.class.getDeclaredClasses()) {
if (isSubclassOf(cls, ParserRuleContext.class)) {
stg.registerModelAdaptor(cls, adaptor);
}
}
return stg;
}
第 3 步:实现 isSubclassOf() 方法以便 registerAdaptors() 编译:
private static boolean isSubclassOf(Class<?> cls, Class<?> superCls) {
while (cls != null) {
if (cls == superCls) {
return true;
}
cls = cls.getSuperclass();
}
return false;
}
生成的解析树上下文节点访问器不符合 getProperty()/isProperty()/hasProperty() 标准。因此,ST 不能直接应用于解析树。似乎有 3 种替代方法可以将 ST 应用于生成的解析树:
- 为每个生成的上下文节点创建 ST 模型适配器 类。然后 ST 可以直接应用于生成的解析树。 这里的重复工作是创建模型适配器。
- 为每个解析树节点创建一个符合 getProperty()/isProperty()/hasProperty() 标准的包装器节点。然后 ST 可以应用于包装器节点。 这里的重复工作是创建包装器节点。(在这种情况下甚至不需要解析树;可以关闭自动解析树构造,并且可以在语法中创建包装器 (AST) 节点动作)。
- 创建访客。每个 visit*() 实例化一个特定于被访问的上下文节点的 ST,设置参数(可以是访问子节点或简单字符串返回的 ST)和 returns ST。这是我目前使用的选项。 这里的重复工作是在代码中创建访问者和分配模板参数。
是否有 Antlr4 选项生成符合 getProperty()/isProperty()/hasProperty() 标准的解析树上下文节点的访问器?或者是否有一个 ST4 选项允许它访问 属性() 而不是查找 getProperty()?
最好简单地实例化一个ST模板,以根上下文节点为参数,让ST遍历树。
只是想分享一个几乎可以避免重复工作的解决方案,同时使用我的问题中的方法 #1。
第1步:创建模型适配器,使用反射调用不符合getProperty()/isProperty()/hasProperty()标准的方法。
private static class MyModelAdaptor extends ObjectModelAdaptor {
@Override
public synchronized Object getProperty(Interpreter interp, ST self, Object o, Object property, String propertyName) throws STNoSuchPropertyException {
try {
return super.getProperty(interp, self, o, property, propertyName);
} catch (STNoSuchPropertyException noProperty) {
final Class<?> cls = o.getClass();
try {
return cls.getMethod(propertyName).invoke(o);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
throw noProperty;
}
}
}
}
第 2 步:注册模型适配器
public static STGroup registerAdaptors(STGroup stg) {
final MyModelAdaptor adaptor = new MyModelAdaptor();
for (final Class<?> cls : MyParser.class.getDeclaredClasses()) {
if (isSubclassOf(cls, ParserRuleContext.class)) {
stg.registerModelAdaptor(cls, adaptor);
}
}
return stg;
}
第 3 步:实现 isSubclassOf() 方法以便 registerAdaptors() 编译:
private static boolean isSubclassOf(Class<?> cls, Class<?> superCls) {
while (cls != null) {
if (cls == superCls) {
return true;
}
cls = cls.getSuperclass();
}
return false;
}