Home | History | Annotate | Download | only in tool
      1 /*
      2  * [The "BSD license"]
      3  *  Copyright (c) 2010 Terence Parr
      4  *  All rights reserved.
      5  *
      6  *  Redistribution and use in source and binary forms, with or without
      7  *  modification, are permitted provided that the following conditions
      8  *  are met:
      9  *  1. Redistributions of source code must retain the above copyright
     10  *      notice, this list of conditions and the following disclaimer.
     11  *  2. Redistributions in binary form must reproduce the above copyright
     12  *      notice, this list of conditions and the following disclaimer in the
     13  *      documentation and/or other materials provided with the distribution.
     14  *  3. The name of the author may not be used to endorse or promote products
     15  *      derived from this software without specific prior written permission.
     16  *
     17  *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     18  *  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     19  *  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     20  *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     21  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     22  *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23  *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24  *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 package org.antlr.tool;
     29 
     30 import org.antlr.Tool;
     31 import org.antlr.codegen.CodeGenerator;
     32 import org.antlr.misc.Utils;
     33 import org.stringtemplate.v4.ST;
     34 import org.stringtemplate.v4.STGroup;
     35 import org.stringtemplate.v4.STGroupFile;
     36 
     37 import java.io.*;
     38 import java.util.ArrayList;
     39 import java.util.List;
     40 
     41 /** Given a grammar file, show the dependencies on .tokens etc...
     42  *  Using ST, emit a simple "make compatible" list of dependencies.
     43  *  For example, combined grammar T.g (no token import) generates:
     44  *
     45  *		TParser.java : T.g
     46  * 		T.tokens : T.g
     47  * 		T__g : T.g
     48  *
     49  *  For tree grammar TP with import of T.tokens:
     50  *
     51  * 		TP.g : T.tokens
     52  * 		TP.java : TP.g
     53  *
     54  *  If "-lib libdir" is used on command-line with -depend, then include the
     55  *  path like
     56  *
     57  * 		TP.g : libdir/T.tokens
     58  *
     59  *  Pay attention to -o as well:
     60  *
     61  * 		outputdir/TParser.java : T.g
     62  *
     63  *  So this output shows what the grammar depends on *and* what it generates.
     64  *
     65  *  Operate on one grammar file at a time.  If given a list of .g on the
     66  *  command-line with -depend, just emit the dependencies.  The grammars
     67  *  may depend on each other, but the order doesn't matter.  Build tools,
     68  *  reading in this output, will know how to organize it.
     69  *
     70  *  This is a wee bit slow probably because the code generator has to load
     71  *  all of its template files in order to figure out the file extension
     72  *  for the generated recognizer.
     73  *
     74  *  This code was obvious until I removed redundant "./" on front of files
     75  *  and had to escape spaces in filenames :(
     76  */
     77 public class BuildDependencyGenerator {
     78     protected String grammarFileName;
     79     protected String tokenVocab;
     80     protected Tool tool;
     81     protected Grammar grammar;
     82     protected CodeGenerator generator;
     83     protected STGroup templates;
     84 
     85     public BuildDependencyGenerator(Tool tool, String grammarFileName)
     86             throws IOException {
     87         this.tool = tool;
     88         this.grammarFileName = grammarFileName;
     89         grammar = tool.getRootGrammar(grammarFileName);
     90         String language = (String) grammar.getOption("language");
     91         generator = new CodeGenerator(tool, grammar, language);
     92         generator.loadTemplates(language);
     93     }
     94 
     95     /** From T.g return a list of File objects that
     96      *  name files ANTLR will emit from T.g.
     97      */
     98     public List<File> getGeneratedFileList() {
     99         List<File> files = new ArrayList<File>();
    100         File outputDir = tool.getOutputDirectory(grammarFileName);
    101         if (outputDir.getName().equals(".")) {
    102             outputDir = null;
    103         } else if (outputDir.getName().indexOf(' ') >= 0) { // has spaces?
    104             String escSpaces = Utils.replace(outputDir.toString(),
    105                     " ",
    106                     "\\ ");
    107             outputDir = new File(escSpaces);
    108         }
    109         // add generated recognizer; e.g., TParser.java
    110         String recognizer =
    111                 generator.getRecognizerFileName(grammar.name, grammar.type);
    112         files.add(new File(outputDir, recognizer));
    113         // add output vocab file; e.g., T.tokens. This is always generated to
    114         // the base output directory, which will be just . if there is no -o option
    115         //
    116         files.add(new File(tool.getOutputDirectory(), generator.getVocabFileName()));
    117         // are we generating a .h file?
    118         ST headerExtST = null;
    119         ST extST = generator.getTemplates().getInstanceOf("codeFileExtension");
    120         if (generator.getTemplates().isDefined("headerFile")) {
    121             headerExtST = generator.getTemplates().getInstanceOf("headerFileExtension");
    122             String suffix = Grammar.grammarTypeToFileNameSuffix[grammar.type];
    123             String fileName = grammar.name + suffix + headerExtST.render();
    124             files.add(new File(outputDir, fileName));
    125         }
    126         if (grammar.type == Grammar.COMBINED) {
    127             // add autogenerated lexer; e.g., TLexer.java TLexer.h TLexer.tokens
    128             // don't add T__.g (just a temp file)
    129 
    130             String suffix = Grammar.grammarTypeToFileNameSuffix[Grammar.LEXER];
    131             String lexer = grammar.name + suffix + extST.render();
    132             files.add(new File(outputDir, lexer));
    133 
    134             // TLexer.h
    135             if (headerExtST != null) {
    136                 String header = grammar.name + suffix + headerExtST.render();
    137                 files.add(new File(outputDir, header));
    138             }
    139         // for combined, don't generate TLexer.tokens
    140         }
    141 
    142         // handle generated files for imported grammars
    143         List<Grammar> imports =
    144                 grammar.composite.getDelegates(grammar.composite.getRootGrammar());
    145         for (Grammar g : imports) {
    146             outputDir = tool.getOutputDirectory(g.getFileName());
    147             String fname = groomQualifiedFileName(outputDir.toString(), g.getRecognizerName() + extST.render());
    148             files.add(new File(fname));
    149         }
    150 
    151         if (files.size() == 0) {
    152             return null;
    153         }
    154         return files;
    155     }
    156 
    157     /**
    158      * Return a list of File objects that name files ANTLR will read
    159      * to process T.g; This can be .tokens files if the grammar uses the tokenVocab option
    160      * as well as any imported grammar files.
    161      */
    162     public List<File> getDependenciesFileList() {
    163         // Find all the things other than imported grammars
    164         List<File> files = getNonImportDependenciesFileList();
    165 
    166         // Handle imported grammars
    167         List<Grammar> imports =
    168                 grammar.composite.getDelegates(grammar.composite.getRootGrammar());
    169         for (Grammar g : imports) {
    170             String libdir = tool.getLibraryDirectory();
    171             String fileName = groomQualifiedFileName(libdir, g.fileName);
    172             files.add(new File(fileName));
    173         }
    174 
    175         if (files.size() == 0) {
    176             return null;
    177         }
    178         return files;
    179     }
    180 
    181     /**
    182      * Return a list of File objects that name files ANTLR will read
    183      * to process T.g; This can only be .tokens files and only
    184      * if they use the tokenVocab option.
    185      *
    186      * @return List of dependencies other than imported grammars
    187      */
    188     public List<File> getNonImportDependenciesFileList() {
    189         List<File> files = new ArrayList<File>();
    190 
    191         // handle token vocabulary loads
    192         tokenVocab = (String) grammar.getOption("tokenVocab");
    193         if (tokenVocab != null) {
    194 
    195             File vocabFile = tool.getImportedVocabFile(tokenVocab);
    196             files.add(vocabFile);
    197         }
    198 
    199         return files;
    200     }
    201 
    202     public ST getDependencies() {
    203         loadDependencyTemplates();
    204         ST dependenciesST = templates.getInstanceOf("dependencies");
    205         dependenciesST.add("in", getDependenciesFileList());
    206         dependenciesST.add("out", getGeneratedFileList());
    207         dependenciesST.add("grammarFileName", grammar.fileName);
    208         return dependenciesST;
    209     }
    210 
    211     public void loadDependencyTemplates() {
    212         if (templates != null) return;
    213         String fileName = "org/antlr/tool/templates/depend.stg";
    214         templates = new STGroupFile(fileName);
    215     }
    216 
    217     public String getTokenVocab() {
    218         return tokenVocab;
    219     }
    220 
    221     public CodeGenerator getGenerator() {
    222         return generator;
    223     }
    224 
    225     public String groomQualifiedFileName(String outputDir, String fileName) {
    226         if (outputDir.equals(".")) {
    227             return fileName;
    228         } else if (outputDir.indexOf(' ') >= 0) { // has spaces?
    229             String escSpaces = Utils.replace(outputDir.toString(),
    230                     " ",
    231                     "\\ ");
    232             return escSpaces + File.separator + fileName;
    233         } else {
    234             return outputDir + File.separator + fileName;
    235         }
    236     }
    237 }
    238