added -depend cmd-line option; fixes #71

This commit is contained in:
Terence Parr 2012-09-30 18:25:15 -07:00
parent 68dd847c04
commit 2db3691f6d
5 changed files with 272 additions and 5 deletions

View File

@ -9,6 +9,8 @@ September 30, 2012
* Added a getter to IntStream for the token factory
* Added -depend cmd-line option.
September 29, 2012
* no nongreedy or wildcard in parser.

View File

@ -1,4 +1,5 @@
grammar T;
options {tokenVocab=A;}
s : ID ;
ID : 'a'..'z'+ ;
WS : (' '|'\n') {skip();} ;

View File

@ -1,5 +1,4 @@
/** templates used to generate make-compatible dependencies */
group depend;
/** Generate "f : x, y, z" dependencies for input
* dependencies and generated files. in and out

View File

@ -40,6 +40,7 @@ import org.antlr.v4.automata.ATNFactory;
import org.antlr.v4.automata.LexerATNFactory;
import org.antlr.v4.automata.ParserATNFactory;
import org.antlr.v4.codegen.CodeGenPipeline;
import org.antlr.v4.codegen.CodeGenerator;
import org.antlr.v4.misc.Graph;
import org.antlr.v4.parse.ANTLRLexer;
import org.antlr.v4.parse.ANTLRParser;
@ -50,6 +51,7 @@ import org.antlr.v4.runtime.misc.Nullable;
import org.antlr.v4.semantics.SemanticPipeline;
import org.antlr.v4.tool.ANTLRMessage;
import org.antlr.v4.tool.ANTLRToolListener;
import org.antlr.v4.tool.BuildDependencyGenerator;
import org.antlr.v4.tool.DOTGenerator;
import org.antlr.v4.tool.DefaultToolListener;
import org.antlr.v4.tool.ErrorManager;
@ -126,6 +128,7 @@ public class Tool {
public boolean verbose_dfa = false;
public boolean gen_listener = true;
public boolean gen_visitor = false;
public boolean gen_dependencies = false;
public String genPackage = null;
public Map<String, String> grammarOptions = null;
@ -146,10 +149,10 @@ public class Tool {
new Option("gen_visitor", "-visitor", "generate parse tree visitor"),
new Option("gen_visitor", "-no-visitor", "don't generate parse tree visitor (default)"),
new Option("genPackage", "-package", OptionArgType.STRING, "specify a package/namespace for the generated code"),
new Option("gen_dependencies", "-depend", "generate file dependencies"),
new Option("", "-D<option>=value", "set/override a grammar-level option"),
new Option("saveLexer", "-Xsave-lexer", "save temp lexer file created for combined grammars"),
new Option("saveLexer", "-Xsave-lexer", "save temp lexer file created for combined grammars"),
new Option("launch_ST_inspector", "-XdbgST", "launch StringTemplate visualizer on generated code"),
new Option("force_atn", "-Xforce-atn", "use the ATN simulator for all predictions"),
new Option("log", "-Xlog", "dump lots of logging info to antlr-timestamp.log"),
@ -324,7 +327,21 @@ public class Tool {
for (GrammarRootAST t : sortedGrammars) {
final Grammar g = createGrammar(t);
g.fileName = t.fileName;
process(g, true);
if ( gen_dependencies ) {
BuildDependencyGenerator dep =
new BuildDependencyGenerator(this, g);
/*
List outputFiles = dep.getGeneratedFileList();
List dependents = dep.getDependenciesFileList();
System.out.println("output: "+outputFiles);
System.out.println("dependents: "+dependents);
*/
System.out.println(dep.getDependencies().render());
}
else {
process(g, true);
}
}
}
@ -589,7 +606,7 @@ public class Tool {
// be the base output directory (or current directory if there is not a -o)
//
File outputDir;
if ( fileName.endsWith(".tokens") ) {// CodeGenerator.VOCAB_FILE_EXTENSION)) {
if ( fileName.endsWith(CodeGenerator.VOCAB_FILE_EXTENSION) ) {
outputDir = new File(outputDirectory);
}
else {

View File

@ -0,0 +1,248 @@
/*
* [The "BSD license"]
* Copyright (c) 2012 Terence Parr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.antlr.v4.tool;
import org.antlr.v4.Tool;
import org.antlr.v4.codegen.CodeGenerator;
import org.antlr.v4.misc.Utils;
import org.antlr.v4.parse.ANTLRParser;
import org.stringtemplate.v4.ST;
import org.stringtemplate.v4.STGroup;
import org.stringtemplate.v4.STGroupFile;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
/** Given a grammar file, show the dependencies on .tokens etc...
* Using ST, emit a simple "make compatible" list of dependencies.
* For example, combined grammar T.g (no token import) generates:
*
* TParser.java : T.g
* T.tokens : T.g
* TLexer.java : T.g
*
* If "-lib libdir" is used on command-line with -depend and option
* tokenVocab=A in grammar, then include the path like this:
*
* T.g: libdir/A.tokens
*
* Pay attention to -o as well:
*
* outputdir/TParser.java : T.g
*
* So this output shows what the grammar depends on *and* what it generates.
*
* Operate on one grammar file at a time. If given a list of .g on the
* command-line with -depend, just emit the dependencies. The grammars
* may depend on each other, but the order doesn't matter. Build tools,
* reading in this output, will know how to organize it.
*
* This code was obvious until I removed redundant "./" on front of files
* and had to escape spaces in filenames :(
*
* I literally copied from v3 so might be slightly inconsistent with the
* v4 code base.
*/
public class BuildDependencyGenerator {
protected Tool tool;
protected Grammar g;
protected CodeGenerator generator;
protected STGroup templates;
public BuildDependencyGenerator(Tool tool, Grammar g) {
this.tool = tool;
this.g = g;
String language = g.getOptionString("language");
generator = new CodeGenerator(tool, g, language);
}
/** From T.g return a list of File objects that
* name files ANTLR will emit from T.g.
*/
public List<File> getGeneratedFileList() {
List<File> files = new ArrayList<File>();
// add generated recognizer; e.g., TParser.java
files.add(getOutputFile(generator.getRecognizerFileName()));
// add output vocab file; e.g., T.tokens. This is always generated to
// the base output directory, which will be just . if there is no -o option
//
files.add(getOutputFile(generator.getVocabFileName()));
// are we generating a .h file?
ST headerExtST = null;
ST extST = generator.templates.getInstanceOf("codeFileExtension");
if (generator.templates.isDefined("headerFile")) {
headerExtST = generator.templates.getInstanceOf("headerFileExtension");
String suffix = Grammar.getGrammarTypeToFileNameSuffix(g.getType());
String fileName = g.name + suffix + headerExtST.render();
files.add(getOutputFile(fileName));
}
if ( g.isCombined() ) {
// add autogenerated lexer; e.g., TLexer.java TLexer.h TLexer.tokens
String suffix = Grammar.getGrammarTypeToFileNameSuffix(ANTLRParser.LEXER);
String lexer = g.name + suffix + extST.render();
files.add(getOutputFile(lexer));
// TLexer.h
if (headerExtST != null) {
String header = g.name + suffix + headerExtST.render();
files.add(getOutputFile(header));
}
// for combined, don't generate TLexer.tokens
}
// handle generated files for imported grammars
List<Grammar> imports = g.getAllImportedGrammars();
if ( imports!=null ) {
for (Grammar g : imports) {
// File outputDir = tool.getOutputDirectory(g.fileName);
// String fname = groomQualifiedFileName(outputDir.toString(), g.getRecognizerName() + extST.render());
// files.add(new File(outputDir, fname));
files.add(getOutputFile(g.fileName));
}
}
if (files.isEmpty()) {
return null;
}
return files;
}
public File getOutputFile(String fileName) {
File outputDir = tool.getOutputDirectory(g.fileName);
if ( outputDir.toString().equals(".") ) {
// pay attention to -o then
outputDir = tool.getOutputDirectory(fileName);
}
if ( outputDir.toString().equals(".") ) {
return new File(fileName);
}
if (outputDir.getName().equals(".")) {
String fname = outputDir.toString();
int dot = fname.lastIndexOf('.');
outputDir = new File(outputDir.toString().substring(0,dot));
}
if (outputDir.getName().indexOf(' ') >= 0) { // has spaces?
String escSpaces = Utils.replace(outputDir.toString(),
" ",
"\\ ");
outputDir = new File(escSpaces);
}
return new File(outputDir, fileName);
}
/**
* Return a list of File objects that name files ANTLR will read
* to process T.g; This can be .tokens files if the grammar uses the tokenVocab option
* as well as any imported grammar files.
*/
public List<File> getDependenciesFileList() {
// Find all the things other than imported grammars
List<File> files = getNonImportDependenciesFileList();
// Handle imported grammars
List<Grammar> imports = g.getAllImportedGrammars();
if ( imports!=null ) {
for (Grammar g : imports) {
String libdir = tool.libDirectory;
String fileName = groomQualifiedFileName(libdir, g.fileName);
files.add(new File(fileName));
}
}
if (files.isEmpty()) {
return null;
}
return files;
}
/**
* Return a list of File objects that name files ANTLR will read
* to process T.g; This can only be .tokens files and only
* if they use the tokenVocab option.
*
* @return List of dependencies other than imported grammars
*/
public List<File> getNonImportDependenciesFileList() {
List<File> files = new ArrayList<File>();
// handle token vocabulary loads
String tokenVocab = g.getOptionString("tokenVocab");
if (tokenVocab != null) {
String fileName =
tokenVocab + CodeGenerator.VOCAB_FILE_EXTENSION;
File vocabFile;
if ( tool.libDirectory.equals(".") ) {
vocabFile = new File(fileName);
}
else {
vocabFile = new File(tool.libDirectory, fileName);
}
files.add(vocabFile);
}
return files;
}
public ST getDependencies() {
loadDependencyTemplates();
ST dependenciesST = templates.getInstanceOf("dependencies");
dependenciesST.add("in", getDependenciesFileList());
dependenciesST.add("out", getGeneratedFileList());
dependenciesST.add("grammarFileName", g.fileName);
return dependenciesST;
}
public void loadDependencyTemplates() {
if (templates != null) return;
String fileName = "org/antlr/v4/tool/templates/depend.stg";
templates = new STGroupFile(fileName);
}
public CodeGenerator getGenerator() {
return generator;
}
public String groomQualifiedFileName(String outputDir, String fileName) {
if (outputDir.equals(".")) {
return fileName;
}
else if (outputDir.indexOf(' ') >= 0) { // has spaces?
String escSpaces = Utils.replace(outputDir,
" ",
"\\ ");
return escSpaces + File.separator + fileName;
}
else {
return outputDir + File.separator + fileName;
}
}
}