Search code examples
templatesantlrantlr3stringtemplate

Use of StringTemplate in Antlr


I would have this problem : Given this rules

   defField: type VAR ( ',' VAR)* SEP ;  

   VAR : ('a'..'z'|'A'..'Z')+ ;

   type: 'Number'|'String' ;

   SEP : '\n'|';' ;

where I have to do is to associate a template with a rule "defField", that returns the string that represents the xml-schema for the field, that is:

   Number a,b,c ;-> "<xs:element name="a" type = "xs:Number"\>" ,also for b and c.

my problem is in * of Kleene, that is, how do I write the template to do what I described above in the light of the '*' ??

Thanks you!!!


Solution

  • Collect all VAR tokens in a java.util.List by using the += operator:

    defField
      :  t=type v+=VAR (',' v+=VAR)* SEP 
      ;  
    

    Now v (a List) contains all VAR's.

    Then pass t and v as a parameter to a method in your StringTemplateGroup:

    defField
      :  t=type v+=VAR (',' v+=VAR)* SEP -> defFieldSchema(type={$t.text}, vars={$v})
      ;  
    

    where defFieldSchema(...) must be declared in your StringTemplateGroup, which might look like (file: T.stg):

    group T;
    
    defFieldSchema(type, vars) ::= <<
    <vars:{ v | \<xs:element name="<v.text>" type="xs:<type>"\>
    }>
    >>
    

    The syntax for iterating over a collection is as follows:

    <COLLECTION:{ EACH_ITEM_IN_COLLECTION | TEXT_TO_EMIT }>
    

    Ans since vars is a List containing CommonTokens's, I grabbed its .text attribute instead of relying on its toString() method.

    Demo

    Take the following grammar (file T.g):

    grammar T;
    
    options {
      output=template;
    }
    
    defField
      :  t=type v+=VAR (',' v+=VAR)* SEP -> defFieldSchema(type={$t.text}, vars={$v})
      ;  
    
    type
      :  NUMBER
      |  STRING
      ;
    
    NUMBER
      :  'Number'
      ;
    
    STRING
      :  'String' 
      ;
    
    VAR 
      :  ('a'..'z'|'A'..'Z')+ 
      ;
    
    SEP 
      :  '\n'
      |  ';' 
      ;
    
    SPACE
      :  ' ' {skip();}
      ;
    

    which can be tested with the following class (file: Main.java):

    import org.antlr.runtime.*;
    import org.antlr.stringtemplate.*;
    import java.io.*;
    
    public class Main {
      public static void main(String[] args) throws Exception {
        StringTemplateGroup group = new StringTemplateGroup(new FileReader("T.stg"));
        ANTLRStringStream in = new ANTLRStringStream("Number a,b,c;");
        TLexer lexer = new TLexer(in);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        TParser parser = new TParser(tokens);
        parser.setTemplateLib(group);
        TParser.defField_return returnValue = parser.defField();
        StringTemplate st = (StringTemplate)returnValue.getTemplate();
        System.out.println(st.toString());
      }
    }
    

    As you will see when you run this class, it parses the input "Number a,b,c;" and produces the following output:

    <xs:element name="a" type="xs:Number">
    <xs:element name="b" type="xs:Number">
    <xs:element name="c" type="xs:Number">
    

    EDIT

    To run the demo, make sure you have all of the following files in the same directory:

    • T.g (the combined grammar file)
    • T.stg (the StringTemplateGroup file)
    • antlr-3.3.jar (the latest stable ANTLR build as of this writing)
    • Main.java (the test class)

    then execute to following commands from your OS's shell/prompt (from the same directory all the files are in):

    java -cp antlr-3.3.jar org.antlr.Tool T.g  # generate the lexer & parser
    
    javac -cp antlr-3.3.jar *.java             # compile all .java source files
    
    java -cp .:antlr-3.3.jar Main              # run the main class (*nix)
                                               # or
    java -cp .;antlr-3.3.jar Main              # run the main class (Windows)         
    

    Probably not necessary to mention, but the # including the text after it should not be a part of the commands: these are only comments to indicate what these commands are for.