Search code examples
parsingtemplatesjinja2antlr4linter

Modify Parse Tree to evaluate inheritance in a templating language


I'm building a linter program for a templating (like jinja, Twig) language that has a structure called "block".

I'm sure many of you are familiar with the concept of a block in templating languages. Now, a template that contains these blocks can be overridden by another template, the child template, having the same names for the blocks, and defining their own content and functionality for it.

So when evaluating a given source code (for scope checking) the requirement is that I first evaluate all the blocks in the child template independently. Then, I have to place the blocks in the places where they were defined in the root template, and evaluation should take place again so as to check for any violation of root template's template scope attributes (variables, macros etc).

I started out by using ANTLR4 to visit the child template. So in ANTLR, I hit the block, I evaluate it there, and then call on another visitor on the root template's parse tree.

But how do I place the child block's content in root template for evaluation? There is no tree modification / mutation in ANTLR4. I'm new to this whole compiler and parser paradigm. Can anyone suggest what is the best approach to take from here, considering my requirements? Also I'm ready to answer any other question you have just in case you feel the question is too vague, or I haven't provided enough information.


Solution

  • This problem -- versioning or inheritance -- can be easily handled through the use of a symbol table.

    Visit the contexts corresponding to the root template blocks, saving corresponding symbol/value pairs for each block. Then visit the child block contexts to update and/or add to the symbol/value pairs for each block. Finally, evaluate the symbol table to read out the merged results.

    This SO answer provides a simple symbol table.

    The Antlr git repo provides an exemplary fully-typed symbol table.