antlr4stringtemplate-4

Applying ST to Antlr4-generated parse trees


Generated accessors of parse tree context nodes do not conform getProperty()/isProperty()/hasProperty() standard. As a result, ST can’t be applied to the parse tree directly. There seems to be 3 alternatives to apply ST to the generated parse trees:

  1. Create ST model adapter classes for each generated context node. Then ST can be applied directly to the generated parse tree. Duplicate work here is creating model adapters.
  2. For every parse tree node create a wrapper node that conforms getProperty()/isProperty()/hasProperty() standard. Then ST can be applied to wrapper nodes. Duplicate work here is creating wrapper nodes. (In this case parse tree is not even required; auto-parse-tree construction could be turned off and wrapper (AST) nodes could be created in grammar actions).
  3. Create a Visitor. Each visit*() instantiates an ST specific to a context node being visited, sets parameters (which could be STs returned by visiting child nodes or simple strings) and returns the ST. This is the option I’m currently using. Duplicate work here is creating visitor and assigning template parameters in code.

Is there an Antlr4 option that generates accessors of parse tree context nodes that conform getProperty()/isProperty()/hasProperty() standard? Or is there an ST4 option that allows it accessing property() instead of looking for getProperty()?

It would be nice to simply instantiate an ST template with a root context node as a parameter and let ST traverse the tree.


Solution

  • Just wanted to share a solution that almost avoids duplicate work while using approach #1 from my question.

    Step 1: create a model adaptor that uses reflection to call a method that does not conform getProperty()/isProperty()/hasProperty() standard.

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

    Step 2: Register model adaptors

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

    Step 3: implement isSubclassOf() method so that registerAdaptors() compiles:

    private static boolean isSubclassOf(Class<?> cls, Class<?> superCls) {
        while (cls != null) {
            if (cls == superCls) {
                return true;
            }
            cls = cls.getSuperclass();
        }
        return false;
    }