diff --git a/tool/resources/org/antlr/v4/tool/templates/codegen/Java/Java.stg b/tool/resources/org/antlr/v4/tool/templates/codegen/Java/Java.stg index 883870a70..036d0aba1 100644 --- a/tool/resources/org/antlr/v4/tool/templates/codegen/Java/Java.stg +++ b/tool/resources/org/antlr/v4/tool/templates/codegen/Java/Java.stg @@ -35,6 +35,15 @@ import java.util.ArrayList; >> +ListenerFile(listener) ::= << +@SuppressWarnings({"all", "warnings", "unchecked", "unused"}) +public interface { + _ctx ctx); +void exitRule(_ctx ctx);}; separator="\n"> +} +>> + Parser(parser, scopes, funcs, atn, sempredFuncs) ::= << @SuppressWarnings({"all", "warnings", "unchecked", "unused"}) public class extends Parser { @@ -415,10 +424,8 @@ public static class extends ParserRuleContext { super(parent, state); = ;}; separator="\n"> } - /* public void enterRule(TListener listener) { listener.enterRule(this); } public void exitRule(TListener listener) { listener.exitRule(this); } - */ } >> diff --git a/tool/src/org/antlr/v4/codegen/CodeGenPipeline.java b/tool/src/org/antlr/v4/codegen/CodeGenPipeline.java index 5732e0f19..be9feb169 100644 --- a/tool/src/org/antlr/v4/codegen/CodeGenPipeline.java +++ b/tool/src/org/antlr/v4/codegen/CodeGenPipeline.java @@ -30,17 +30,25 @@ package org.antlr.v4.codegen; import org.antlr.v4.tool.Grammar; -import org.stringtemplate.v4.ST; public class CodeGenPipeline { Grammar g; + public CodeGenPipeline(Grammar g) { this.g = g; } + public void process() { CodeGenerator gen = new CodeGenerator(g); - ST outputFileST = gen.generate(); - gen.write(outputFileST); + if ( g.isLexer() ) { + gen.writeRecognizer(gen.generateLexer()); + } + else { + gen.writeRecognizer(gen.generateParser()); + gen.writeListener(gen.generateListener()); + gen.writeHeaderFile(); + } + gen.writeVocabFile(); } } diff --git a/tool/src/org/antlr/v4/codegen/CodeGenerator.java b/tool/src/org/antlr/v4/codegen/CodeGenerator.java index e2430dbd4..41c1225dd 100644 --- a/tool/src/org/antlr/v4/codegen/CodeGenerator.java +++ b/tool/src/org/antlr/v4/codegen/CodeGenerator.java @@ -32,12 +32,16 @@ package org.antlr.v4.codegen; import org.antlr.v4.Tool; import org.antlr.v4.codegen.model.OutputModelObject; import org.antlr.v4.runtime.Token; -import org.antlr.v4.tool.*; +import org.antlr.v4.tool.ErrorType; +import org.antlr.v4.tool.Grammar; import org.stringtemplate.v4.*; -import java.io.*; -import java.lang.reflect.*; -import java.util.*; +import java.io.IOException; +import java.io.Writer; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.Map; /** General controller for code gen. Can instantiate sub generator(s). */ @@ -48,6 +52,8 @@ public class CodeGenerator { "=\n}>" + "=\n}>"; + public OutputModelObject outputModel; + public Grammar g; public Tool tool; public Target target; @@ -108,10 +114,47 @@ public class CodeGenerator { } } - public ST generate() { - OutputModelFactory factory; - if ( g.isLexer() ) factory = new LexerFactory(this); - else factory = new ParserFactory(this); +// public void buildOutputModel() { +// OutputModelFactory factory; +// if ( g.isLexer() ) factory = new LexerFactory(this); +// else factory = new ParserFactory(this); +// +// // TODO: let someone add their own factory? +// +// // CREATE OUTPUT MODEL FROM GRAMMAR OBJ AND AST WITHIN RULES +// OutputModelController controller = new OutputModelController(factory); +// if ( g.hasASTOption() ) { +// controller.addExtension( new ParserASTExtension(factory) ); +// } +// factory.setController(controller); +// +// if ( g.isLexer() ) outputModel = controller.buildLexerOutputModel(); +// else outputModel = controller.buildParserOutputModel(); +// } + + // CREATE TEMPLATES BY WALKING MODEL + public ST generateLexer() { + OutputModelFactory factory = new LexerFactory(this); + + // CREATE OUTPUT MODEL FROM GRAMMAR OBJ AND AST WITHIN RULES + OutputModelController controller = new OutputModelController(factory); + factory.setController(controller); + + outputModel = controller.buildLexerOutputModel(); + + OutputModelWalker walker = new OutputModelWalker(tool, templates); + ST st = walker.walk(outputModel); + + if ( tool.launch_ST_inspector ) { + st.inspect(); + //if ( templates.isDefined("headerFile") ) headerFileST.inspect(); + } + + return st; + } + + public ST generateParser() { + OutputModelFactory factory = new ParserFactory(this); // TODO: let someone add their own factory? @@ -122,11 +165,33 @@ public class CodeGenerator { } factory.setController(controller); - OutputModelObject outputModel; - if ( g.isLexer() ) outputModel = controller.buildLexerOutputModel(); - else outputModel = controller.buildParserOutputModel(); + outputModel = controller.buildParserOutputModel(); + + OutputModelWalker walker = new OutputModelWalker(tool, templates); + ST st = walker.walk(outputModel); + + if ( tool.launch_ST_inspector ) { + st.inspect(); + //if ( templates.isDefined("headerFile") ) headerFileST.inspect(); + } + + return st; + } + + public ST generateListener() { + OutputModelFactory factory = new ParserFactory(this); + + // TODO: let someone add their own factory? + + // CREATE OUTPUT MODEL FROM GRAMMAR OBJ AND AST WITHIN RULES + OutputModelController controller = new OutputModelController(factory); + if ( g.hasASTOption() ) { + controller.addExtension( new ParserASTExtension(factory) ); + } + factory.setController(controller); + + outputModel = controller.buildListenerOutputModel(); - // CREATE TEMPLATES BY WALKING MODEL OutputModelWalker walker = new OutputModelWalker(tool, templates); ST st = walker.walk(outputModel); @@ -170,42 +235,73 @@ public class CodeGenerator { return vocabFileST; } - public void write(ST outputFileST) { - // WRITE FILES - String fileName = "unknown"; - try { - fileName = getRecognizerFileName(); - target.genRecognizerFile(g,outputFileST); - if ( templates.isDefined("headerFile") ) { - fileName = getHeaderFileName(); - ST extST = templates.getInstanceOf("headerFileExtension"); - ST headerFileST = null; - target.genRecognizerHeaderFile(g,headerFileST,extST.render(lineWidth)); - } - // write out the vocab interchange file; used by antlr, - // does not change per target - ST tokenVocabSerialization = getTokenVocabOutput(); - fileName = getVocabFileName(); - if ( fileName!=null ) { - write(tokenVocabSerialization, fileName); - } - } - catch (IOException ioe) { - tool.errMgr.toolError(ErrorType.CANNOT_WRITE_FILE, - ioe, - fileName); + public void writeRecognizer(ST outputFileST) { + target.genFile(g, outputFileST, getRecognizerFileName()); + } + + public void writeListener(ST outputFileST) { + target.genFile(g,outputFileST,getListenerFileName()); + } + + public void writeHeaderFile() { + String fileName = getHeaderFileName(); + if ( fileName==null ) return; + if ( templates.isDefined("headerFile") ) { + ST extST = templates.getInstanceOf("headerFileExtension"); + ST headerFileST = null; + // TODO: don't hide this header file generation here! + target.genRecognizerHeaderFile(g,headerFileST,extST.render(lineWidth)); } } - public void write(ST code, String fileName) throws IOException { - long start = System.currentTimeMillis(); - Writer w = tool.getOutputFileWriter(g, fileName); - STWriter wr = new AutoIndentWriter(w); - wr.setLineWidth(lineWidth); - code.write(wr); - w.close(); - long stop = System.currentTimeMillis(); - System.out.println("render time for "+fileName+": "+(int)(stop-start)+"ms"); + public void writeVocabFile() { + // write out the vocab interchange file; used by antlr, + // does not change per target + ST tokenVocabSerialization = getTokenVocabOutput(); + String fileName = getVocabFileName(); + if ( fileName!=null ) { + write(tokenVocabSerialization, fileName); + } + } + +// public void write(ST outputFileST) { +// // WRITE FILES +// String fileName = "unknown"; +// try { +// fileName = getRecognizerFileName(); +// target.genRecognizerFile(g,outputFileST); +// writeHeaderFile(); +// // write out the vocab interchange file; used by antlr, +// // does not change per target +// ST tokenVocabSerialization = getTokenVocabOutput(); +// fileName = getVocabFileName(); +// if ( fileName!=null ) { +// write(tokenVocabSerialization, fileName); +// } +// } +// catch (IOException ioe) { +// tool.errMgr.toolError(ErrorType.CANNOT_WRITE_FILE, +// ioe, +// fileName); +// } +// } + + public void write(ST code, String fileName) { + try { + long start = System.currentTimeMillis(); + Writer w = tool.getOutputFileWriter(g, fileName); + STWriter wr = new AutoIndentWriter(w); + wr.setLineWidth(lineWidth); + code.write(wr); + w.close(); + long stop = System.currentTimeMillis(); + System.out.println("render time for "+fileName+": "+(int)(stop-start)+"ms"); + } + catch (IOException ioe) { + tool.errMgr.toolError(ErrorType.CANNOT_WRITE_FILE, + ioe, + fileName); + } } /** Generate TParser.java and TLexer.java from T.g if combined, else @@ -217,6 +313,15 @@ public class CodeGenerator { return recognizerName+extST.render(); } + /** A given grammar T, return the listener name such as + * TListener.java, if we're using the Java target. + */ + public String getListenerFileName() { + ST extST = templates.getInstanceOf("codeFileExtension"); + String listenerName = g.name + "Listener"; + return listenerName+extST.render(); + } + /** What is the name of the vocab file generated for this grammar? * Returns null if no .tokens file should be generated. */ @@ -226,6 +331,7 @@ public class CodeGenerator { public String getHeaderFileName() { ST extST = templates.getInstanceOf("headerFileExtension"); + if ( extST==null ) return null; String recognizerName = g.getRecognizerName(); return recognizerName+extST.render(); } diff --git a/tool/src/org/antlr/v4/codegen/OutputModelController.java b/tool/src/org/antlr/v4/codegen/OutputModelController.java index 9e78cbdb3..bfde688b1 100644 --- a/tool/src/org/antlr/v4/codegen/OutputModelController.java +++ b/tool/src/org/antlr/v4/codegen/OutputModelController.java @@ -33,10 +33,13 @@ import org.antlr.runtime.tree.CommonTreeNodeStream; import org.antlr.v4.codegen.model.*; import org.antlr.v4.codegen.model.ast.*; import org.antlr.v4.codegen.model.decl.CodeBlock; -import org.antlr.v4.parse.*; +import org.antlr.v4.parse.ANTLRParser; +import org.antlr.v4.parse.GrammarASTAdaptor; import org.antlr.v4.tool.*; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; /** This receives events from SourceGenTriggers.g and asks factory to do work. * Then runs extensions in order on resulting SrcOps to get final list. @@ -102,6 +105,11 @@ public class OutputModelController { return file; } + public OutputModelObject buildListenerOutputModel() { + CodeGenerator gen = delegate.getGenerator(); + return new ListenerFile(delegate, gen.getListenerFileName()); + } + public ParserFile parserFile(String fileName) { ParserFile f = delegate.parserFile(fileName); for (CodeGeneratorExtension ext : extensions) f = ext.parserFile(f); @@ -115,7 +123,7 @@ public class OutputModelController { } public LexerFile lexerFile(String fileName) { - return new LexerFile(delegate, getGenerator().getRecognizerFileName()); + return new LexerFile(delegate, fileName); } public Lexer lexer(LexerFile file) { diff --git a/tool/src/org/antlr/v4/codegen/Target.java b/tool/src/org/antlr/v4/codegen/Target.java index a50125609..f1e791dc8 100644 --- a/tool/src/org/antlr/v4/codegen/Target.java +++ b/tool/src/org/antlr/v4/codegen/Target.java @@ -31,12 +31,13 @@ package org.antlr.v4.codegen; import org.antlr.v4.codegen.model.RuleFunction; import org.antlr.v4.parse.ANTLRParser; -import org.antlr.v4.runtime.*; -import org.antlr.v4.tool.*; +import org.antlr.v4.runtime.Lexer; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.tool.Grammar; +import org.antlr.v4.tool.GrammarAST; +import org.antlr.v4.tool.Rule; import org.stringtemplate.v4.ST; -import java.io.IOException; - /** */ public class Target { /** For pure strings of Java 16-bit unicode char, how can we display @@ -66,18 +67,23 @@ public class Target { this.gen = gen; } - protected void genRecognizerFile(Grammar g, - ST outputFileST) - throws IOException + protected void genFile(Grammar g, + ST outputFileST, + String fileName) { - String fileName = gen.getRecognizerFileName(); + gen.write(outputFileST, fileName); + } + + protected void genListenerFile(Grammar g, + ST outputFileST) + { + String fileName = gen.getListenerFileName(); gen.write(outputFileST, fileName); } protected void genRecognizerHeaderFile(Grammar g, ST headerFileST, String extName) // e.g., ".h" - throws IOException { // no header file by default } diff --git a/tool/src/org/antlr/v4/codegen/model/ListenerFile.java b/tool/src/org/antlr/v4/codegen/model/ListenerFile.java new file mode 100644 index 000000000..5b014d25b --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/ListenerFile.java @@ -0,0 +1,24 @@ +package org.antlr.v4.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.tool.Grammar; +import org.antlr.v4.tool.Rule; + +import java.util.Collection; + +/** A model object representing a parse tree listener file. + * These are the rules specific events triggered by a parse tree visitor. + */ +public class ListenerFile extends OutputModelObject { + public String fileName; + public String grammarName; + public Collection rules; + + public ListenerFile(OutputModelFactory factory, String fileName) { + super(factory); + this.fileName = fileName; + Grammar g = factory.getGrammar(); + grammarName = g.name; + rules = g.rules.values(); + } +} diff --git a/tool/test/org/antlr/v4/test/TestActionTranslation.java b/tool/test/org/antlr/v4/test/TestActionTranslation.java index 2beddd696..8e23fe066 100644 --- a/tool/test/org/antlr/v4/test/TestActionTranslation.java +++ b/tool/test/org/antlr/v4/test/TestActionTranslation.java @@ -1,12 +1,9 @@ package org.antlr.v4.test; import org.antlr.runtime.RecognitionException; -import org.antlr.v4.automata.*; import org.antlr.v4.codegen.CodeGenerator; import org.antlr.v4.semantics.SemanticPipeline; -import org.antlr.v4.tool.*; import org.junit.Test; -import org.stringtemplate.v4.*; /** */ public class TestActionTranslation extends BaseTest { @@ -423,7 +420,7 @@ public class TestActionTranslation extends BaseTest { g.atn = factory.createATN(); CodeGenerator gen = new CodeGenerator(g); - ST outputFileST = gen.generate(); + ST outputFileST = gen.generateRecognizer(); String output = outputFileST.render(); //System.out.println(output); String b = "#" + actionName + "#"; diff --git a/tool/test/org/antlr/v4/test/TestLexerAttributes.java b/tool/test/org/antlr/v4/test/TestLexerAttributes.java index d567a9e65..c2a925659 100644 --- a/tool/test/org/antlr/v4/test/TestLexerAttributes.java +++ b/tool/test/org/antlr/v4/test/TestLexerAttributes.java @@ -17,7 +17,7 @@ public class TestLexerAttributes extends BaseTest { Tool antlr = new Tool(); antlr.process(g,false); CodeGenerator gen = new CodeGenerator(g); - ST outputFileST = gen.generate(); + ST outputFileST = gen.generateRecognizer(); String output = outputFileST.render(); int start = output.indexOf('#'); int end = output.lastIndexOf('#');