diff options
author | Emmanuel Bourg <ebourg@apache.org> | 2015-09-30 23:36:28 +0200 |
---|---|---|
committer | Emmanuel Bourg <ebourg@apache.org> | 2015-09-30 23:36:28 +0200 |
commit | bbb7bd0887717b544df1a50e3842ca64c3489941 (patch) | |
tree | 3423dfb0f105f33e5752dd43bb0cd83728ecd2d7 /tool/src/org/antlr/v4 | |
parent | cdbf09fa7db7d25d9ce65b92d0398c3a54a5fd5d (diff) |
Imported Upstream version 4.5.1
Diffstat (limited to 'tool/src/org/antlr/v4')
226 files changed, 30544 insertions, 0 deletions
diff --git a/tool/src/org/antlr/v4/Tool.java b/tool/src/org/antlr/v4/Tool.java new file mode 100644 index 0000000..2441268 --- /dev/null +++ b/tool/src/org/antlr/v4/Tool.java @@ -0,0 +1,891 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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; + +import org.antlr.runtime.ANTLRFileStream; +import org.antlr.runtime.ANTLRStringStream; +import org.antlr.runtime.CharStream; +import org.antlr.runtime.CommonTokenStream; +import org.antlr.runtime.ParserRuleReturnScope; +import org.antlr.runtime.RecognitionException; +import org.antlr.v4.analysis.AnalysisPipeline; +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.ANTLRParser; +import org.antlr.v4.parse.GrammarASTAdaptor; +import org.antlr.v4.parse.GrammarTreeVisitor; +import org.antlr.v4.parse.ToolANTLRLexer; +import org.antlr.v4.parse.ToolANTLRParser; +import org.antlr.v4.parse.v3TreeGrammarException; +import org.antlr.v4.runtime.RuntimeMetaData; +import org.antlr.v4.runtime.misc.LogManager; +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; +import org.antlr.v4.tool.ErrorType; +import org.antlr.v4.tool.Grammar; +import org.antlr.v4.tool.GrammarTransformPipeline; +import org.antlr.v4.tool.LexerGrammar; +import org.antlr.v4.tool.Rule; +import org.antlr.v4.tool.ast.ActionAST; +import org.antlr.v4.tool.ast.GrammarAST; +import org.antlr.v4.tool.ast.GrammarASTErrorNode; +import org.antlr.v4.tool.ast.GrammarRootAST; +import org.antlr.v4.tool.ast.RuleAST; +import org.antlr.v4.tool.ast.TerminalAST; +import org.stringtemplate.v4.STGroup; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.StringWriter; +import java.io.Writer; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; + +public class Tool { + public static final String VERSION; + static { + // Assigned in a static{} block to prevent the field from becoming a + // compile-time constant + VERSION = RuntimeMetaData.VERSION; + } + + public static final String GRAMMAR_EXTENSION = ".g4"; + public static final String LEGACY_GRAMMAR_EXTENSION = ".g"; + + public static final List<String> ALL_GRAMMAR_EXTENSIONS = + Collections.unmodifiableList(Arrays.asList(GRAMMAR_EXTENSION, LEGACY_GRAMMAR_EXTENSION)); + + public static enum OptionArgType { NONE, STRING } // NONE implies boolean + public static class Option { + String fieldName; + String name; + OptionArgType argType; + String description; + + public Option(String fieldName, String name, String description) { + this(fieldName, name, OptionArgType.NONE, description); + } + + public Option(String fieldName, String name, OptionArgType argType, String description) { + this.fieldName = fieldName; + this.name = name; + this.argType = argType; + this.description = description; + } + } + + // fields set by option manager + + public File inputDirectory; // used by mvn plugin but not set by tool itself. + public String outputDirectory; + public String libDirectory; + public boolean generate_ATN_dot = false; + public String grammarEncoding = null; // use default locale's encoding + public String msgFormat = "antlr"; + public boolean launch_ST_inspector = false; + public boolean ST_inspector_wait_for_close = false; + public boolean force_atn = false; + public boolean log = 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; + public boolean warnings_are_errors = false; + public boolean longMessages = false; + + public static Option[] optionDefs = { + new Option("outputDirectory", "-o", OptionArgType.STRING, "specify output directory where all output is generated"), + new Option("libDirectory", "-lib", OptionArgType.STRING, "specify location of grammars, tokens files"), + new Option("generate_ATN_dot", "-atn", "generate rule augmented transition network diagrams"), + new Option("grammarEncoding", "-encoding", OptionArgType.STRING, "specify grammar file encoding; e.g., euc-jp"), + new Option("msgFormat", "-message-format", OptionArgType.STRING, "specify output style for messages in antlr, gnu, vs2005"), + new Option("longMessages", "-long-messages", "show exception details when available for errors and warnings"), + new Option("gen_listener", "-listener", "generate parse tree listener (default)"), + new Option("gen_listener", "-no-listener", "don't generate parse tree listener"), + 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("warnings_are_errors", "-Werror", "treat warnings as errors"), + new Option("launch_ST_inspector", "-XdbgST", "launch StringTemplate visualizer on generated code"), + new Option("ST_inspector_wait_for_close", "-XdbgSTWait", "wait for STViz to close before continuing"), + 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"), + }; + + // helper vars for option management + protected boolean haveOutputDir = false; + protected boolean return_dont_exit = false; + + // The internal options are for my use on the command line during dev + public static boolean internalOption_PrintGrammarTree = false; + public static boolean internalOption_ShowATNConfigsInDFA = false; + + + public final String[] args; + + protected List<String> grammarFiles = new ArrayList<String>(); + + public ErrorManager errMgr; + public LogManager logMgr = new LogManager(); + + List<ANTLRToolListener> listeners = new CopyOnWriteArrayList<ANTLRToolListener>(); + + /** Track separately so if someone adds a listener, it's the only one + * instead of it and the default stderr listener. + */ + DefaultToolListener defaultListener = new DefaultToolListener(this); + + public static void main(String[] args) { + Tool antlr = new Tool(args); + if ( args.length == 0 ) { antlr.help(); antlr.exit(0); } + + try { + antlr.processGrammarsOnCommandLine(); + } + finally { + if ( antlr.log ) { + try { + String logname = antlr.logMgr.save(); + System.out.println("wrote "+logname); + } + catch (IOException ioe) { + antlr.errMgr.toolError(ErrorType.INTERNAL_ERROR, ioe); + } + } + } + if ( antlr.return_dont_exit ) return; + + if (antlr.errMgr.getNumErrors() > 0) { + antlr.exit(1); + } + antlr.exit(0); + } + + public Tool() { this(null); } + + public Tool(String[] args) { + this.args = args; + errMgr = new ErrorManager(this); + errMgr.setFormat(msgFormat); + handleArgs(); + } + + protected void handleArgs() { + int i=0; + while ( args!=null && i<args.length ) { + String arg = args[i]; + i++; + if ( arg.startsWith("-D") ) { // -Dlanguage=Java syntax + handleOptionSetArg(arg); + continue; + } + if ( arg.charAt(0)!='-' ) { // file name + if ( !grammarFiles.contains(arg) ) grammarFiles.add(arg); + continue; + } + boolean found = false; + for (Option o : optionDefs) { + if ( arg.equals(o.name) ) { + found = true; + String argValue = null; + if ( o.argType==OptionArgType.STRING ) { + argValue = args[i]; + i++; + } + // use reflection to set field + Class<? extends Tool> c = this.getClass(); + try { + Field f = c.getField(o.fieldName); + if ( argValue==null ) { + if ( arg.startsWith("-no-") ) f.setBoolean(this, false); + else f.setBoolean(this, true); + } + else f.set(this, argValue); + } + catch (Exception e) { + errMgr.toolError(ErrorType.INTERNAL_ERROR, "can't access field "+o.fieldName); + } + } + } + if ( !found ) { + errMgr.toolError(ErrorType.INVALID_CMDLINE_ARG, arg); + } + } + if ( outputDirectory!=null ) { + if (outputDirectory.endsWith("/") || + outputDirectory.endsWith("\\")) { + outputDirectory = + outputDirectory.substring(0, outputDirectory.length() - 1); + } + File outDir = new File(outputDirectory); + haveOutputDir = true; + if (outDir.exists() && !outDir.isDirectory()) { + errMgr.toolError(ErrorType.OUTPUT_DIR_IS_FILE, outputDirectory); + libDirectory = "."; + } + } + else { + outputDirectory = "."; + } + if ( libDirectory!=null ) { + if (libDirectory.endsWith("/") || + libDirectory.endsWith("\\")) { + libDirectory = libDirectory.substring(0, libDirectory.length() - 1); + } + File outDir = new File(libDirectory); + if (!outDir.exists()) { + errMgr.toolError(ErrorType.DIR_NOT_FOUND, libDirectory); + libDirectory = "."; + } + } + else { + libDirectory = "."; + } + if ( launch_ST_inspector ) { + STGroup.trackCreationEvents = true; + return_dont_exit = true; + } + } + + protected void handleOptionSetArg(String arg) { + int eq = arg.indexOf('='); + if ( eq>0 && arg.length()>3 ) { + String option = arg.substring("-D".length(), eq); + String value = arg.substring(eq+1); + if ( value.length()==0 ) { + errMgr.toolError(ErrorType.BAD_OPTION_SET_SYNTAX, arg); + return; + } + if ( Grammar.parserOptions.contains(option) || + Grammar.lexerOptions.contains(option) ) + { + if ( grammarOptions==null ) grammarOptions = new HashMap<String, String>(); + grammarOptions.put(option, value); + } + else { + errMgr.grammarError(ErrorType.ILLEGAL_OPTION, + null, + null, + option); + } + } + else { + errMgr.toolError(ErrorType.BAD_OPTION_SET_SYNTAX, arg); + } + } + + public void processGrammarsOnCommandLine() { + List<GrammarRootAST> sortedGrammars = sortGrammarByTokenVocab(grammarFiles); + + for (GrammarRootAST t : sortedGrammars) { + final Grammar g = createGrammar(t); + g.fileName = t.fileName; + 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 if (errMgr.getNumErrors() == 0) { + process(g, true); + } + } + } + + /** To process a grammar, we load all of its imported grammars into + subordinate grammar objects. Then we merge the imported rules + into the root grammar. If a root grammar is a combined grammar, + we have to extract the implicit lexer. Once all this is done, we + process the lexer first, if present, and then the parser grammar + */ + public void process(Grammar g, boolean gencode) { + g.loadImportedGrammars(); + + GrammarTransformPipeline transform = new GrammarTransformPipeline(g, this); + transform.process(); + + LexerGrammar lexerg; + GrammarRootAST lexerAST; + if ( g.ast!=null && g.ast.grammarType== ANTLRParser.COMBINED && + !g.ast.hasErrors ) + { + lexerAST = transform.extractImplicitLexer(g); // alters g.ast + if ( lexerAST!=null ) { + if (grammarOptions != null) { + lexerAST.cmdLineOptions = grammarOptions; + } + + lexerg = new LexerGrammar(this, lexerAST); + lexerg.fileName = g.fileName; + lexerg.originalGrammar = g; + g.implicitLexer = lexerg; + lexerg.implicitLexerOwner = g; + processNonCombinedGrammar(lexerg, gencode); +// System.out.println("lexer tokens="+lexerg.tokenNameToTypeMap); +// System.out.println("lexer strings="+lexerg.stringLiteralToTypeMap); + } + } + if ( g.implicitLexer!=null ) g.importVocab(g.implicitLexer); +// System.out.println("tokens="+g.tokenNameToTypeMap); +// System.out.println("strings="+g.stringLiteralToTypeMap); + processNonCombinedGrammar(g, gencode); + } + + public void processNonCombinedGrammar(Grammar g, boolean gencode) { + if ( g.ast==null || g.ast.hasErrors ) return; + if ( internalOption_PrintGrammarTree ) System.out.println(g.ast.toStringTree()); + + boolean ruleFail = checkForRuleIssues(g); + if ( ruleFail ) return; + + int prevErrors = errMgr.getNumErrors(); + // MAKE SURE GRAMMAR IS SEMANTICALLY CORRECT (FILL IN GRAMMAR OBJECT) + SemanticPipeline sem = new SemanticPipeline(g); + sem.process(); + + String language = g.getOptionString("language"); + if ( !CodeGenerator.targetExists(language) ) { + errMgr.toolError(ErrorType.CANNOT_CREATE_TARGET_GENERATOR, language); + return; + } + + if ( errMgr.getNumErrors()>prevErrors ) return; + + // BUILD ATN FROM AST + ATNFactory factory; + if ( g.isLexer() ) factory = new LexerATNFactory((LexerGrammar)g); + else factory = new ParserATNFactory(g); + g.atn = factory.createATN(); + + if ( generate_ATN_dot ) generateATNs(g); + + // PERFORM GRAMMAR ANALYSIS ON ATN: BUILD DECISION DFAs + AnalysisPipeline anal = new AnalysisPipeline(g); + anal.process(); + + //if ( generate_DFA_dot ) generateDFAs(g); + + if ( g.tool.getNumErrors()>prevErrors ) return; + + // GENERATE CODE + if ( gencode ) { + CodeGenPipeline gen = new CodeGenPipeline(g); + gen.process(); + } + } + + /** + * Important enough to avoid multiple definitions that we do very early, + * right after AST construction. Also check for undefined rules in + * parser/lexer to avoid exceptions later. Return true if we find multiple + * definitions of the same rule or a reference to an undefined rule or + * parser rule ref in lexer rule. + */ + public boolean checkForRuleIssues(final Grammar g) { + // check for redefined rules + GrammarAST RULES = (GrammarAST)g.ast.getFirstChildWithType(ANTLRParser.RULES); + List<GrammarAST> rules = new ArrayList<GrammarAST>(RULES.getAllChildrenWithType(ANTLRParser.RULE)); + for (GrammarAST mode : g.ast.getAllChildrenWithType(ANTLRParser.MODE)) { + rules.addAll(mode.getAllChildrenWithType(ANTLRParser.RULE)); + } + + boolean redefinition = false; + final Map<String, RuleAST> ruleToAST = new HashMap<String, RuleAST>(); + for (GrammarAST r : rules) { + RuleAST ruleAST = (RuleAST)r; + GrammarAST ID = (GrammarAST)ruleAST.getChild(0); + String ruleName = ID.getText(); + RuleAST prev = ruleToAST.get(ruleName); + if ( prev !=null ) { + GrammarAST prevChild = (GrammarAST)prev.getChild(0); + g.tool.errMgr.grammarError(ErrorType.RULE_REDEFINITION, + g.fileName, + ID.getToken(), + ruleName, + prevChild.getToken().getLine()); + redefinition = true; + continue; + } + ruleToAST.put(ruleName, ruleAST); + } + + // check for undefined rules + class UndefChecker extends GrammarTreeVisitor { + public boolean badref = false; + @Override + public void tokenRef(TerminalAST ref) { + if ("EOF".equals(ref.getText())) { + // this is a special predefined reference + return; + } + + if ( g.isLexer() ) ruleRef(ref, null); + } + + @Override + public void ruleRef(GrammarAST ref, ActionAST arg) { + RuleAST ruleAST = ruleToAST.get(ref.getText()); + String fileName = ref.getToken().getInputStream().getSourceName(); + if (Character.isUpperCase(currentRuleName.charAt(0)) && + Character.isLowerCase(ref.getText().charAt(0))) + { + badref = true; + errMgr.grammarError(ErrorType.PARSER_RULE_REF_IN_LEXER_RULE, + fileName, ref.getToken(), ref.getText(), currentRuleName); + } + else if ( ruleAST==null ) { + badref = true; + errMgr.grammarError(ErrorType.UNDEFINED_RULE_REF, + fileName, ref.token, ref.getText()); + } + } + @Override + public ErrorManager getErrorManager() { return errMgr; } + } + + UndefChecker chk = new UndefChecker(); + chk.visitGrammar(g.ast); + + return redefinition || chk.badref; + } + + public List<GrammarRootAST> sortGrammarByTokenVocab(List<String> fileNames) { +// System.out.println(fileNames); + Graph<String> g = new Graph<String>(); + List<GrammarRootAST> roots = new ArrayList<GrammarRootAST>(); + for (String fileName : fileNames) { + GrammarAST t = parseGrammar(fileName); + if ( t==null || t instanceof GrammarASTErrorNode) continue; // came back as error node + if ( ((GrammarRootAST)t).hasErrors ) continue; + GrammarRootAST root = (GrammarRootAST)t; + roots.add(root); + root.fileName = fileName; + String grammarName = root.getChild(0).getText(); + + GrammarAST tokenVocabNode = findOptionValueAST(root, "tokenVocab"); + // Make grammars depend on any tokenVocab options + if ( tokenVocabNode!=null ) { + String vocabName = tokenVocabNode.getText(); + g.addEdge(grammarName, vocabName); + } + // add cycle to graph so we always process a grammar if no error + // even if no dependency + g.addEdge(grammarName, grammarName); + } + + List<String> sortedGrammarNames = g.sort(); +// System.out.println("sortedGrammarNames="+sortedGrammarNames); + + List<GrammarRootAST> sortedRoots = new ArrayList<GrammarRootAST>(); + for (String grammarName : sortedGrammarNames) { + for (GrammarRootAST root : roots) { + if ( root.getGrammarName().equals(grammarName) ) { + sortedRoots.add(root); + break; + } + } + } + + return sortedRoots; + } + + /** Manually get option node from tree; return null if no defined. */ + public static GrammarAST findOptionValueAST(GrammarRootAST root, String option) { + GrammarAST options = (GrammarAST)root.getFirstChildWithType(ANTLRParser.OPTIONS); + if ( options!=null && options.getChildCount() > 0 ) { + for (Object o : options.getChildren()) { + GrammarAST c = (GrammarAST)o; + if ( c.getType() == ANTLRParser.ASSIGN && + c.getChild(0).getText().equals(option) ) + { + return (GrammarAST)c.getChild(1); + } + } + } + return null; + } + + + /** Given the raw AST of a grammar, create a grammar object + associated with the AST. Once we have the grammar object, ensure + that all nodes in tree referred to this grammar. Later, we will + use it for error handling and generally knowing from where a rule + comes from. + */ + public Grammar createGrammar(GrammarRootAST ast) { + final Grammar g; + if ( ast.grammarType==ANTLRParser.LEXER ) g = new LexerGrammar(this, ast); + else g = new Grammar(this, ast); + + // ensure each node has pointer to surrounding grammar + GrammarTransformPipeline.setGrammarPtr(g, ast); + return g; + } + + public GrammarRootAST parseGrammar(String fileName) { + try { + File file = new File(fileName); + if (!file.isAbsolute()) { + file = new File(inputDirectory, fileName); + } + + ANTLRFileStream in = new ANTLRFileStream(file.getAbsolutePath(), grammarEncoding); + GrammarRootAST t = parse(fileName, in); + return t; + } + catch (IOException ioe) { + errMgr.toolError(ErrorType.CANNOT_OPEN_FILE, ioe, fileName); + } + return null; + } + + /** Convenience method to load and process an ANTLR grammar. Useful + * when creating interpreters. If you need to access to the lexer + * grammar created while processing a combined grammar, use + * getImplicitLexer() on returned grammar. + */ + public Grammar loadGrammar(String fileName) { + GrammarRootAST grammarRootAST = parseGrammar(fileName); + final Grammar g = createGrammar(grammarRootAST); + g.fileName = fileName; + process(g, false); + return g; + } + + private final Map<String, Grammar> importedGrammars = new HashMap<String, Grammar>(); + + /** + * Try current dir then dir of g then lib dir + * @param g + * @param nameNode The node associated with the imported grammar name. + */ + public Grammar loadImportedGrammar(Grammar g, GrammarAST nameNode) throws IOException { + String name = nameNode.getText(); + Grammar imported = importedGrammars.get(name); + if (imported == null) { + g.tool.log("grammar", "load " + name + " from " + g.fileName); + File importedFile = null; + for (String extension : ALL_GRAMMAR_EXTENSIONS) { + importedFile = getImportedGrammarFile(g, name + extension); + if (importedFile != null) { + break; + } + } + + if ( importedFile==null ) { + errMgr.grammarError(ErrorType.CANNOT_FIND_IMPORTED_GRAMMAR, g.fileName, nameNode.getToken(), name); + return null; + } + + String absolutePath = importedFile.getAbsolutePath(); + ANTLRFileStream in = new ANTLRFileStream(absolutePath, grammarEncoding); + GrammarRootAST root = parse(g.fileName, in); + if (root == null) { + return null; + } + + imported = createGrammar(root); + imported.fileName = absolutePath; + importedGrammars.put(root.getGrammarName(), imported); + } + + return imported; + } + + public GrammarRootAST parseGrammarFromString(String grammar) { + return parse("<string>", new ANTLRStringStream(grammar)); + } + + public GrammarRootAST parse(String fileName, CharStream in) { + try { + GrammarASTAdaptor adaptor = new GrammarASTAdaptor(in); + ToolANTLRLexer lexer = new ToolANTLRLexer(in, this); + CommonTokenStream tokens = new CommonTokenStream(lexer); + lexer.tokens = tokens; + ToolANTLRParser p = new ToolANTLRParser(tokens, this); + p.setTreeAdaptor(adaptor); + try { + ParserRuleReturnScope r = p.grammarSpec(); + GrammarAST root = (GrammarAST)r.getTree(); + if ( root instanceof GrammarRootAST) { + ((GrammarRootAST)root).hasErrors = lexer.getNumberOfSyntaxErrors()>0 || p.getNumberOfSyntaxErrors()>0; + assert ((GrammarRootAST)root).tokenStream == tokens; + if ( grammarOptions!=null ) { + ((GrammarRootAST)root).cmdLineOptions = grammarOptions; + } + return ((GrammarRootAST)root); + } + } + catch (v3TreeGrammarException e) { + errMgr.grammarError(ErrorType.V3_TREE_GRAMMAR, fileName, e.location); + } + return null; + } + catch (RecognitionException re) { + // TODO: do we gen errors now? + ErrorManager.internalError("can't generate this message at moment; antlr recovers"); + } + return null; + } + + public void generateATNs(Grammar g) { + DOTGenerator dotGenerator = new DOTGenerator(g); + List<Grammar> grammars = new ArrayList<Grammar>(); + grammars.add(g); + List<Grammar> imported = g.getAllImportedGrammars(); + if ( imported!=null ) grammars.addAll(imported); + for (Grammar ig : grammars) { + for (Rule r : ig.rules.values()) { + try { + String dot = dotGenerator.getDOT(g.atn.ruleToStartState[r.index], g.isLexer()); + if (dot != null) { + writeDOTFile(g, r, dot); + } + } + catch (IOException ioe) { + errMgr.toolError(ErrorType.CANNOT_WRITE_FILE, ioe); + } + } + } + } + + /** This method is used by all code generators to create new output + * files. If the outputDir set by -o is not present it will be created. + * The final filename is sensitive to the output directory and + * the directory where the grammar file was found. If -o is /tmp + * and the original grammar file was foo/t.g4 then output files + * go in /tmp/foo. + * + * The output dir -o spec takes precedence if it's absolute. + * E.g., if the grammar file dir is absolute the output dir is given + * precendence. "-o /tmp /usr/lib/t.g4" results in "/tmp/T.java" as + * output (assuming t.g4 holds T.java). + * + * If no -o is specified, then just write to the directory where the + * grammar file was found. + * + * If outputDirectory==null then write a String. + */ + public Writer getOutputFileWriter(Grammar g, String fileName) throws IOException { + if (outputDirectory == null) { + return new StringWriter(); + } + // output directory is a function of where the grammar file lives + // for subdir/T.g4, you get subdir here. Well, depends on -o etc... + File outputDir = getOutputDirectory(g.fileName); + File outputFile = new File(outputDir, fileName); + + if (!outputDir.exists()) { + outputDir.mkdirs(); + } + FileOutputStream fos = new FileOutputStream(outputFile); + OutputStreamWriter osw; + if ( grammarEncoding!=null ) { + osw = new OutputStreamWriter(fos, grammarEncoding); + } + else { + osw = new OutputStreamWriter(fos); + } + return new BufferedWriter(osw); + } + + public File getImportedGrammarFile(Grammar g, String fileName) { + File importedFile = new File(inputDirectory, fileName); + if ( !importedFile.exists() ) { + File gfile = new File(g.fileName); + String parentDir = gfile.getParent(); + importedFile = new File(parentDir, fileName); + if ( !importedFile.exists() ) { // try in lib dir + importedFile = new File(libDirectory, fileName); + if ( !importedFile.exists() ) { + return null; + } + } + } + return importedFile; + } + + /** + * Return the location where ANTLR will generate output files for a given + * file. This is a base directory and output files will be relative to + * here in some cases such as when -o option is used and input files are + * given relative to the input directory. + * + * @param fileNameWithPath path to input source + */ + public File getOutputDirectory(String fileNameWithPath) { + File outputDir; + String fileDirectory; + + // Some files are given to us without a PATH but should should + // still be written to the output directory in the relative path of + // the output directory. The file directory is either the set of sub directories + // or just or the relative path recorded for the parent grammar. This means + // that when we write the tokens files, or the .java files for imported grammars + // taht we will write them in the correct place. + if (fileNameWithPath.lastIndexOf(File.separatorChar) == -1) { + // No path is included in the file name, so make the file + // directory the same as the parent grammar (which might sitll be just "" + // but when it is not, we will write the file in the correct place. + fileDirectory = "."; + + } + else { + fileDirectory = fileNameWithPath.substring(0, fileNameWithPath.lastIndexOf(File.separatorChar)); + } + if ( haveOutputDir ) { + // -o /tmp /var/lib/t.g4 => /tmp/T.java + // -o subdir/output /usr/lib/t.g4 => subdir/output/T.java + // -o . /usr/lib/t.g4 => ./T.java + if (fileDirectory != null && + (new File(fileDirectory).isAbsolute() || + fileDirectory.startsWith("~"))) { // isAbsolute doesn't count this :( + // somebody set the dir, it takes precendence; write new file there + outputDir = new File(outputDirectory); + } + else { + // -o /tmp subdir/t.g4 => /tmp/subdir/t.g4 + if (fileDirectory != null) { + outputDir = new File(outputDirectory, fileDirectory); + } + else { + outputDir = new File(outputDirectory); + } + } + } + else { + // they didn't specify a -o dir so just write to location + // where grammar is, absolute or relative, this will only happen + // with command line invocation as build tools will always + // supply an output directory. + outputDir = new File(fileDirectory); + } + return outputDir; + } + + protected void writeDOTFile(Grammar g, Rule r, String dot) throws IOException { + writeDOTFile(g, r.g.name + "." + r.name, dot); + } + + protected void writeDOTFile(Grammar g, String name, String dot) throws IOException { + Writer fw = getOutputFileWriter(g, name + ".dot"); + try { + fw.write(dot); + } + finally { + fw.close(); + } + } + + public void help() { + info("ANTLR Parser Generator Version " + Tool.VERSION); + for (Option o : optionDefs) { + String name = o.name + (o.argType!=OptionArgType.NONE? " ___" : ""); + String s = String.format(" %-19s %s", name, o.description); + info(s); + } + } + + public void log(String component, String msg) { logMgr.log(component, msg); } + public void log(String msg) { log(null, msg); } + + public int getNumErrors() { return errMgr.getNumErrors(); } + + public void addListener(ANTLRToolListener tl) { + if ( tl!=null ) listeners.add(tl); + } + public void removeListener(ANTLRToolListener tl) { listeners.remove(tl); } + public void removeListeners() { listeners.clear(); } + public List<ANTLRToolListener> getListeners() { return listeners; } + + public void info(String msg) { + if ( listeners.isEmpty() ) { + defaultListener.info(msg); + return; + } + for (ANTLRToolListener l : listeners) l.info(msg); + } + public void error(ANTLRMessage msg) { + if ( listeners.isEmpty() ) { + defaultListener.error(msg); + return; + } + for (ANTLRToolListener l : listeners) l.error(msg); + } + public void warning(ANTLRMessage msg) { + if ( listeners.isEmpty() ) { + defaultListener.warning(msg); + } + else { + for (ANTLRToolListener l : listeners) l.warning(msg); + } + + if (warnings_are_errors) { + errMgr.emit(ErrorType.WARNING_TREATED_AS_ERROR, new ANTLRMessage(ErrorType.WARNING_TREATED_AS_ERROR)); + } + } + + public void version() { + info("ANTLR Parser Generator Version " + VERSION); + } + + public void exit(int e) { System.exit(e); } + + public void panic() { throw new Error("ANTLR panic"); } + +} diff --git a/tool/src/org/antlr/v4/analysis/AnalysisPipeline.java b/tool/src/org/antlr/v4/analysis/AnalysisPipeline.java new file mode 100644 index 0000000..5bd84be --- /dev/null +++ b/tool/src/org/antlr/v4/analysis/AnalysisPipeline.java @@ -0,0 +1,118 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.analysis; + +import org.antlr.v4.misc.Utils; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.atn.DecisionState; +import org.antlr.v4.runtime.atn.LL1Analyzer; +import org.antlr.v4.runtime.misc.IntervalSet; +import org.antlr.v4.tool.ErrorType; +import org.antlr.v4.tool.Grammar; +import org.antlr.v4.tool.Rule; +import org.antlr.v4.tool.ast.GrammarAST; + +import java.util.ArrayList; +import java.util.Arrays; + +public class AnalysisPipeline { + public Grammar g; + + public AnalysisPipeline(Grammar g) { + this.g = g; + } + + public void process() { + // LEFT-RECURSION CHECK + LeftRecursionDetector lr = new LeftRecursionDetector(g, g.atn); + lr.check(); + if ( !lr.listOfRecursiveCycles.isEmpty() ) return; // bail out + + if (g.isLexer()) { + processLexer(); + } else { + // BUILD DFA FOR EACH DECISION + processParser(); + } + } + + protected void processLexer() { + // make sure all non-fragment lexer rules must match at least one symbol + for (Rule rule : g.rules.values()) { + if (rule.isFragment()) { + continue; + } + + LL1Analyzer analyzer = new LL1Analyzer(g.atn); + IntervalSet look = analyzer.LOOK(g.atn.ruleToStartState[rule.index], null); + if (look.contains(Token.EPSILON)) { + g.tool.errMgr.grammarError(ErrorType.EPSILON_TOKEN, g.fileName, ((GrammarAST)rule.ast.getChild(0)).getToken(), rule.name); + } + } + } + + protected void processParser() { + g.decisionLOOK = new ArrayList<IntervalSet[]>(g.atn.getNumberOfDecisions()+1); + for (DecisionState s : g.atn.decisionToState) { + g.tool.log("LL1", "\nDECISION "+s.decision+" in rule "+g.getRule(s.ruleIndex).name); + IntervalSet[] look; + if ( s.nonGreedy ) { // nongreedy decisions can't be LL(1) + look = new IntervalSet[s.getNumberOfTransitions()+1]; + } + else { + LL1Analyzer anal = new LL1Analyzer(g.atn); + look = anal.getDecisionLookahead(s); + g.tool.log("LL1", "look=" + Arrays.toString(look)); + } + + assert s.decision + 1 >= g.decisionLOOK.size(); + Utils.setSize(g.decisionLOOK, s.decision+1); + g.decisionLOOK.set(s.decision, look); + g.tool.log("LL1", "LL(1)? " + disjoint(look)); + } + } + + /** Return whether lookahead sets are disjoint; no lookahead => not disjoint */ + public static boolean disjoint(IntervalSet[] altLook) { + boolean collision = false; + IntervalSet combined = new IntervalSet(); + if ( altLook==null ) return false; + for (IntervalSet look : altLook) { + if ( look==null ) return false; // lookahead must've computation failed + if ( !look.and(combined).isNil() ) { + collision = true; + break; + } + combined.addAll(look); + } + return !collision; + } +} diff --git a/tool/src/org/antlr/v4/analysis/LeftRecursionDetector.java b/tool/src/org/antlr/v4/analysis/LeftRecursionDetector.java new file mode 100644 index 0000000..44e4b4a --- /dev/null +++ b/tool/src/org/antlr/v4/analysis/LeftRecursionDetector.java @@ -0,0 +1,154 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.analysis; + +import org.antlr.v4.runtime.atn.ATN; +import org.antlr.v4.runtime.atn.ATNState; +import org.antlr.v4.runtime.atn.RuleStartState; +import org.antlr.v4.runtime.atn.RuleStopState; +import org.antlr.v4.runtime.atn.RuleTransition; +import org.antlr.v4.runtime.atn.Transition; +import org.antlr.v4.runtime.misc.OrderedHashSet; +import org.antlr.v4.tool.Grammar; +import org.antlr.v4.tool.Rule; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class LeftRecursionDetector { + Grammar g; + public ATN atn; + + /** Holds a list of cycles (sets of rule names). */ + public List<Set<Rule>> listOfRecursiveCycles = new ArrayList<Set<Rule>>(); + + /** Which rule start states have we visited while looking for a single + * left-recursion check? + */ + Set<RuleStartState> rulesVisitedPerRuleCheck = new HashSet<RuleStartState>(); + + public LeftRecursionDetector(Grammar g, ATN atn) { + this.g = g; + this.atn = atn; + } + + public void check() { + for (RuleStartState start : atn.ruleToStartState) { + //System.out.print("check "+start.rule.name); + rulesVisitedPerRuleCheck.clear(); + rulesVisitedPerRuleCheck.add(start); + //FASerializer ser = new FASerializer(atn.g, start); + //System.out.print(":\n"+ser+"\n"); + + check(g.getRule(start.ruleIndex), start, new HashSet<ATNState>()); + } + //System.out.println("cycles="+listOfRecursiveCycles); + if ( !listOfRecursiveCycles.isEmpty() ) { + g.tool.errMgr.leftRecursionCycles(g.fileName, listOfRecursiveCycles); + } + } + + /** From state s, look for any transition to a rule that is currently + * being traced. When tracing r, visitedPerRuleCheck has r + * initially. If you reach a rule stop state, return but notify the + * invoking rule that the called rule is nullable. This implies that + * invoking rule must look at follow transition for that invoking state. + * + * The visitedStates tracks visited states within a single rule so + * we can avoid epsilon-loop-induced infinite recursion here. Keep + * filling the cycles in listOfRecursiveCycles and also, as a + * side-effect, set leftRecursiveRules. + */ + public boolean check(Rule enclosingRule, ATNState s, Set<ATNState> visitedStates) { + if ( s instanceof RuleStopState) return true; + if ( visitedStates.contains(s) ) return false; + visitedStates.add(s); + + //System.out.println("visit "+s); + int n = s.getNumberOfTransitions(); + boolean stateReachesStopState = false; + for (int i=0; i<n; i++) { + Transition t = s.transition(i); + if ( t instanceof RuleTransition ) { + RuleTransition rt = (RuleTransition) t; + Rule r = g.getRule(rt.ruleIndex); + if ( rulesVisitedPerRuleCheck.contains((RuleStartState)t.target) ) { + addRulesToCycle(enclosingRule, r); + } + else { + // must visit if not already visited; mark target, pop when done + rulesVisitedPerRuleCheck.add((RuleStartState)t.target); + // send new visitedStates set per rule invocation + boolean nullable = check(r, t.target, new HashSet<ATNState>()); + // we're back from visiting that rule + rulesVisitedPerRuleCheck.remove((RuleStartState)t.target); + if ( nullable ) { + stateReachesStopState |= check(enclosingRule, rt.followState, visitedStates); + } + } + } + else if ( t.isEpsilon() ) { + stateReachesStopState |= check(enclosingRule, t.target, visitedStates); + } + // else ignore non-epsilon transitions + } + return stateReachesStopState; + } + + /** enclosingRule calls targetRule. Find the cycle containing + * the target and add the caller. Find the cycle containing the caller + * and add the target. If no cycles contain either, then create a new + * cycle. + */ + protected void addRulesToCycle(Rule enclosingRule, Rule targetRule) { + //System.err.println("left-recursion to "+targetRule.name+" from "+enclosingRule.name); + boolean foundCycle = false; + for (Set<Rule> rulesInCycle : listOfRecursiveCycles) { + // ensure both rules are in same cycle + if (rulesInCycle.contains(targetRule)) { + rulesInCycle.add(enclosingRule); + foundCycle = true; + } + if (rulesInCycle.contains(enclosingRule)) { + rulesInCycle.add(targetRule); + foundCycle = true; + } + } + if ( !foundCycle ) { + Set<Rule> cycle = new OrderedHashSet<Rule>(); + cycle.add(targetRule); + cycle.add(enclosingRule); + listOfRecursiveCycles.add(cycle); + } + } +} diff --git a/tool/src/org/antlr/v4/analysis/LeftRecursiveRuleAltInfo.java b/tool/src/org/antlr/v4/analysis/LeftRecursiveRuleAltInfo.java new file mode 100644 index 0000000..a5ef7b4 --- /dev/null +++ b/tool/src/org/antlr/v4/analysis/LeftRecursiveRuleAltInfo.java @@ -0,0 +1,62 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.analysis; + +import org.antlr.v4.tool.ast.AltAST; + +public class LeftRecursiveRuleAltInfo { + public int altNum; // original alt index (from 1) + public String leftRecursiveRuleRefLabel; + public String altLabel; + public final boolean isListLabel; + public String altText; + public AltAST altAST; // transformed ALT + public AltAST originalAltAST; + public int nextPrec; + + public LeftRecursiveRuleAltInfo(int altNum, String altText) { + this(altNum, altText, null, null, false, null); + } + + public LeftRecursiveRuleAltInfo(int altNum, String altText, + String leftRecursiveRuleRefLabel, + String altLabel, + boolean isListLabel, + AltAST originalAltAST) + { + this.altNum = altNum; + this.altText = altText; + this.leftRecursiveRuleRefLabel = leftRecursiveRuleRefLabel; + this.altLabel = altLabel; + this.isListLabel = isListLabel; + this.originalAltAST = originalAltAST; + } +} diff --git a/tool/src/org/antlr/v4/analysis/LeftRecursiveRuleAnalyzer.java b/tool/src/org/antlr/v4/analysis/LeftRecursiveRuleAnalyzer.java new file mode 100644 index 0000000..5b6ba05 --- /dev/null +++ b/tool/src/org/antlr/v4/analysis/LeftRecursiveRuleAnalyzer.java @@ -0,0 +1,446 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.analysis; + +import org.antlr.runtime.CommonToken; +import org.antlr.runtime.Token; +import org.antlr.runtime.TokenStream; +import org.antlr.runtime.tree.CommonTreeNodeStream; +import org.antlr.runtime.tree.Tree; +import org.antlr.v4.Tool; +import org.antlr.v4.codegen.CodeGenerator; +import org.antlr.v4.parse.ANTLRParser; +import org.antlr.v4.parse.GrammarASTAdaptor; +import org.antlr.v4.parse.LeftRecursiveRuleWalker; +import org.antlr.v4.runtime.misc.IntervalSet; +import org.antlr.v4.runtime.misc.Pair; +import org.antlr.v4.tool.ErrorType; +import org.antlr.v4.tool.ast.AltAST; +import org.antlr.v4.tool.ast.GrammarAST; +import org.antlr.v4.tool.ast.GrammarASTWithOptions; +import org.antlr.v4.tool.ast.RuleRefAST; +import org.stringtemplate.v4.ST; +import org.stringtemplate.v4.STGroup; +import org.stringtemplate.v4.STGroupFile; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** Using a tree walker on the rules, determine if a rule is directly left-recursive and if it follows + * our pattern. + */ +public class LeftRecursiveRuleAnalyzer extends LeftRecursiveRuleWalker { + public static enum ASSOC { left, right } + + public Tool tool; + public String ruleName; + public LinkedHashMap<Integer, LeftRecursiveRuleAltInfo> binaryAlts = new LinkedHashMap<Integer, LeftRecursiveRuleAltInfo>(); + public LinkedHashMap<Integer, LeftRecursiveRuleAltInfo> ternaryAlts = new LinkedHashMap<Integer, LeftRecursiveRuleAltInfo>(); + public LinkedHashMap<Integer, LeftRecursiveRuleAltInfo> suffixAlts = new LinkedHashMap<Integer, LeftRecursiveRuleAltInfo>(); + public List<LeftRecursiveRuleAltInfo> prefixAlts = new ArrayList<LeftRecursiveRuleAltInfo>(); + public List<LeftRecursiveRuleAltInfo> otherAlts = new ArrayList<LeftRecursiveRuleAltInfo>(); + + /** Pointer to ID node of ^(= ID element) */ + public List<Pair<GrammarAST,String>> leftRecursiveRuleRefLabels = + new ArrayList<Pair<GrammarAST,String>>(); + + /** Tokens from which rule AST comes from */ + public final TokenStream tokenStream; + + public GrammarAST retvals; + + public STGroup recRuleTemplates; + public STGroup codegenTemplates; + public String language; + + public Map<Integer, ASSOC> altAssociativity = new HashMap<Integer, ASSOC>(); + + public LeftRecursiveRuleAnalyzer(GrammarAST ruleAST, + Tool tool, String ruleName, String language) + { + super(new CommonTreeNodeStream(new GrammarASTAdaptor(ruleAST.token.getInputStream()), ruleAST)); + this.tool = tool; + this.ruleName = ruleName; + this.language = language; + this.tokenStream = ruleAST.g.tokenStream; + if (this.tokenStream == null) { + throw new NullPointerException("grammar must have a token stream"); + } + + loadPrecRuleTemplates(); + } + + public void loadPrecRuleTemplates() { + String templateGroupFile = "org/antlr/v4/tool/templates/LeftRecursiveRules.stg"; + recRuleTemplates = new STGroupFile(templateGroupFile); + if ( !recRuleTemplates.isDefined("recRule") ) { + tool.errMgr.toolError(ErrorType.MISSING_CODE_GEN_TEMPLATES, "LeftRecursiveRules"); + } + + // use codegen to get correct language templates; that's it though + CodeGenerator gen = new CodeGenerator(tool, null, language); + codegenTemplates = gen.getTemplates(); + } + + @Override + public void setReturnValues(GrammarAST t) { + retvals = t; + } + + @Override + public void setAltAssoc(AltAST t, int alt) { + ASSOC assoc = ASSOC.left; + if ( t.getOptions()!=null ) { + String a = t.getOptionString("assoc"); + if ( a!=null ) { + if ( a.equals(ASSOC.right.toString()) ) { + assoc = ASSOC.right; + } + else if ( a.equals(ASSOC.left.toString()) ) { + assoc = ASSOC.left; + } + else { + tool.errMgr.grammarError(ErrorType.ILLEGAL_OPTION_VALUE, t.g.fileName, t.getOptionAST("assoc").getToken(), "assoc", assoc); + } + } + } + + if ( altAssociativity.get(alt)!=null && altAssociativity.get(alt)!=assoc ) { + tool.errMgr.toolError(ErrorType.INTERNAL_ERROR, "all operators of alt " + alt + " of left-recursive rule must have same associativity"); + } + altAssociativity.put(alt, assoc); + +// System.out.println("setAltAssoc: op " + alt + ": " + t.getText()+", assoc="+assoc); + } + + @Override + public void binaryAlt(AltAST originalAltTree, int alt) { + AltAST altTree = (AltAST)originalAltTree.dupTree(); + String altLabel = altTree.altLabel!=null ? altTree.altLabel.getText() : null; + + String label = null; + boolean isListLabel = false; + GrammarAST lrlabel = stripLeftRecursion(altTree); + if ( lrlabel!=null ) { + label = lrlabel.getText(); + isListLabel = lrlabel.getParent().getType() == PLUS_ASSIGN; + leftRecursiveRuleRefLabels.add(new Pair<GrammarAST,String>(lrlabel,altLabel)); + } + + stripAltLabel(altTree); + + // rewrite e to be e_[rec_arg] + int nextPrec = nextPrecedence(alt); + altTree = addPrecedenceArgToRules(altTree, nextPrec); + + stripAltLabel(altTree); + String altText = text(altTree); + altText = altText.trim(); + LeftRecursiveRuleAltInfo a = + new LeftRecursiveRuleAltInfo(alt, altText, label, altLabel, isListLabel, originalAltTree); + a.nextPrec = nextPrec; + binaryAlts.put(alt, a); + //System.out.println("binaryAlt " + alt + ": " + altText + ", rewrite=" + rewriteText); + } + + @Override + public void prefixAlt(AltAST originalAltTree, int alt) { + AltAST altTree = (AltAST)originalAltTree.dupTree(); + stripAltLabel(altTree); + + int nextPrec = precedence(alt); + // rewrite e to be e_[prec] + altTree = addPrecedenceArgToRules(altTree, nextPrec); + String altText = text(altTree); + altText = altText.trim(); + String altLabel = altTree.altLabel!=null ? altTree.altLabel.getText() : null; + LeftRecursiveRuleAltInfo a = + new LeftRecursiveRuleAltInfo(alt, altText, null, altLabel, false, originalAltTree); + a.nextPrec = nextPrec; + prefixAlts.add(a); + //System.out.println("prefixAlt " + alt + ": " + altText + ", rewrite=" + rewriteText); + } + + @Override + public void suffixAlt(AltAST originalAltTree, int alt) { + AltAST altTree = (AltAST)originalAltTree.dupTree(); + String altLabel = altTree.altLabel!=null ? altTree.altLabel.getText() : null; + + String label = null; + boolean isListLabel = false; + GrammarAST lrlabel = stripLeftRecursion(altTree); + if ( lrlabel!=null ) { + label = lrlabel.getText(); + isListLabel = lrlabel.getParent().getType() == PLUS_ASSIGN; + leftRecursiveRuleRefLabels.add(new Pair<GrammarAST,String>(lrlabel,altLabel)); + } + stripAltLabel(altTree); + String altText = text(altTree); + altText = altText.trim(); + LeftRecursiveRuleAltInfo a = + new LeftRecursiveRuleAltInfo(alt, altText, label, altLabel, isListLabel, originalAltTree); + suffixAlts.put(alt, a); +// System.out.println("suffixAlt " + alt + ": " + altText + ", rewrite=" + rewriteText); + } + + @Override + public void otherAlt(AltAST originalAltTree, int alt) { + AltAST altTree = (AltAST)originalAltTree.dupTree(); + stripAltLabel(altTree); + String altText = text(altTree); + String altLabel = altTree.altLabel!=null ? altTree.altLabel.getText() : null; + LeftRecursiveRuleAltInfo a = + new LeftRecursiveRuleAltInfo(alt, altText, null, altLabel, false, originalAltTree); + otherAlts.add(a); +// System.out.println("otherAlt " + alt + ": " + altText); + } + + // --------- get transformed rules ---------------- + + public String getArtificialOpPrecRule() { + ST ruleST = recRuleTemplates.getInstanceOf("recRule"); + ruleST.add("ruleName", ruleName); + ST ruleArgST = codegenTemplates.getInstanceOf("recRuleArg"); + ruleST.add("argName", ruleArgST); + ST setResultST = codegenTemplates.getInstanceOf("recRuleSetResultAction"); + ruleST.add("setResultAction", setResultST); + ruleST.add("userRetvals", retvals); + + LinkedHashMap<Integer, LeftRecursiveRuleAltInfo> opPrecRuleAlts = new LinkedHashMap<Integer, LeftRecursiveRuleAltInfo>(); + opPrecRuleAlts.putAll(binaryAlts); + opPrecRuleAlts.putAll(ternaryAlts); + opPrecRuleAlts.putAll(suffixAlts); + for (int alt : opPrecRuleAlts.keySet()) { + LeftRecursiveRuleAltInfo altInfo = opPrecRuleAlts.get(alt); + ST altST = recRuleTemplates.getInstanceOf("recRuleAlt"); + ST predST = codegenTemplates.getInstanceOf("recRuleAltPredicate"); + predST.add("opPrec", precedence(alt)); + predST.add("ruleName", ruleName); + altST.add("pred", predST); + altST.add("alt", altInfo); + altST.add("precOption", LeftRecursiveRuleTransformer.PRECEDENCE_OPTION_NAME); + altST.add("opPrec", precedence(alt)); + ruleST.add("opAlts", altST); + } + + ruleST.add("primaryAlts", prefixAlts); + ruleST.add("primaryAlts", otherAlts); + + tool.log("left-recursion", ruleST.render()); + + return ruleST.render(); + } + + public AltAST addPrecedenceArgToRules(AltAST t, int prec) { + if ( t==null ) return null; + // get all top-level rule refs from ALT + List<GrammarAST> outerAltRuleRefs = t.getNodesWithTypePreorderDFS(IntervalSet.of(RULE_REF)); + for (GrammarAST x : outerAltRuleRefs) { + RuleRefAST rref = (RuleRefAST)x; + boolean recursive = rref.getText().equals(ruleName); + boolean rightmost = rref == outerAltRuleRefs.get(outerAltRuleRefs.size()-1); + if ( recursive && rightmost ) { + GrammarAST dummyValueNode = new GrammarAST(new CommonToken(ANTLRParser.INT, ""+prec)); + rref.setOption(LeftRecursiveRuleTransformer.PRECEDENCE_OPTION_NAME, dummyValueNode); + } + } + return t; + } + + /** + * Match (RULE RULE_REF (BLOCK (ALT .*) (ALT RULE_REF[self] .*) (ALT .*))) + * Match (RULE RULE_REF (BLOCK (ALT .*) (ALT (ASSIGN ID RULE_REF[self]) .*) (ALT .*))) + */ + public static boolean hasImmediateRecursiveRuleRefs(GrammarAST t, String ruleName) { + if ( t==null ) return false; + GrammarAST blk = (GrammarAST)t.getFirstChildWithType(BLOCK); + if ( blk==null ) return false; + int n = blk.getChildren().size(); + for (int i = 0; i < n; i++) { + GrammarAST alt = (GrammarAST)blk.getChildren().get(i); + Tree first = alt.getChild(0); + if ( first==null ) continue; + if (first.getType() == ELEMENT_OPTIONS) { + first = alt.getChild(1); + if (first == null) { + continue; + } + } + if ( first.getType()==RULE_REF && first.getText().equals(ruleName) ) return true; + Tree rref = first.getChild(1); + if ( rref!=null && rref.getType()==RULE_REF && rref.getText().equals(ruleName) ) return true; + } + return false; + } + + // TODO: this strips the tree properly, but since text() + // uses the start of stop token index and gets text from that + // ineffectively ignores this routine. + public GrammarAST stripLeftRecursion(GrammarAST altAST) { + GrammarAST lrlabel=null; + GrammarAST first = (GrammarAST)altAST.getChild(0); + int leftRecurRuleIndex = 0; + if ( first.getType() == ELEMENT_OPTIONS ) { + first = (GrammarAST)altAST.getChild(1); + leftRecurRuleIndex = 1; + } + Tree rref = first.getChild(1); // if label=rule + if ( (first.getType()==RULE_REF && first.getText().equals(ruleName)) || + (rref!=null && rref.getType()==RULE_REF && rref.getText().equals(ruleName)) ) + { + if ( first.getType()==ASSIGN || first.getType()==PLUS_ASSIGN ) lrlabel = (GrammarAST)first.getChild(0); + // remove rule ref (first child unless options present) + altAST.deleteChild(leftRecurRuleIndex); + // reset index so it prints properly (sets token range of + // ALT to start to right of left recur rule we deleted) + GrammarAST newFirstChild = (GrammarAST)altAST.getChild(leftRecurRuleIndex); + altAST.setTokenStartIndex(newFirstChild.getTokenStartIndex()); + } + return lrlabel; + } + + /** Strip last 2 tokens if -> label; alter indexes in altAST */ + public void stripAltLabel(GrammarAST altAST) { + int start = altAST.getTokenStartIndex(); + int stop = altAST.getTokenStopIndex(); + // find => + for (int i=stop; i>=start; i--) { + if ( tokenStream.get(i).getType()==POUND ) { + altAST.setTokenStopIndex(i-1); + return; + } + } + } + + public String text(GrammarAST t) { + if ( t==null ) return ""; + + int tokenStartIndex = t.getTokenStartIndex(); + int tokenStopIndex = t.getTokenStopIndex(); + + // ignore tokens from existing option subtrees like: + // (ELEMENT_OPTIONS (= assoc right)) + // + // element options are added back according to the values in the map + // returned by getOptions(). + IntervalSet ignore = new IntervalSet(); + List<GrammarAST> optionsSubTrees = t.getNodesWithType(ELEMENT_OPTIONS); + for (GrammarAST sub : optionsSubTrees) { + ignore.add(sub.getTokenStartIndex(), sub.getTokenStopIndex()); + } + + // Individual labels appear as RULE_REF or TOKEN_REF tokens in the tree, + // but do not support the ELEMENT_OPTIONS syntax. Make sure to not try + // and add the tokenIndex option when writing these tokens. + IntervalSet noOptions = new IntervalSet(); + List<GrammarAST> labeledSubTrees = t.getNodesWithType(new IntervalSet(ASSIGN,PLUS_ASSIGN)); + for (GrammarAST sub : labeledSubTrees) { + noOptions.add(sub.getChild(0).getTokenStartIndex()); + } + + StringBuilder buf = new StringBuilder(); + int i=tokenStartIndex; + while ( i<=tokenStopIndex ) { + if ( ignore.contains(i) ) { + i++; + continue; + } + + Token tok = tokenStream.get(i); + + // Compute/hold any element options + StringBuilder elementOptions = new StringBuilder(); + if (!noOptions.contains(i)) { + GrammarAST node = t.getNodeWithTokenIndex(tok.getTokenIndex()); + if ( node!=null && + (tok.getType()==TOKEN_REF || + tok.getType()==STRING_LITERAL || + tok.getType()==RULE_REF) ) + { + elementOptions.append("tokenIndex=").append(tok.getTokenIndex()); + } + + if ( node instanceof GrammarASTWithOptions ) { + GrammarASTWithOptions o = (GrammarASTWithOptions)node; + for (Map.Entry<String, GrammarAST> entry : o.getOptions().entrySet()) { + if (elementOptions.length() > 0) { + elementOptions.append(','); + } + + elementOptions.append(entry.getKey()); + elementOptions.append('='); + elementOptions.append(entry.getValue().getText()); + } + } + } + + buf.append(tok.getText()); // add actual text of the current token to the rewritten alternative + i++; // move to the next token + + // Are there args on a rule? + if ( tok.getType()==RULE_REF && i<=tokenStopIndex && tokenStream.get(i).getType()==ARG_ACTION ) { + buf.append('['+tokenStream.get(i).getText()+']'); + i++; + } + + // now that we have the actual element, we can add the options. + if (elementOptions.length() > 0) { + buf.append('<').append(elementOptions).append('>'); + } + } + return buf.toString(); + } + + public int precedence(int alt) { + return numAlts-alt+1; + } + + // Assumes left assoc + public int nextPrecedence(int alt) { + int p = precedence(alt); + if ( altAssociativity.get(alt)==ASSOC.right ) return p; + return p+1; + } + + @Override + public String toString() { + return "PrecRuleOperatorCollector{" + + "binaryAlts=" + binaryAlts + + ", ternaryAlts=" + ternaryAlts + + ", suffixAlts=" + suffixAlts + + ", prefixAlts=" + prefixAlts + + ", otherAlts=" + otherAlts + + '}'; + } +} diff --git a/tool/src/org/antlr/v4/analysis/LeftRecursiveRuleTransformer.java b/tool/src/org/antlr/v4/analysis/LeftRecursiveRuleTransformer.java new file mode 100644 index 0000000..ea67493 --- /dev/null +++ b/tool/src/org/antlr/v4/analysis/LeftRecursiveRuleTransformer.java @@ -0,0 +1,276 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.analysis; + +import org.antlr.runtime.ANTLRStringStream; +import org.antlr.runtime.CommonTokenStream; +import org.antlr.runtime.ParserRuleReturnScope; +import org.antlr.runtime.RecognitionException; +import org.antlr.runtime.Token; +import org.antlr.v4.Tool; +import org.antlr.v4.misc.OrderedHashMap; +import org.antlr.v4.parse.ANTLRLexer; +import org.antlr.v4.parse.ANTLRParser; +import org.antlr.v4.parse.GrammarASTAdaptor; +import org.antlr.v4.parse.ScopeParser; +import org.antlr.v4.parse.ToolANTLRParser; +import org.antlr.v4.runtime.misc.Pair; +import org.antlr.v4.semantics.BasicSemanticChecks; +import org.antlr.v4.semantics.RuleCollector; +import org.antlr.v4.tool.AttributeDict; +import org.antlr.v4.tool.ErrorType; +import org.antlr.v4.tool.Grammar; +import org.antlr.v4.tool.GrammarTransformPipeline; +import org.antlr.v4.tool.LabelElementPair; +import org.antlr.v4.tool.LeftRecursiveRule; +import org.antlr.v4.tool.Rule; +import org.antlr.v4.tool.ast.ActionAST; +import org.antlr.v4.tool.ast.AltAST; +import org.antlr.v4.tool.ast.BlockAST; +import org.antlr.v4.tool.ast.GrammarAST; +import org.antlr.v4.tool.ast.GrammarASTWithOptions; +import org.antlr.v4.tool.ast.GrammarRootAST; +import org.antlr.v4.tool.ast.RuleAST; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** Remove left-recursive rule refs, add precedence args to recursive rule refs. + * Rewrite rule so we can create ATN. + * + * MODIFIES grammar AST in place. + */ +public class LeftRecursiveRuleTransformer { + public static final String PRECEDENCE_OPTION_NAME = "p"; + public static final String TOKENINDEX_OPTION_NAME = "tokenIndex"; + + public GrammarRootAST ast; + public Collection<Rule> rules; + public Grammar g; + public Tool tool; + + public LeftRecursiveRuleTransformer(GrammarRootAST ast, Collection<Rule> rules, Grammar g) { + this.ast = ast; + this.rules = rules; + this.g = g; + this.tool = g.tool; + } + + public void translateLeftRecursiveRules() { + String language = g.getOptionString("language"); + // translate all recursive rules + List<String> leftRecursiveRuleNames = new ArrayList<String>(); + for (Rule r : rules) { + if ( !Grammar.isTokenName(r.name) ) { + if ( LeftRecursiveRuleAnalyzer.hasImmediateRecursiveRuleRefs(r.ast, r.name) ) { + boolean fitsPattern = translateLeftRecursiveRule(ast, (LeftRecursiveRule)r, language); + if ( fitsPattern ) { + leftRecursiveRuleNames.add(r.name); + } + else { // better given an error that non-conforming left-recursion exists + tool.errMgr.grammarError(ErrorType.NONCONFORMING_LR_RULE, g.fileName, ((GrammarAST)r.ast.getChild(0)).token, r.name); + } + } + } + } + + // update all refs to recursive rules to have [0] argument + for (GrammarAST r : ast.getNodesWithType(ANTLRParser.RULE_REF)) { + if ( r.getParent().getType()==ANTLRParser.RULE ) continue; // must be rule def + if ( ((GrammarASTWithOptions)r).getOptionString(PRECEDENCE_OPTION_NAME) != null ) continue; // already has arg; must be in rewritten rule + if ( leftRecursiveRuleNames.contains(r.getText()) ) { + // found ref to recursive rule not already rewritten with arg + ((GrammarASTWithOptions)r).setOption(PRECEDENCE_OPTION_NAME, (GrammarAST)new GrammarASTAdaptor().create(ANTLRParser.INT, "0")); + } + } + } + + /** Return true if successful */ + public boolean translateLeftRecursiveRule(GrammarRootAST ast, + LeftRecursiveRule r, + String language) + { + //tool.log("grammar", ruleAST.toStringTree()); + GrammarAST prevRuleAST = r.ast; + String ruleName = prevRuleAST.getChild(0).getText(); + LeftRecursiveRuleAnalyzer leftRecursiveRuleWalker = + new LeftRecursiveRuleAnalyzer(prevRuleAST, tool, ruleName, language); + boolean isLeftRec; + try { +// System.out.println("TESTING ---------------\n"+ +// leftRecursiveRuleWalker.text(ruleAST)); + isLeftRec = leftRecursiveRuleWalker.rec_rule(); + } + catch (RecognitionException re) { + isLeftRec = false; // didn't match; oh well + } + if ( !isLeftRec ) return false; + + // replace old rule's AST; first create text of altered rule + GrammarAST RULES = (GrammarAST)ast.getFirstChildWithType(ANTLRParser.RULES); + String newRuleText = leftRecursiveRuleWalker.getArtificialOpPrecRule(); +// System.out.println("created: "+newRuleText); + // now parse within the context of the grammar that originally created + // the AST we are transforming. This could be an imported grammar so + // we cannot just reference this.g because the role might come from + // the imported grammar and not the root grammar (this.g) + RuleAST t = parseArtificialRule(prevRuleAST.g, newRuleText); + + // reuse the name token from the original AST since it refers to the proper source location in the original grammar + ((GrammarAST)t.getChild(0)).token = ((GrammarAST)prevRuleAST.getChild(0)).getToken(); + + // update grammar AST and set rule's AST. + RULES.setChild(prevRuleAST.getChildIndex(), t); + r.ast = t; + + // Reduce sets in newly created rule tree + GrammarTransformPipeline transform = new GrammarTransformPipeline(g, g.tool); + transform.reduceBlocksToSets(r.ast); + transform.expandParameterizedLoops(r.ast); + + // Rerun semantic checks on the new rule + RuleCollector ruleCollector = new RuleCollector(g); + ruleCollector.visit(t, "rule"); + BasicSemanticChecks basics = new BasicSemanticChecks(g, ruleCollector); + // disable the assoc element option checks because they are already + // handled for the pre-transformed rule. + basics.checkAssocElementOption = false; + basics.visit(t, "rule"); + + // track recursive alt info for codegen + r.recPrimaryAlts = new ArrayList<LeftRecursiveRuleAltInfo>(); + r.recPrimaryAlts.addAll(leftRecursiveRuleWalker.prefixAlts); + r.recPrimaryAlts.addAll(leftRecursiveRuleWalker.otherAlts); + if (r.recPrimaryAlts.isEmpty()) { + tool.errMgr.grammarError(ErrorType.NO_NON_LR_ALTS, g.fileName, ((GrammarAST)r.ast.getChild(0)).getToken(), r.name); + } + + r.recOpAlts = new OrderedHashMap<Integer, LeftRecursiveRuleAltInfo>(); + r.recOpAlts.putAll(leftRecursiveRuleWalker.binaryAlts); + r.recOpAlts.putAll(leftRecursiveRuleWalker.ternaryAlts); + r.recOpAlts.putAll(leftRecursiveRuleWalker.suffixAlts); + + // walk alt info records and set their altAST to point to appropriate ALT subtree + // from freshly created AST + setAltASTPointers(r, t); + + // update Rule to just one alt and add prec alt + ActionAST arg = (ActionAST)r.ast.getFirstChildWithType(ANTLRParser.ARG_ACTION); + if ( arg!=null ) { + r.args = ScopeParser.parseTypedArgList(arg, arg.getText(), g); + r.args.type = AttributeDict.DictType.ARG; + r.args.ast = arg; + arg.resolver = r.alt[1]; // todo: isn't this Rule or something? + } + + // define labels on recursive rule refs we delete; they don't point to nodes of course + // these are so $label in action translation works + for (Pair<GrammarAST,String> pair : leftRecursiveRuleWalker.leftRecursiveRuleRefLabels) { + GrammarAST labelNode = pair.a; + GrammarAST labelOpNode = (GrammarAST)labelNode.getParent(); + GrammarAST elementNode = (GrammarAST)labelOpNode.getChild(1); + LabelElementPair lp = new LabelElementPair(g, labelNode, elementNode, labelOpNode.getType()); + r.alt[1].labelDefs.map(labelNode.getText(), lp); + } + // copy to rule from walker + r.leftRecursiveRuleRefLabels = leftRecursiveRuleWalker.leftRecursiveRuleRefLabels; + + tool.log("grammar", "added: "+t.toStringTree()); + return true; + } + + public RuleAST parseArtificialRule(final Grammar g, String ruleText) { + ANTLRLexer lexer = new ANTLRLexer(new ANTLRStringStream(ruleText)); + GrammarASTAdaptor adaptor = new GrammarASTAdaptor(lexer.getCharStream()); + CommonTokenStream tokens = new CommonTokenStream(lexer); + lexer.tokens = tokens; + ToolANTLRParser p = new ToolANTLRParser(tokens, tool); + p.setTreeAdaptor(adaptor); + Token ruleStart = null; + try { + ParserRuleReturnScope r = p.rule(); + RuleAST tree = (RuleAST)r.getTree(); + ruleStart = (Token)r.getStart(); + GrammarTransformPipeline.setGrammarPtr(g, tree); + GrammarTransformPipeline.augmentTokensWithOriginalPosition(g, tree); + return tree; + } + catch (Exception e) { + tool.errMgr.toolError(ErrorType.INTERNAL_ERROR, + e, + ruleStart, + "error parsing rule created during left-recursion detection: "+ruleText); + } + return null; + } + + /** + * <pre> + * (RULE e int _p (returns int v) + * (BLOCK + * (ALT + * (BLOCK + * (ALT INT {$v = $INT.int;}) + * (ALT '(' (= x e) ')' {$v = $x.v;}) + * (ALT ID)) + * (* (BLOCK + * (OPTIONS ...) + * (ALT {7 >= $_p}? '*' (= b e) {$v = $a.v * $b.v;}) + * (ALT {6 >= $_p}? '+' (= b e) {$v = $a.v + $b.v;}) + * (ALT {3 >= $_p}? '++') (ALT {2 >= $_p}? '--')))))) + * </pre> + */ + public void setAltASTPointers(LeftRecursiveRule r, RuleAST t) { +// System.out.println("RULE: "+t.toStringTree()); + BlockAST ruleBlk = (BlockAST)t.getFirstChildWithType(ANTLRParser.BLOCK); + AltAST mainAlt = (AltAST)ruleBlk.getChild(0); + BlockAST primaryBlk = (BlockAST)mainAlt.getChild(0); + BlockAST opsBlk = (BlockAST)mainAlt.getChild(1).getChild(0); // (* BLOCK ...) + for (int i = 0; i < r.recPrimaryAlts.size(); i++) { + LeftRecursiveRuleAltInfo altInfo = r.recPrimaryAlts.get(i); + altInfo.altAST = (AltAST)primaryBlk.getChild(i); + altInfo.altAST.leftRecursiveAltInfo = altInfo; + altInfo.originalAltAST.leftRecursiveAltInfo = altInfo; +// altInfo.originalAltAST.parent = altInfo.altAST.parent; +// System.out.println(altInfo.altAST.toStringTree()); + } + for (int i = 0; i < r.recOpAlts.size(); i++) { + LeftRecursiveRuleAltInfo altInfo = r.recOpAlts.getElement(i); + altInfo.altAST = (AltAST)opsBlk.getChild(i); + altInfo.altAST.leftRecursiveAltInfo = altInfo; + altInfo.originalAltAST.leftRecursiveAltInfo = altInfo; +// altInfo.originalAltAST.parent = altInfo.altAST.parent; +// System.out.println(altInfo.altAST.toStringTree()); + } + } + +} diff --git a/tool/src/org/antlr/v4/automata/ATNFactory.java b/tool/src/org/antlr/v4/automata/ATNFactory.java new file mode 100644 index 0000000..aff2862 --- /dev/null +++ b/tool/src/org/antlr/v4/automata/ATNFactory.java @@ -0,0 +1,244 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.automata; + +import org.antlr.v4.runtime.atn.ATN; +import org.antlr.v4.runtime.atn.ATNState; +import org.antlr.v4.tool.ast.ActionAST; +import org.antlr.v4.tool.ast.BlockAST; +import org.antlr.v4.tool.ast.GrammarAST; +import org.antlr.v4.tool.ast.PredAST; +import org.antlr.v4.tool.ast.TerminalAST; + +import java.util.List; + +public interface ATNFactory { + /** A pair of states pointing to the left/right (start and end) states of a + * state submachine. Used to build ATNs. + */ + public static class Handle { + public ATNState left; + public ATNState right; + + public Handle(ATNState left, ATNState right) { + this.left = left; + this.right = right; + } + + @Override + public String toString() { + return "("+left+","+right+")"; + } + } + + + ATN createATN(); + + void setCurrentRuleName(String name); + + void setCurrentOuterAlt(int alt); + + + Handle rule(GrammarAST ruleAST, String name, Handle blk); + + + ATNState newState(); + + + Handle label(Handle t); + + + Handle listLabel(Handle t); + + + Handle tokenRef(TerminalAST node); + + + Handle set(GrammarAST associatedAST, List<GrammarAST> alts, boolean invert); + + + Handle charSetLiteral(GrammarAST charSetAST); + + + Handle range(GrammarAST a, GrammarAST b); + + /** For a non-lexer, just build a simple token reference atom. + * For a lexer, a string is a sequence of char to match. That is, + * "fog" is treated as 'f' 'o' 'g' not as a single transition in + * the DFA. Machine== o-'f'->o-'o'->o-'g'->o and has n+1 states + * for n characters. + */ + + Handle stringLiteral(TerminalAST stringLiteralAST); + + /** For reference to rule r, build + * + * o-e->(r) o + * + * where (r) is the start of rule r and the trailing o is not linked + * to from rule ref state directly (it's done thru the transition(0) + * RuleClosureTransition. + * + * If the rule r is just a list of tokens, it's block will be just + * a set on an edge o->o->o-set->o->o->o, could inline it rather than doing + * the rule reference, but i'm not doing this yet as I'm not sure + * it would help much in the ATN->DFA construction. + * + * TODO add to codegen: collapse alt blks that are sets into single matchSet + * @param node + */ + + Handle ruleRef(GrammarAST node); + + /** From an empty alternative build Grip o-e->o */ + + Handle epsilon(GrammarAST node); + + /** Build what amounts to an epsilon transition with a semantic + * predicate action. The pred is a pointer into the AST of + * the SEMPRED token. + */ + + Handle sempred(PredAST pred); + + /** Build what amounts to an epsilon transition with an action. + * The action goes into ATN though it is ignored during analysis. + */ + + Handle action(ActionAST action); + + + Handle action(String action); + + + Handle alt(List<Handle> els); + + /** From A|B|..|Z alternative block build + * + * o->o-A->o->o (last ATNState is blockEndATNState pointed to by all alts) + * | ^ + * o->o-B->o--| + * | | + * ... | + * | | + * o->o-Z->o--| + * + * So every alternative gets begin ATNState connected by epsilon + * and every alt right side points at a block end ATNState. There is a + * new ATNState in the ATNState in the Grip for each alt plus one for the + * end ATNState. + * + * Special case: only one alternative: don't make a block with alt + * begin/end. + * + * Special case: if just a list of tokens/chars/sets, then collapse + * to a single edge'd o-set->o graph. + * + * Set alt number (1..n) in the left-Transition ATNState. + */ + + Handle block(BlockAST blockAST, GrammarAST ebnfRoot, List<Handle> alternativeGrips); + +// Handle notBlock(GrammarAST blockAST, Handle set); + + /** From (A)? build either: + * + * o--A->o + * | ^ + * o---->| + * + * or, if A is a block, just add an empty alt to the end of the block + */ + + Handle optional(GrammarAST optAST, Handle blk); + + /** From (A)+ build + * + * |---| (Transition 2 from A.right points at alt 1) + * v | (follow of loop is Transition 1) + * o->o-A-o->o + * + * Meaning that the last ATNState in A points back to A's left Transition ATNState + * and we add a new begin/end ATNState. A can be single alternative or + * multiple. + * + * During analysis we'll call the follow link (transition 1) alt n+1 for + * an n-alt A block. + */ + + Handle plus(GrammarAST plusAST, Handle blk); + + /** From (A)* build + * + * |---| + * v | + * o->o-A-o--o (Transition 2 from block end points at alt 1; follow is Transition 1) + * | ^ + * o---------| (optional branch is 2nd alt of optional block containing A+) + * + * Meaning that the last (end) ATNState in A points back to A's + * left side ATNState and we add 3 new ATNStates (the + * optional branch is built just like an optional subrule). + * See the Aplus() method for more on the loop back Transition. + * The new node on right edge is set to RIGHT_EDGE_OF_CLOSURE so we + * can detect nested (A*)* loops and insert an extra node. Previously, + * two blocks shared same EOB node. + * + * There are 2 or 3 decision points in a A*. If A is not a block (i.e., + * it only has one alt), then there are two decisions: the optional bypass + * and then loopback. If A is a block of alts, then there are three + * decisions: bypass, loopback, and A's decision point. + * + * Note that the optional bypass must be outside the loop as (A|B)* is + * not the same thing as (A|B|)+. + * + * This is an accurate ATN representation of the meaning of (A)*, but + * for generating code, I don't need a DFA for the optional branch by + * virtue of how I generate code. The exit-loopback-branch decision + * is sufficient to let me make an appropriate enter, exit, loop + * determination. See codegen.g + */ + + Handle star(GrammarAST starAST, Handle blk); + + /** Build an atom with all possible values in its label */ + + Handle wildcard(GrammarAST associatedAST); + + + Handle lexerAltCommands(Handle alt, Handle cmds); + + + Handle lexerCallCommand(GrammarAST ID, GrammarAST arg); + + + Handle lexerCommand(GrammarAST ID); +} diff --git a/tool/src/org/antlr/v4/automata/ATNOptimizer.java b/tool/src/org/antlr/v4/automata/ATNOptimizer.java new file mode 100644 index 0000000..4f72b64 --- /dev/null +++ b/tool/src/org/antlr/v4/automata/ATNOptimizer.java @@ -0,0 +1,170 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.automata; + +import org.antlr.v4.runtime.atn.ATN; +import org.antlr.v4.runtime.atn.ATNState; +import org.antlr.v4.runtime.atn.AtomTransition; +import org.antlr.v4.runtime.atn.BlockEndState; +import org.antlr.v4.runtime.atn.DecisionState; +import org.antlr.v4.runtime.atn.EpsilonTransition; +import org.antlr.v4.runtime.atn.NotSetTransition; +import org.antlr.v4.runtime.atn.RangeTransition; +import org.antlr.v4.runtime.atn.SetTransition; +import org.antlr.v4.runtime.atn.Transition; +import org.antlr.v4.runtime.misc.Interval; +import org.antlr.v4.runtime.misc.IntervalSet; +import org.antlr.v4.tool.Grammar; +import org.antlr.v4.tool.Rule; + +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author Sam Harwell + */ +public class ATNOptimizer { + + public static void optimize(Grammar g, ATN atn) { + optimizeSets(g, atn); + optimizeStates(atn); + } + + private static void optimizeSets(Grammar g, ATN atn) { + if (g.isParser()) { + // parser codegen doesn't currently support SetTransition + return; + } + + int removedStates = 0; + List<DecisionState> decisions = atn.decisionToState; + for (DecisionState decision : decisions) { + if (decision.ruleIndex >= 0) { + Rule rule = g.getRule(decision.ruleIndex); + if (Character.isLowerCase(rule.name.charAt(0))) { + // parser codegen doesn't currently support SetTransition + continue; + } + } + + IntervalSet setTransitions = new IntervalSet(); + for (int i = 0; i < decision.getNumberOfTransitions(); i++) { + Transition epsTransition = decision.transition(i); + if (!(epsTransition instanceof EpsilonTransition)) { + continue; + } + + if (epsTransition.target.getNumberOfTransitions() != 1) { + continue; + } + + Transition transition = epsTransition.target.transition(0); + if (!(transition.target instanceof BlockEndState)) { + continue; + } + + if (transition instanceof NotSetTransition) { + // TODO: not yet implemented + continue; + } + + if (transition instanceof AtomTransition + || transition instanceof RangeTransition + || transition instanceof SetTransition) + { + setTransitions.add(i); + } + } + + // due to min alt resolution policies, can only collapse sequential alts + for (int i = setTransitions.getIntervals().size() - 1; i >= 0; i--) { + Interval interval = setTransitions.getIntervals().get(i); + if (interval.length() <= 1) { + continue; + } + + ATNState blockEndState = decision.transition(interval.a).target.transition(0).target; + IntervalSet matchSet = new IntervalSet(); + for (int j = interval.a; j <= interval.b; j++) { + Transition matchTransition = decision.transition(j).target.transition(0); + if (matchTransition instanceof NotSetTransition) { + throw new UnsupportedOperationException("Not yet implemented."); + } else { + matchSet.addAll(matchTransition.label()); + } + } + + Transition newTransition; + if (matchSet.getIntervals().size() == 1) { + if (matchSet.size() == 1) { + newTransition = new AtomTransition(blockEndState, matchSet.getMinElement()); + } else { + Interval matchInterval = matchSet.getIntervals().get(0); + newTransition = new RangeTransition(blockEndState, matchInterval.a, matchInterval.b); + } + } else { + newTransition = new SetTransition(blockEndState, matchSet); + } + + decision.transition(interval.a).target.setTransition(0, newTransition); + for (int j = interval.a + 1; j <= interval.b; j++) { + Transition removed = decision.removeTransition(interval.a + 1); + atn.removeState(removed.target); + removedStates++; + } + } + } + +// System.out.println("ATN optimizer removed " + removedStates + " states by collapsing sets."); + } + + private static void optimizeStates(ATN atn) { +// System.out.println(atn.states); + List<ATNState> compressed = new ArrayList<ATNState>(); + int i = 0; // new state number + for (ATNState s : atn.states) { + if ( s!=null ) { + compressed.add(s); + s.stateNumber = i; // reset state number as we shift to new position + i++; + } + } +// System.out.println(compressed); +// System.out.println("ATN optimizer removed " + (atn.states.size() - compressed.size()) + " null states."); + atn.states.clear(); + atn.states.addAll(compressed); + } + + private ATNOptimizer() { + } + +} diff --git a/tool/src/org/antlr/v4/automata/ATNPrinter.java b/tool/src/org/antlr/v4/automata/ATNPrinter.java new file mode 100644 index 0000000..9fe1145 --- /dev/null +++ b/tool/src/org/antlr/v4/automata/ATNPrinter.java @@ -0,0 +1,139 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.automata; + +import org.antlr.v4.runtime.atn.ATNState; +import org.antlr.v4.runtime.atn.ActionTransition; +import org.antlr.v4.runtime.atn.AtomTransition; +import org.antlr.v4.runtime.atn.BlockEndState; +import org.antlr.v4.runtime.atn.BlockStartState; +import org.antlr.v4.runtime.atn.EpsilonTransition; +import org.antlr.v4.runtime.atn.NotSetTransition; +import org.antlr.v4.runtime.atn.PlusBlockStartState; +import org.antlr.v4.runtime.atn.PlusLoopbackState; +import org.antlr.v4.runtime.atn.RuleStartState; +import org.antlr.v4.runtime.atn.RuleStopState; +import org.antlr.v4.runtime.atn.RuleTransition; +import org.antlr.v4.runtime.atn.SetTransition; +import org.antlr.v4.runtime.atn.StarBlockStartState; +import org.antlr.v4.runtime.atn.StarLoopEntryState; +import org.antlr.v4.runtime.atn.StarLoopbackState; +import org.antlr.v4.runtime.atn.Transition; +import org.antlr.v4.tool.Grammar; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** An ATN walker that knows how to dump them to serialized strings. */ +public class ATNPrinter { + List<ATNState> work; + Set<ATNState> marked; + Grammar g; + ATNState start; + + public ATNPrinter(Grammar g, ATNState start) { + this.g = g; + this.start = start; + } + + public String asString() { + if ( start==null ) return null; + marked = new HashSet<ATNState>(); + + work = new ArrayList<ATNState>(); + work.add(start); + + StringBuilder buf = new StringBuilder(); + ATNState s; + + while ( !work.isEmpty() ) { + s = work.remove(0); + if ( marked.contains(s) ) continue; + int n = s.getNumberOfTransitions(); +// System.out.println("visit "+s+"; edges="+n); + marked.add(s); + for (int i=0; i<n; i++) { + Transition t = s.transition(i); + if ( !(s instanceof RuleStopState) ) { // don't add follow states to work + if ( t instanceof RuleTransition ) work.add(((RuleTransition)t).followState); + else work.add( t.target ); + } + buf.append(getStateString(s)); + if ( t instanceof EpsilonTransition ) { + buf.append("->").append(getStateString(t.target)).append('\n'); + } + else if ( t instanceof RuleTransition ) { + buf.append("-").append(g.getRule(((RuleTransition)t).ruleIndex).name).append("->").append(getStateString(t.target)).append('\n'); + } + else if ( t instanceof ActionTransition ) { + ActionTransition a = (ActionTransition)t; + buf.append("-").append(a.toString()).append("->").append(getStateString(t.target)).append('\n'); + } + else if ( t instanceof SetTransition ) { + SetTransition st = (SetTransition)t; + boolean not = st instanceof NotSetTransition; + if ( g.isLexer() ) { + buf.append("-").append(not?"~":"").append(st.toString()).append("->").append(getStateString(t.target)).append('\n'); + } + else { + buf.append("-").append(not?"~":"").append(st.label().toString(g.getVocabulary())).append("->").append(getStateString(t.target)).append('\n'); + } + } + else if ( t instanceof AtomTransition ) { + AtomTransition a = (AtomTransition)t; + String label = g.getTokenDisplayName(a.label); + buf.append("-").append(label).append("->").append(getStateString(t.target)).append('\n'); + } + else { + buf.append("-").append(t.toString()).append("->").append(getStateString(t.target)).append('\n'); + } + } + } + return buf.toString(); + } + + String getStateString(ATNState s) { + int n = s.stateNumber; + String stateStr = "s"+n; + if ( s instanceof StarBlockStartState ) stateStr = "StarBlockStart_"+n; + else if ( s instanceof PlusBlockStartState ) stateStr = "PlusBlockStart_"+n; + else if ( s instanceof BlockStartState) stateStr = "BlockStart_"+n; + else if ( s instanceof BlockEndState ) stateStr = "BlockEnd_"+n; + else if ( s instanceof RuleStartState) stateStr = "RuleStart_"+g.getRule(s.ruleIndex).name+"_"+n; + else if ( s instanceof RuleStopState ) stateStr = "RuleStop_"+g.getRule(s.ruleIndex).name+"_"+n; + else if ( s instanceof PlusLoopbackState) stateStr = "PlusLoopBack_"+n; + else if ( s instanceof StarLoopbackState) stateStr = "StarLoopBack_"+n; + else if ( s instanceof StarLoopEntryState) stateStr = "StarLoopEntry_"+n; + return stateStr; + } +} diff --git a/tool/src/org/antlr/v4/automata/ATNVisitor.java b/tool/src/org/antlr/v4/automata/ATNVisitor.java new file mode 100644 index 0000000..af5e18a --- /dev/null +++ b/tool/src/org/antlr/v4/automata/ATNVisitor.java @@ -0,0 +1,61 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.automata; + +import org.antlr.v4.runtime.atn.ATNState; +import org.antlr.v4.runtime.atn.Transition; + +import java.util.HashSet; +import java.util.Set; + +/** A simple visitor that walks everywhere it can go starting from s, + * without going into an infinite cycle. Override and implement + * visitState() to provide functionality. + */ +public class ATNVisitor { + public void visit(ATNState s) { + visit_(s, new HashSet<Integer>()); + } + + public void visit_(ATNState s, Set<Integer> visited) { + if ( !visited.add(s.stateNumber) ) return; + visited.add(s.stateNumber); + + visitState(s); + int n = s.getNumberOfTransitions(); + for (int i=0; i<n; i++) { + Transition t = s.transition(i); + visit_(t.target, visited); + } + } + + public void visitState(ATNState s) { } +} diff --git a/tool/src/org/antlr/v4/automata/LexerATNFactory.java b/tool/src/org/antlr/v4/automata/LexerATNFactory.java new file mode 100644 index 0000000..4feff0a --- /dev/null +++ b/tool/src/org/antlr/v4/automata/LexerATNFactory.java @@ -0,0 +1,483 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.automata; + +import org.antlr.runtime.CommonToken; +import org.antlr.runtime.Token; +import org.antlr.v4.codegen.CodeGenerator; +import org.antlr.v4.misc.CharSupport; +import org.antlr.v4.parse.ANTLRParser; +import org.antlr.v4.runtime.IntStream; +import org.antlr.v4.runtime.Lexer; +import org.antlr.v4.runtime.atn.ATN; +import org.antlr.v4.runtime.atn.ATNState; +import org.antlr.v4.runtime.atn.ActionTransition; +import org.antlr.v4.runtime.atn.AtomTransition; +import org.antlr.v4.runtime.atn.LexerAction; +import org.antlr.v4.runtime.atn.LexerChannelAction; +import org.antlr.v4.runtime.atn.LexerCustomAction; +import org.antlr.v4.runtime.atn.LexerModeAction; +import org.antlr.v4.runtime.atn.LexerMoreAction; +import org.antlr.v4.runtime.atn.LexerPopModeAction; +import org.antlr.v4.runtime.atn.LexerPushModeAction; +import org.antlr.v4.runtime.atn.LexerSkipAction; +import org.antlr.v4.runtime.atn.LexerTypeAction; +import org.antlr.v4.runtime.atn.NotSetTransition; +import org.antlr.v4.runtime.atn.RangeTransition; +import org.antlr.v4.runtime.atn.RuleStartState; +import org.antlr.v4.runtime.atn.SetTransition; +import org.antlr.v4.runtime.atn.TokensStartState; +import org.antlr.v4.runtime.atn.Transition; +import org.antlr.v4.runtime.misc.Interval; +import org.antlr.v4.runtime.misc.IntervalSet; +import org.antlr.v4.tool.ErrorType; +import org.antlr.v4.tool.LexerGrammar; +import org.antlr.v4.tool.Rule; +import org.antlr.v4.tool.ast.ActionAST; +import org.antlr.v4.tool.ast.GrammarAST; +import org.antlr.v4.tool.ast.TerminalAST; +import org.stringtemplate.v4.ST; +import org.stringtemplate.v4.STGroup; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class LexerATNFactory extends ParserATNFactory { + public STGroup codegenTemplates; + + /** + * Provides a map of names of predefined constants which are likely to + * appear as the argument for lexer commands. These names would be resolved + * by the Java compiler for lexer commands that are translated to embedded + * actions, but are required during code generation for creating + * {@link LexerAction} instances that are usable by a lexer interpreter. + */ + protected static final Map<String, Integer> COMMON_CONSTANTS = new HashMap<String, Integer>(); + static { + COMMON_CONSTANTS.put("HIDDEN", Lexer.HIDDEN); + COMMON_CONSTANTS.put("DEFAULT_TOKEN_CHANNEL", Lexer.DEFAULT_TOKEN_CHANNEL); + COMMON_CONSTANTS.put("DEFAULT_MODE", Lexer.DEFAULT_MODE); + COMMON_CONSTANTS.put("SKIP", Lexer.SKIP); + COMMON_CONSTANTS.put("MORE", Lexer.MORE); + COMMON_CONSTANTS.put("EOF", Lexer.EOF); + COMMON_CONSTANTS.put("MAX_CHAR_VALUE", Lexer.MAX_CHAR_VALUE); + COMMON_CONSTANTS.put("MIN_CHAR_VALUE", Lexer.MIN_CHAR_VALUE); + } + + /** + * Maps from an action index to a {@link LexerAction} object. + */ + protected Map<Integer, LexerAction> indexToActionMap = new HashMap<Integer, LexerAction>(); + /** + * Maps from a {@link LexerAction} object to the action index. + */ + protected Map<LexerAction, Integer> actionToIndexMap = new HashMap<LexerAction, Integer>(); + + public LexerATNFactory(LexerGrammar g) { + super(g); + // use codegen to get correct language templates for lexer commands + String language = g.getOptionString("language"); + CodeGenerator gen = new CodeGenerator(g.tool, null, language); + codegenTemplates = gen.getTemplates(); + } + + @Override + public ATN createATN() { + // BUILD ALL START STATES (ONE PER MODE) + Set<String> modes = ((LexerGrammar) g).modes.keySet(); + for (String modeName : modes) { + // create s0, start state; implied Tokens rule node + TokensStartState startState = + newState(TokensStartState.class, null); + atn.modeNameToStartState.put(modeName, startState); + atn.modeToStartState.add(startState); + atn.defineDecisionState(startState); + } + + // INIT ACTION, RULE->TOKEN_TYPE MAP + atn.ruleToTokenType = new int[g.rules.size()]; + for (Rule r : g.rules.values()) { + atn.ruleToTokenType[r.index] = g.getTokenType(r.name); + } + + // CREATE ATN FOR EACH RULE + _createATN(g.rules.values()); + + atn.lexerActions = new LexerAction[indexToActionMap.size()]; + for (Map.Entry<Integer, LexerAction> entry : indexToActionMap.entrySet()) { + atn.lexerActions[entry.getKey()] = entry.getValue(); + } + + // LINK MODE START STATE TO EACH TOKEN RULE + for (String modeName : modes) { + List<Rule> rules = ((LexerGrammar)g).modes.get(modeName); + TokensStartState startState = atn.modeNameToStartState.get(modeName); + for (Rule r : rules) { + if ( !r.isFragment() ) { + RuleStartState s = atn.ruleToStartState[r.index]; + epsilon(startState, s); + } + } + } + + ATNOptimizer.optimize(g, atn); + return atn; + } + + @Override + public Handle action(ActionAST action) { + int ruleIndex = currentRule.index; + int actionIndex = g.lexerActions.get(action); + LexerCustomAction lexerAction = new LexerCustomAction(ruleIndex, actionIndex); + return action(action, lexerAction); + } + + protected int getLexerActionIndex(LexerAction lexerAction) { + Integer lexerActionIndex = actionToIndexMap.get(lexerAction); + if (lexerActionIndex == null) { + lexerActionIndex = actionToIndexMap.size(); + actionToIndexMap.put(lexerAction, lexerActionIndex); + indexToActionMap.put(lexerActionIndex, lexerAction); + } + + return lexerActionIndex; + } + + @Override + public Handle action(String action) { + if (action.trim().isEmpty()) { + ATNState left = newState(null); + ATNState right = newState(null); + epsilon(left, right); + return new Handle(left, right); + } + + // define action AST for this rule as if we had found in grammar + ActionAST ast = new ActionAST(new CommonToken(ANTLRParser.ACTION, action)); + currentRule.defineActionInAlt(currentOuterAlt, ast); + return action(ast); + } + + protected Handle action(GrammarAST node, LexerAction lexerAction) { + ATNState left = newState(node); + ATNState right = newState(node); + boolean isCtxDependent = false; + int lexerActionIndex = getLexerActionIndex(lexerAction); + ActionTransition a = + new ActionTransition(right, currentRule.index, lexerActionIndex, isCtxDependent); + left.addTransition(a); + node.atnState = left; + Handle h = new Handle(left, right); + return h; + } + + @Override + public Handle lexerAltCommands(Handle alt, Handle cmds) { + Handle h = new Handle(alt.left, cmds.right); + epsilon(alt.right, cmds.left); + return h; + } + + @Override + public Handle lexerCallCommand(GrammarAST ID, GrammarAST arg) { + LexerAction lexerAction = createLexerAction(ID, arg); + if (lexerAction != null) { + return action(ID, lexerAction); + } + + // fall back to standard action generation for the command + ST cmdST = codegenTemplates.getInstanceOf("Lexer" + + CharSupport.capitalize(ID.getText())+ + "Command"); + if (cmdST == null) { + g.tool.errMgr.grammarError(ErrorType.INVALID_LEXER_COMMAND, g.fileName, ID.token, ID.getText()); + return epsilon(ID); + } + + if (cmdST.impl.formalArguments == null || !cmdST.impl.formalArguments.containsKey("arg")) { + g.tool.errMgr.grammarError(ErrorType.UNWANTED_LEXER_COMMAND_ARGUMENT, g.fileName, ID.token, ID.getText()); + return epsilon(ID); + } + + cmdST.add("arg", arg.getText()); + return action(cmdST.render()); + } + + @Override + public Handle lexerCommand(GrammarAST ID) { + LexerAction lexerAction = createLexerAction(ID, null); + if (lexerAction != null) { + return action(ID, lexerAction); + } + + // fall back to standard action generation for the command + ST cmdST = codegenTemplates.getInstanceOf("Lexer" + + CharSupport.capitalize(ID.getText())+ + "Command"); + if (cmdST == null) { + g.tool.errMgr.grammarError(ErrorType.INVALID_LEXER_COMMAND, g.fileName, ID.token, ID.getText()); + return epsilon(ID); + } + + if (cmdST.impl.formalArguments != null && cmdST.impl.formalArguments.containsKey("arg")) { + g.tool.errMgr.grammarError(ErrorType.MISSING_LEXER_COMMAND_ARGUMENT, g.fileName, ID.token, ID.getText()); + return epsilon(ID); + } + + return action(cmdST.render()); + } + + @Override + public Handle range(GrammarAST a, GrammarAST b) { + ATNState left = newState(a); + ATNState right = newState(b); + int t1 = CharSupport.getCharValueFromGrammarCharLiteral(a.getText()); + int t2 = CharSupport.getCharValueFromGrammarCharLiteral(b.getText()); + left.addTransition(new RangeTransition(right, t1, t2)); + a.atnState = left; + b.atnState = left; + return new Handle(left, right); + } + + @Override + public Handle set(GrammarAST associatedAST, List<GrammarAST> alts, boolean invert) { + ATNState left = newState(associatedAST); + ATNState right = newState(associatedAST); + IntervalSet set = new IntervalSet(); + for (GrammarAST t : alts) { + if ( t.getType()==ANTLRParser.RANGE ) { + int a = CharSupport.getCharValueFromGrammarCharLiteral(t.getChild(0).getText()); + int b = CharSupport.getCharValueFromGrammarCharLiteral(t.getChild(1).getText()); + set.add(a, b); + } + else if ( t.getType()==ANTLRParser.LEXER_CHAR_SET ) { + set.addAll(getSetFromCharSetLiteral(t)); + } + else if ( t.getType()==ANTLRParser.STRING_LITERAL ) { + int c = CharSupport.getCharValueFromGrammarCharLiteral(t.getText()); + if ( c != -1 ) { + set.add(c); + } + else { + g.tool.errMgr.grammarError(ErrorType.INVALID_LITERAL_IN_LEXER_SET, + g.fileName, t.getToken(), t.getText()); + + } + } + else if ( t.getType()==ANTLRParser.TOKEN_REF ) { + g.tool.errMgr.grammarError(ErrorType.UNSUPPORTED_REFERENCE_IN_LEXER_SET, + g.fileName, t.getToken(), t.getText()); + } + } + if ( invert ) { + left.addTransition(new NotSetTransition(right, set)); + } + else { + Transition transition; + if (set.getIntervals().size() == 1) { + Interval interval = set.getIntervals().get(0); + transition = new RangeTransition(right, interval.a, interval.b); + } else { + transition = new SetTransition(right, set); + } + + left.addTransition(transition); + } + associatedAST.atnState = left; + return new Handle(left, right); + } + + /** For a lexer, a string is a sequence of char to match. That is, + * "fog" is treated as 'f' 'o' 'g' not as a single transition in + * the DFA. Machine== o-'f'->o-'o'->o-'g'->o and has n+1 states + * for n characters. + */ + @Override + public Handle stringLiteral(TerminalAST stringLiteralAST) { + String chars = stringLiteralAST.getText(); + chars = CharSupport.getStringFromGrammarStringLiteral(chars); + int n = chars.length(); + ATNState left = newState(stringLiteralAST); + ATNState prev = left; + ATNState right = null; + for (int i=0; i<n; i++) { + right = newState(stringLiteralAST); + prev.addTransition(new AtomTransition(right, chars.charAt(i))); + prev = right; + } + stringLiteralAST.atnState = left; + return new Handle(left, right); + } + + /** [Aa\t \u1234a-z\]\-] char sets */ + @Override + public Handle charSetLiteral(GrammarAST charSetAST) { + ATNState left = newState(charSetAST); + ATNState right = newState(charSetAST); + IntervalSet set = getSetFromCharSetLiteral(charSetAST); + left.addTransition(new SetTransition(right, set)); + charSetAST.atnState = left; + return new Handle(left, right); + } + + public IntervalSet getSetFromCharSetLiteral(GrammarAST charSetAST) { + String chars = charSetAST.getText(); + chars = chars.substring(1, chars.length()-1); + String cset = '"'+ chars +'"'; + IntervalSet set = new IntervalSet(); + + // unescape all valid escape char like \n, leaving escaped dashes as '\-' + // so we can avoid seeing them as '-' range ops. + chars = CharSupport.getStringFromGrammarStringLiteral(cset); + // now make x-y become set of char + int n = chars.length(); + for (int i=0; i< n; i++) { + int c = chars.charAt(i); + if ( c=='\\' && (i+1)<n && chars.charAt(i+1)=='-' ) { // \- + set.add('-'); + i++; + } + else if ( (i+2)<n && chars.charAt(i+1)=='-' ) { // range x-y + int x = c; + int y = chars.charAt(i+2); + if ( x<=y ) set.add(x,y); + i+=2; + } + else { + set.add(c); + } + } + return set; + } + + @Override + public Handle tokenRef(TerminalAST node) { + // Ref to EOF in lexer yields char transition on -1 + if ( node.getText().equals("EOF") ) { + ATNState left = newState(node); + ATNState right = newState(node); + left.addTransition(new AtomTransition(right, IntStream.EOF)); + return new Handle(left, right); + } + return _ruleRef(node); + } + + + protected LexerAction createLexerAction(GrammarAST ID, GrammarAST arg) { + String command = ID.getText(); + if ("skip".equals(command) && arg == null) { + return LexerSkipAction.INSTANCE; + } + else if ("more".equals(command) && arg == null) { + return LexerMoreAction.INSTANCE; + } + else if ("popMode".equals(command) && arg == null) { + return LexerPopModeAction.INSTANCE; + } + else if ("mode".equals(command) && arg != null) { + String modeName = arg.getText(); + Integer mode = getConstantValue(modeName, arg.getToken()); + if (mode == null) { + return null; + } + + return new LexerModeAction(mode); + } + else if ("pushMode".equals(command) && arg != null) { + String modeName = arg.getText(); + Integer mode = getConstantValue(modeName, arg.getToken()); + if (mode == null) { + return null; + } + + return new LexerPushModeAction(mode); + } + else if ("type".equals(command) && arg != null) { + String typeName = arg.getText(); + Integer type = getConstantValue(typeName, arg.getToken()); + if (type == null) { + return null; + } + + return new LexerTypeAction(type); + } + else if ("channel".equals(command) && arg != null) { + String channelName = arg.getText(); + Integer channel = getConstantValue(channelName, arg.getToken()); + if (channel == null) { + return null; + } + + return new LexerChannelAction(channel); + } + else { + return null; + } + } + + + protected Integer getConstantValue(String name, Token token) { + if (name == null) { + return null; + } + + Integer commonConstant = COMMON_CONSTANTS.get(name); + if (commonConstant != null) { + return commonConstant; + } + + int tokenType = g.getTokenType(name); + if (tokenType != org.antlr.v4.runtime.Token.INVALID_TYPE) { + return tokenType; + } + + int channelValue = g.getChannelValue(name); + if (channelValue >= org.antlr.v4.runtime.Token.MIN_USER_CHANNEL_VALUE) { + return channelValue; + } + + List<String> modeNames = new ArrayList<String>(((LexerGrammar)g).modes.keySet()); + int mode = modeNames.indexOf(name); + if (mode >= 0) { + return mode; + } + + try { + return Integer.parseInt(name); + } catch (NumberFormatException ex) { + g.tool.errMgr.grammarError(ErrorType.UNKNOWN_LEXER_CONSTANT, g.fileName, token, currentRule.name, token != null ? token.getText() : null); + return null; + } + } +} diff --git a/tool/src/org/antlr/v4/automata/ParserATNFactory.java b/tool/src/org/antlr/v4/automata/ParserATNFactory.java new file mode 100644 index 0000000..3664111 --- /dev/null +++ b/tool/src/org/antlr/v4/automata/ParserATNFactory.java @@ -0,0 +1,797 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.automata; + + +import org.antlr.runtime.RecognitionException; +import org.antlr.runtime.Token; +import org.antlr.runtime.tree.CommonTreeNodeStream; +import org.antlr.runtime.tree.Tree; +import org.antlr.v4.analysis.LeftRecursiveRuleTransformer; +import org.antlr.v4.misc.CharSupport; +import org.antlr.v4.parse.ANTLRParser; +import org.antlr.v4.parse.ATNBuilder; +import org.antlr.v4.parse.GrammarASTAdaptor; +import org.antlr.v4.runtime.atn.ATN; +import org.antlr.v4.runtime.atn.ATNState; +import org.antlr.v4.runtime.atn.ATNType; +import org.antlr.v4.runtime.atn.AbstractPredicateTransition; +import org.antlr.v4.runtime.atn.ActionTransition; +import org.antlr.v4.runtime.atn.AtomTransition; +import org.antlr.v4.runtime.atn.BasicBlockStartState; +import org.antlr.v4.runtime.atn.BasicState; +import org.antlr.v4.runtime.atn.BlockEndState; +import org.antlr.v4.runtime.atn.BlockStartState; +import org.antlr.v4.runtime.atn.EpsilonTransition; +import org.antlr.v4.runtime.atn.LL1Analyzer; +import org.antlr.v4.runtime.atn.LoopEndState; +import org.antlr.v4.runtime.atn.NotSetTransition; +import org.antlr.v4.runtime.atn.PlusBlockStartState; +import org.antlr.v4.runtime.atn.PlusLoopbackState; +import org.antlr.v4.runtime.atn.PrecedencePredicateTransition; +import org.antlr.v4.runtime.atn.PredicateTransition; +import org.antlr.v4.runtime.atn.RuleStartState; +import org.antlr.v4.runtime.atn.RuleStopState; +import org.antlr.v4.runtime.atn.RuleTransition; +import org.antlr.v4.runtime.atn.SetTransition; +import org.antlr.v4.runtime.atn.StarBlockStartState; +import org.antlr.v4.runtime.atn.StarLoopEntryState; +import org.antlr.v4.runtime.atn.StarLoopbackState; +import org.antlr.v4.runtime.atn.Transition; +import org.antlr.v4.runtime.atn.WildcardTransition; +import org.antlr.v4.runtime.misc.IntervalSet; +import org.antlr.v4.runtime.misc.Triple; +import org.antlr.v4.semantics.UseDefAnalyzer; +import org.antlr.v4.tool.ErrorManager; +import org.antlr.v4.tool.ErrorType; +import org.antlr.v4.tool.Grammar; +import org.antlr.v4.tool.LeftRecursiveRule; +import org.antlr.v4.tool.LexerGrammar; +import org.antlr.v4.tool.Rule; +import org.antlr.v4.tool.ast.ActionAST; +import org.antlr.v4.tool.ast.AltAST; +import org.antlr.v4.tool.ast.BlockAST; +import org.antlr.v4.tool.ast.GrammarAST; +import org.antlr.v4.tool.ast.GrammarASTWithOptions; +import org.antlr.v4.tool.ast.PredAST; +import org.antlr.v4.tool.ast.QuantifierAST; +import org.antlr.v4.tool.ast.TerminalAST; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** ATN construction routines triggered by ATNBuilder.g. + * + * No side-effects. It builds an {@link ATN} object and returns it. + */ +public class ParserATNFactory implements ATNFactory { + + public final Grammar g; + + + public final ATN atn; + + public Rule currentRule; + + public int currentOuterAlt; + + + protected final List<Triple<Rule, ATNState, ATNState>> preventEpsilonClosureBlocks = + new ArrayList<Triple<Rule, ATNState, ATNState>>(); + + + protected final List<Triple<Rule, ATNState, ATNState>> preventEpsilonOptionalBlocks = + new ArrayList<Triple<Rule, ATNState, ATNState>>(); + + public ParserATNFactory(Grammar g) { + if (g == null) { + throw new NullPointerException("g"); + } + + this.g = g; + + ATNType atnType = g instanceof LexerGrammar ? ATNType.LEXER : ATNType.PARSER; + int maxTokenType = g.getMaxTokenType(); + this.atn = new ATN(atnType, maxTokenType); + } + + + @Override + public ATN createATN() { + _createATN(g.rules.values()); + assert atn.maxTokenType == g.getMaxTokenType(); + addRuleFollowLinks(); + addEOFTransitionToStartRules(); + ATNOptimizer.optimize(g, atn); + + for (Triple<Rule, ATNState, ATNState> pair : preventEpsilonClosureBlocks) { + LL1Analyzer analyzer = new LL1Analyzer(atn); + if (analyzer.LOOK(pair.b, pair.c, null).contains(org.antlr.v4.runtime.Token.EPSILON)) { + ErrorType errorType = pair.a instanceof LeftRecursiveRule ? ErrorType.EPSILON_LR_FOLLOW : ErrorType.EPSILON_CLOSURE; + g.tool.errMgr.grammarError(errorType, g.fileName, ((GrammarAST)pair.a.ast.getChild(0)).getToken(), pair.a.name); + } + } + + optionalCheck: + for (Triple<Rule, ATNState, ATNState> pair : preventEpsilonOptionalBlocks) { + int bypassCount = 0; + for (int i = 0; i < pair.b.getNumberOfTransitions(); i++) { + ATNState startState = pair.b.transition(i).target; + if (startState == pair.c) { + bypassCount++; + continue; + } + + LL1Analyzer analyzer = new LL1Analyzer(atn); + if (analyzer.LOOK(startState, pair.c, null).contains(org.antlr.v4.runtime.Token.EPSILON)) { + g.tool.errMgr.grammarError(ErrorType.EPSILON_OPTIONAL, g.fileName, ((GrammarAST)pair.a.ast.getChild(0)).getToken(), pair.a.name); + continue optionalCheck; + } + } + + if (bypassCount != 1) { + throw new UnsupportedOperationException("Expected optional block with exactly 1 bypass alternative."); + } + } + + return atn; + } + + protected void _createATN(Collection<Rule> rules) { + createRuleStartAndStopATNStates(); + + GrammarASTAdaptor adaptor = new GrammarASTAdaptor(); + for (Rule r : rules) { + // find rule's block + GrammarAST blk = (GrammarAST)r.ast.getFirstChildWithType(ANTLRParser.BLOCK); + CommonTreeNodeStream nodes = new CommonTreeNodeStream(adaptor,blk); + ATNBuilder b = new ATNBuilder(nodes,this); + try { + setCurrentRuleName(r.name); + Handle h = b.ruleBlock(null); + rule(r.ast, r.name, h); + } + catch (RecognitionException re) { + ErrorManager.fatalInternalError("bad grammar AST structure", re); + } + } + } + + @Override + public void setCurrentRuleName(String name) { + this.currentRule = g.getRule(name); + } + + @Override + public void setCurrentOuterAlt(int alt) { + currentOuterAlt = alt; + } + + /* start->ruleblock->end */ + + @Override + public Handle rule(GrammarAST ruleAST, String name, Handle blk) { + Rule r = g.getRule(name); + RuleStartState start = atn.ruleToStartState[r.index]; + epsilon(start, blk.left); + RuleStopState stop = atn.ruleToStopState[r.index]; + epsilon(blk.right, stop); + Handle h = new Handle(start, stop); +// ATNPrinter ser = new ATNPrinter(g, h.left); +// System.out.println(ruleAST.toStringTree()+":\n"+ser.asString()); + ruleAST.atnState = start; + return h; + } + + /** From label {@code A} build graph {@code o-A->o}. */ + + @Override + public Handle tokenRef(TerminalAST node) { + ATNState left = newState(node); + ATNState right = newState(node); + int ttype = g.getTokenType(node.getText()); + left.addTransition(new AtomTransition(right, ttype)); + node.atnState = left; + return new Handle(left, right); + } + + /** From set build single edge graph {@code o->o-set->o}. To conform to + * what an alt block looks like, must have extra state on left. + * This also handles {@code ~A}, converted to {@code ~{A}} set. + */ + + @Override + public Handle set(GrammarAST associatedAST, List<GrammarAST> terminals, boolean invert) { + ATNState left = newState(associatedAST); + ATNState right = newState(associatedAST); + IntervalSet set = new IntervalSet(); + for (GrammarAST t : terminals) { + int ttype = g.getTokenType(t.getText()); + set.add(ttype); + } + if ( invert ) { + left.addTransition(new NotSetTransition(right, set)); + } + else { + left.addTransition(new SetTransition(right, set)); + } + associatedAST.atnState = left; + return new Handle(left, right); + } + + /** Not valid for non-lexers. */ + + @Override + public Handle range(GrammarAST a, GrammarAST b) { + throw new UnsupportedOperationException("This construct is not valid in parsers."); + } + + protected int getTokenType(GrammarAST atom) { + int ttype; + if ( g.isLexer() ) { + ttype = CharSupport.getCharValueFromGrammarCharLiteral(atom.getText()); + } + else { + ttype = g.getTokenType(atom.getText()); + } + return ttype; + } + + /** For a non-lexer, just build a simple token reference atom. */ + + @Override + public Handle stringLiteral(TerminalAST stringLiteralAST) { + return tokenRef(stringLiteralAST); + } + + /** {@code [Aa]} char sets not allowed in parser */ + + @Override + public Handle charSetLiteral(GrammarAST charSetAST) { + return null; + } + + /** + * For reference to rule {@code r}, build + * + * <pre> + * o->(r) o + * </pre> + * + * where {@code (r)} is the start of rule {@code r} and the trailing + * {@code o} is not linked to from rule ref state directly (uses + * {@link RuleTransition#followState}). + */ + + @Override + public Handle ruleRef(GrammarAST node) { + Handle h = _ruleRef(node); + return h; + } + + + public Handle _ruleRef(GrammarAST node) { + Rule r = g.getRule(node.getText()); + if ( r==null ) { + g.tool.errMgr.grammarError(ErrorType.INTERNAL_ERROR, g.fileName, node.getToken(), "Rule "+node.getText()+" undefined"); + return null; + } + RuleStartState start = atn.ruleToStartState[r.index]; + ATNState left = newState(node); + ATNState right = newState(node); + int precedence = 0; + if (((GrammarASTWithOptions)node).getOptionString(LeftRecursiveRuleTransformer.PRECEDENCE_OPTION_NAME) != null) { + precedence = Integer.parseInt(((GrammarASTWithOptions)node).getOptionString(LeftRecursiveRuleTransformer.PRECEDENCE_OPTION_NAME)); + } + RuleTransition call = new RuleTransition(start, r.index, precedence, right); + left.addTransition(call); + + node.atnState = left; + return new Handle(left, right); + } + + public void addFollowLink(int ruleIndex, ATNState right) { + // add follow edge from end of invoked rule + RuleStopState stop = atn.ruleToStopState[ruleIndex]; +// System.out.println("add follow link from "+ruleIndex+" to "+right); + epsilon(stop, right); + } + + /** From an empty alternative build {@code o-e->o}. */ + + @Override + public Handle epsilon(GrammarAST node) { + ATNState left = newState(node); + ATNState right = newState(node); + epsilon(left, right); + node.atnState = left; + return new Handle(left, right); + } + + /** Build what amounts to an epsilon transition with a semantic + * predicate action. The {@code pred} is a pointer into the AST of + * the {@link ANTLRParser#SEMPRED} token. + */ + + @Override + public Handle sempred(PredAST pred) { + //System.out.println("sempred: "+ pred); + ATNState left = newState(pred); + ATNState right = newState(pred); + + AbstractPredicateTransition p; + if (pred.getOptionString(LeftRecursiveRuleTransformer.PRECEDENCE_OPTION_NAME) != null) { + int precedence = Integer.parseInt(pred.getOptionString(LeftRecursiveRuleTransformer.PRECEDENCE_OPTION_NAME)); + p = new PrecedencePredicateTransition(right, precedence); + } + else { + boolean isCtxDependent = UseDefAnalyzer.actionIsContextDependent(pred); + p = new PredicateTransition(right, currentRule.index, g.sempreds.get(pred), isCtxDependent); + } + + left.addTransition(p); + pred.atnState = left; + return new Handle(left, right); + } + + /** Build what amounts to an epsilon transition with an action. + * The action goes into ATN though it is ignored during prediction + * if {@link ActionTransition#actionIndex actionIndex}{@code <0}. + */ + + @Override + public Handle action(ActionAST action) { + //System.out.println("action: "+action); + ATNState left = newState(action); + ATNState right = newState(action); + ActionTransition a = new ActionTransition(right, currentRule.index); + left.addTransition(a); + action.atnState = left; + return new Handle(left, right); + } + + + @Override + public Handle action(String action) { + throw new UnsupportedOperationException("This element is not valid in parsers."); + } + + /** + * From {@code A|B|..|Z} alternative block build + * + * <pre> + * o->o-A->o->o (last ATNState is BlockEndState pointed to by all alts) + * | ^ + * |->o-B->o--| + * | | + * ... | + * | | + * |->o-Z->o--| + * </pre> + * + * So start node points at every alternative with epsilon transition and + * every alt right side points at a block end ATNState. + * <p/> + * Special case: only one alternative: don't make a block with alt + * begin/end. + * <p/> + * Special case: if just a list of tokens/chars/sets, then collapse to a + * single edged o-set->o graph. + * <p/> + * TODO: Set alt number (1..n) in the states? + */ + + @Override + public Handle block(BlockAST blkAST, GrammarAST ebnfRoot, List<Handle> alts) { + if ( ebnfRoot==null ) { + if ( alts.size()==1 ) { + Handle h = alts.get(0); + blkAST.atnState = h.left; + return h; + } + BlockStartState start = newState(BasicBlockStartState.class, blkAST); + if ( alts.size()>1 ) atn.defineDecisionState(start); + return makeBlock(start, blkAST, alts); + } + switch ( ebnfRoot.getType() ) { + case ANTLRParser.OPTIONAL : + BlockStartState start = newState(BasicBlockStartState.class, blkAST); + atn.defineDecisionState(start); + Handle h = makeBlock(start, blkAST, alts); + return optional(ebnfRoot, h); + case ANTLRParser.CLOSURE : + BlockStartState star = newState(StarBlockStartState.class, ebnfRoot); + if ( alts.size()>1 ) atn.defineDecisionState(star); + h = makeBlock(star, blkAST, alts); + return star(ebnfRoot, h); + case ANTLRParser.POSITIVE_CLOSURE : + PlusBlockStartState plus = newState(PlusBlockStartState.class, ebnfRoot); + if ( alts.size()>1 ) atn.defineDecisionState(plus); + h = makeBlock(plus, blkAST, alts); + return plus(ebnfRoot, h); + } + return null; + } + + + protected Handle makeBlock(BlockStartState start, BlockAST blkAST, List<Handle> alts) { + BlockEndState end = newState(BlockEndState.class, blkAST); + start.endState = end; + for (Handle alt : alts) { + // hook alts up to decision block + epsilon(start, alt.left); + epsilon(alt.right, end); + // no back link in ATN so must walk entire alt to see if we can + // strip out the epsilon to 'end' state + TailEpsilonRemover opt = new TailEpsilonRemover(atn); + opt.visit(alt.left); + } + Handle h = new Handle(start, end); +// FASerializer ser = new FASerializer(g, h.left); +// System.out.println(blkAST.toStringTree()+":\n"+ser); + blkAST.atnState = start; + + return h; + } + + + @Override + public Handle alt(List<Handle> els) { + return elemList(els); + } + + + public Handle elemList(List<Handle> els) { + int n = els.size(); + for (int i = 0; i < n - 1; i++) { // hook up elements (visit all but last) + Handle el = els.get(i); + // if el is of form o-x->o for x in {rule, action, pred, token, ...} + // and not last in alt + Transition tr = null; + if ( el.left.getNumberOfTransitions()==1 ) tr = el.left.transition(0); + boolean isRuleTrans = tr instanceof RuleTransition; + if ( el.left.getStateType() == ATNState.BASIC && + el.right.getStateType()== ATNState.BASIC && + tr!=null && (isRuleTrans && ((RuleTransition)tr).followState == el.right || tr.target == el.right) ) + { + // we can avoid epsilon edge to next el + if ( isRuleTrans ) ((RuleTransition)tr).followState = els.get(i+1).left; + else tr.target = els.get(i+1).left; + atn.removeState(el.right); // we skipped over this state + } + else { // need epsilon if previous block's right end node is complicated + epsilon(el.right, els.get(i+1).left); + } + } + Handle first = els.get(0); + Handle last = els.get(n -1); + if ( first==null || last==null ) { + g.tool.errMgr.toolError(ErrorType.INTERNAL_ERROR, "element list has first|last == null"); + } + return new Handle(first.left, last.right); + } + + /** + * From {@code (A)?} build either: + * + * <pre> + * o--A->o + * | ^ + * o---->| + * </pre> + * + * or, if {@code A} is a block, just add an empty alt to the end of the + * block + */ + + @Override + public Handle optional(GrammarAST optAST, Handle blk) { + BlockStartState blkStart = (BlockStartState)blk.left; + ATNState blkEnd = blk.right; + preventEpsilonOptionalBlocks.add(new Triple<Rule, ATNState, ATNState>(currentRule, blkStart, blkEnd)); + + boolean greedy = ((QuantifierAST)optAST).isGreedy(); + blkStart.nonGreedy = !greedy; + epsilon(blkStart, blk.right, !greedy); + + optAST.atnState = blk.left; + return blk; + } + + /** + * From {@code (blk)+} build + * + * <pre> + * |---------| + * v | + * [o-blk-o]->o->o + * </pre> + * + * We add a decision for loop back node to the existing one at {@code blk} + * start. + */ + + @Override + public Handle plus(GrammarAST plusAST, Handle blk) { + PlusBlockStartState blkStart = (PlusBlockStartState)blk.left; + BlockEndState blkEnd = (BlockEndState)blk.right; + preventEpsilonClosureBlocks.add(new Triple<Rule, ATNState, ATNState>(currentRule, blkStart, blkEnd)); + + PlusLoopbackState loop = newState(PlusLoopbackState.class, plusAST); + loop.nonGreedy = !((QuantifierAST)plusAST).isGreedy(); + atn.defineDecisionState(loop); + LoopEndState end = newState(LoopEndState.class, plusAST); + blkStart.loopBackState = loop; + end.loopBackState = loop; + + plusAST.atnState = loop; + epsilon(blkEnd, loop); // blk can see loop back + + BlockAST blkAST = (BlockAST)plusAST.getChild(0); + if ( ((QuantifierAST)plusAST).isGreedy() ) { + if (expectNonGreedy(blkAST)) { + g.tool.errMgr.grammarError(ErrorType.EXPECTED_NON_GREEDY_WILDCARD_BLOCK, g.fileName, plusAST.getToken(), plusAST.getToken().getText()); + } + + epsilon(loop, blkStart); // loop back to start + epsilon(loop, end); // or exit + } + else { + // if not greedy, priority to exit branch; make it first + epsilon(loop, end); // exit + epsilon(loop, blkStart); // loop back to start + } + + return new Handle(blkStart, end); + } + + /** + * From {@code (blk)*} build {@code ( blk+ )?} with *two* decisions, one for + * entry and one for choosing alts of {@code blk}. + * + * <pre> + * |-------------| + * v | + * o--[o-blk-o]->o o + * | ^ + * -----------------| + * </pre> + * + * Note that the optional bypass must jump outside the loop as + * {@code (A|B)*} is not the same thing as {@code (A|B|)+}. + */ + + @Override + public Handle star(GrammarAST starAST, Handle elem) { + StarBlockStartState blkStart = (StarBlockStartState)elem.left; + BlockEndState blkEnd = (BlockEndState)elem.right; + preventEpsilonClosureBlocks.add(new Triple<Rule, ATNState, ATNState>(currentRule, blkStart, blkEnd)); + + StarLoopEntryState entry = newState(StarLoopEntryState.class, starAST); + entry.nonGreedy = !((QuantifierAST)starAST).isGreedy(); + atn.defineDecisionState(entry); + LoopEndState end = newState(LoopEndState.class, starAST); + StarLoopbackState loop = newState(StarLoopbackState.class, starAST); + entry.loopBackState = loop; + end.loopBackState = loop; + + BlockAST blkAST = (BlockAST)starAST.getChild(0); + if ( ((QuantifierAST)starAST).isGreedy() ) { + if (expectNonGreedy(blkAST)) { + g.tool.errMgr.grammarError(ErrorType.EXPECTED_NON_GREEDY_WILDCARD_BLOCK, g.fileName, starAST.getToken(), starAST.getToken().getText()); + } + + epsilon(entry, blkStart); // loop enter edge (alt 1) + epsilon(entry, end); // bypass loop edge (alt 2) + } + else { + // if not greedy, priority to exit branch; make it first + epsilon(entry, end); // bypass loop edge (alt 1) + epsilon(entry, blkStart); // loop enter edge (alt 2) + } + epsilon(blkEnd, loop); // block end hits loop back + epsilon(loop, entry); // loop back to entry/exit decision + + starAST.atnState = entry; // decision is to enter/exit; blk is its own decision + return new Handle(entry, end); + } + + /** Build an atom with all possible values in its label. */ + + @Override + public Handle wildcard(GrammarAST node) { + ATNState left = newState(node); + ATNState right = newState(node); + left.addTransition(new WildcardTransition(right)); + node.atnState = left; + return new Handle(left, right); + } + + protected void epsilon(ATNState a, ATNState b) { + epsilon(a, b, false); + } + + protected void epsilon(ATNState a, ATNState b, boolean prepend) { + if ( a!=null ) { + int index = prepend ? 0 : a.getNumberOfTransitions(); + a.addTransition(index, new EpsilonTransition(b)); + } + } + + /** Define all the rule begin/end ATNStates to solve forward reference + * issues. + */ + void createRuleStartAndStopATNStates() { + atn.ruleToStartState = new RuleStartState[g.rules.size()]; + atn.ruleToStopState = new RuleStopState[g.rules.size()]; + for (Rule r : g.rules.values()) { + RuleStartState start = newState(RuleStartState.class, r.ast); + RuleStopState stop = newState(RuleStopState.class, r.ast); + start.stopState = stop; + start.isLeftRecursiveRule = r instanceof LeftRecursiveRule; + start.setRuleIndex(r.index); + stop.setRuleIndex(r.index); + atn.ruleToStartState[r.index] = start; + atn.ruleToStopState[r.index] = stop; + } + } + + public void addRuleFollowLinks() { + for (ATNState p : atn.states) { + if ( p!=null && + p.getStateType() == ATNState.BASIC && p.getNumberOfTransitions()==1 && + p.transition(0) instanceof RuleTransition ) + { + RuleTransition rt = (RuleTransition) p.transition(0); + addFollowLink(rt.ruleIndex, rt.followState); + } + } + } + + /** Add an EOF transition to any rule end ATNState that points to nothing + * (i.e., for all those rules not invoked by another rule). These + * are start symbols then. + * + * Return the number of grammar entry points; i.e., how many rules are + * not invoked by another rule (they can only be invoked from outside). + * These are the start rules. + */ + public int addEOFTransitionToStartRules() { + int n = 0; + ATNState eofTarget = newState(null); // one unique EOF target for all rules + for (Rule r : g.rules.values()) { + ATNState stop = atn.ruleToStopState[r.index]; + if ( stop.getNumberOfTransitions()>0 ) continue; + n++; + Transition t = new AtomTransition(eofTarget, Token.EOF); + stop.addTransition(t); + } + return n; + } + + + @Override + public Handle label(Handle t) { + return t; + } + + + @Override + public Handle listLabel(Handle t) { + return t; + } + + + public <T extends ATNState> T newState(Class<T> nodeType, GrammarAST node) { + Exception cause; + try { + Constructor<T> ctor = nodeType.getConstructor(); + T s = ctor.newInstance(); + if ( currentRule==null ) s.setRuleIndex(-1); + else s.setRuleIndex(currentRule.index); + atn.addState(s); + return s; + } catch (InstantiationException ex) { + cause = ex; + } catch (IllegalAccessException ex) { + cause = ex; + } catch (IllegalArgumentException ex) { + cause = ex; + } catch (InvocationTargetException ex) { + cause = ex; + } catch (NoSuchMethodException ex) { + cause = ex; + } catch (SecurityException ex) { + cause = ex; + } + + String message = String.format("Could not create %s of type %s.", ATNState.class.getName(), nodeType.getName()); + throw new UnsupportedOperationException(message, cause); + } + + + public ATNState newState(GrammarAST node) { + ATNState n = new BasicState(); + n.setRuleIndex(currentRule.index); + atn.addState(n); + return n; + } + + + @Override + public ATNState newState() { return newState(null); } + + public boolean expectNonGreedy(BlockAST blkAST) { + if ( blockHasWildcardAlt(blkAST) ) { + return true; + } + + return false; + } + + /** + * {@code (BLOCK (ALT .))} or {@code (BLOCK (ALT 'a') (ALT .))}. + */ + public static boolean blockHasWildcardAlt(GrammarAST block) { + for (Object alt : block.getChildren()) { + if ( !(alt instanceof AltAST) ) continue; + AltAST altAST = (AltAST)alt; + if ( altAST.getChildCount()==1 || (altAST.getChildCount() == 2 && altAST.getChild(0).getType() == ANTLRParser.ELEMENT_OPTIONS) ) { + Tree e = altAST.getChild(altAST.getChildCount() - 1); + if ( e.getType()==ANTLRParser.WILDCARD ) { + return true; + } + } + } + return false; + } + + + @Override + public Handle lexerAltCommands(Handle alt, Handle cmds) { + throw new UnsupportedOperationException("This element is not allowed in parsers."); + } + + + @Override + public Handle lexerCallCommand(GrammarAST ID, GrammarAST arg) { + throw new UnsupportedOperationException("This element is not allowed in parsers."); + } + + + @Override + public Handle lexerCommand(GrammarAST ID) { + throw new UnsupportedOperationException("This element is not allowed in parsers."); + } +} diff --git a/tool/src/org/antlr/v4/automata/TailEpsilonRemover.java b/tool/src/org/antlr/v4/automata/TailEpsilonRemover.java new file mode 100644 index 0000000..35d1594 --- /dev/null +++ b/tool/src/org/antlr/v4/automata/TailEpsilonRemover.java @@ -0,0 +1,81 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.automata; + +import org.antlr.v4.runtime.atn.ATN; +import org.antlr.v4.runtime.atn.ATNState; +import org.antlr.v4.runtime.atn.BlockEndState; +import org.antlr.v4.runtime.atn.EpsilonTransition; +import org.antlr.v4.runtime.atn.PlusLoopbackState; +import org.antlr.v4.runtime.atn.RuleTransition; +import org.antlr.v4.runtime.atn.StarLoopbackState; +import org.antlr.v4.runtime.atn.Transition; + +/** + * + * @author Terence Parr + */ +public class TailEpsilonRemover extends ATNVisitor { + + private final ATN _atn; + + public TailEpsilonRemover(ATN atn) { + this._atn = atn; + } + + @Override + public void visitState(ATNState p) { + if (p.getStateType() == ATNState.BASIC && p.getNumberOfTransitions() == 1) { + ATNState q = p.transition(0).target; + if (p.transition(0) instanceof RuleTransition) { + q = ((RuleTransition) p.transition(0)).followState; + } + if (q.getStateType() == ATNState.BASIC) { + // we have p-x->q for x in {rule, action, pred, token, ...} + // if edge out of q is single epsilon to block end + // we can strip epsilon p-x->q-eps->r + Transition trans = q.transition(0); + if (q.getNumberOfTransitions() == 1 && trans instanceof EpsilonTransition) { + ATNState r = trans.target; + if (r instanceof BlockEndState || r instanceof PlusLoopbackState || r instanceof StarLoopbackState) { + // skip over q + if (p.transition(0) instanceof RuleTransition) { + ((RuleTransition) p.transition(0)).followState = r; + } else { + p.transition(0).target = r; + } + _atn.removeState(q); + } + } + } + } + } +} diff --git a/tool/src/org/antlr/v4/codegen/ActionTranslator.java b/tool/src/org/antlr/v4/codegen/ActionTranslator.java new file mode 100644 index 0000000..2a12e8e --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/ActionTranslator.java @@ -0,0 +1,322 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen; + +import org.antlr.runtime.ANTLRStringStream; +import org.antlr.runtime.Token; +import org.antlr.v4.codegen.model.RuleFunction; +import org.antlr.v4.codegen.model.chunk.ActionChunk; +import org.antlr.v4.codegen.model.chunk.ActionText; +import org.antlr.v4.codegen.model.chunk.ArgRef; +import org.antlr.v4.codegen.model.chunk.LabelRef; +import org.antlr.v4.codegen.model.chunk.ListLabelRef; +import org.antlr.v4.codegen.model.chunk.LocalRef; +import org.antlr.v4.codegen.model.chunk.NonLocalAttrRef; +import org.antlr.v4.codegen.model.chunk.QRetValueRef; +import org.antlr.v4.codegen.model.chunk.RetValueRef; +import org.antlr.v4.codegen.model.chunk.RulePropertyRef; +import org.antlr.v4.codegen.model.chunk.RulePropertyRef_ctx; +import org.antlr.v4.codegen.model.chunk.RulePropertyRef_parser; +import org.antlr.v4.codegen.model.chunk.RulePropertyRef_start; +import org.antlr.v4.codegen.model.chunk.RulePropertyRef_stop; +import org.antlr.v4.codegen.model.chunk.RulePropertyRef_text; +import org.antlr.v4.codegen.model.chunk.SetAttr; +import org.antlr.v4.codegen.model.chunk.SetNonLocalAttr; +import org.antlr.v4.codegen.model.chunk.ThisRulePropertyRef_ctx; +import org.antlr.v4.codegen.model.chunk.ThisRulePropertyRef_parser; +import org.antlr.v4.codegen.model.chunk.ThisRulePropertyRef_start; +import org.antlr.v4.codegen.model.chunk.ThisRulePropertyRef_stop; +import org.antlr.v4.codegen.model.chunk.ThisRulePropertyRef_text; +import org.antlr.v4.codegen.model.chunk.TokenPropertyRef; +import org.antlr.v4.codegen.model.chunk.TokenPropertyRef_channel; +import org.antlr.v4.codegen.model.chunk.TokenPropertyRef_index; +import org.antlr.v4.codegen.model.chunk.TokenPropertyRef_int; +import org.antlr.v4.codegen.model.chunk.TokenPropertyRef_line; +import org.antlr.v4.codegen.model.chunk.TokenPropertyRef_pos; +import org.antlr.v4.codegen.model.chunk.TokenPropertyRef_text; +import org.antlr.v4.codegen.model.chunk.TokenPropertyRef_type; +import org.antlr.v4.codegen.model.chunk.TokenRef; +import org.antlr.v4.codegen.model.decl.StructDecl; +import org.antlr.v4.parse.ActionSplitter; +import org.antlr.v4.parse.ActionSplitterListener; +import org.antlr.v4.tool.Attribute; +import org.antlr.v4.tool.ErrorType; +import org.antlr.v4.tool.Grammar; +import org.antlr.v4.tool.Rule; +import org.antlr.v4.tool.ast.ActionAST; + +import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** */ +public class ActionTranslator implements ActionSplitterListener { + public static final Map<String, Class<? extends RulePropertyRef>> thisRulePropToModelMap = + new HashMap<String, Class<? extends RulePropertyRef>>(); + static { + thisRulePropToModelMap.put("start", ThisRulePropertyRef_start.class); + thisRulePropToModelMap.put("stop", ThisRulePropertyRef_stop.class); + thisRulePropToModelMap.put("text", ThisRulePropertyRef_text.class); + thisRulePropToModelMap.put("ctx", ThisRulePropertyRef_ctx.class); + thisRulePropToModelMap.put("parser", ThisRulePropertyRef_parser.class); + } + + public static final Map<String, Class<? extends RulePropertyRef>> rulePropToModelMap = + new HashMap<String, Class<? extends RulePropertyRef>>(); + static { + rulePropToModelMap.put("start", RulePropertyRef_start.class); + rulePropToModelMap.put("stop", RulePropertyRef_stop.class); + rulePropToModelMap.put("text", RulePropertyRef_text.class); + rulePropToModelMap.put("ctx", RulePropertyRef_ctx.class); + rulePropToModelMap.put("parser", RulePropertyRef_parser.class); + } + + public static final Map<String, Class<? extends TokenPropertyRef>> tokenPropToModelMap = + new HashMap<String, Class<? extends TokenPropertyRef>>(); + static { + tokenPropToModelMap.put("text", TokenPropertyRef_text.class); + tokenPropToModelMap.put("type", TokenPropertyRef_type.class); + tokenPropToModelMap.put("line", TokenPropertyRef_line.class); + tokenPropToModelMap.put("index", TokenPropertyRef_index.class); + tokenPropToModelMap.put("pos", TokenPropertyRef_pos.class); + tokenPropToModelMap.put("channel", TokenPropertyRef_channel.class); + tokenPropToModelMap.put("int", TokenPropertyRef_int.class); + } + + CodeGenerator gen; + ActionAST node; + RuleFunction rf; + List<ActionChunk> chunks = new ArrayList<ActionChunk>(); + OutputModelFactory factory; + StructDecl nodeContext; + + public ActionTranslator(OutputModelFactory factory, ActionAST node) { + this.factory = factory; + this.node = node; + this.gen = factory.getGenerator(); + } + + public static String toString(List<ActionChunk> chunks) { + StringBuilder buf = new StringBuilder(); + for (ActionChunk c : chunks) buf.append(c.toString()); + return buf.toString(); + } + + public static List<ActionChunk> translateAction(OutputModelFactory factory, + RuleFunction rf, + Token tokenWithinAction, + ActionAST node) + { + String action = tokenWithinAction.getText(); + if ( action.charAt(0)=='{' ) { + int firstCurly = action.indexOf('{'); + int lastCurly = action.lastIndexOf('}'); + if ( firstCurly>=0 && lastCurly>=0 ) { + action = action.substring(firstCurly+1, lastCurly); // trim {...} + } + } + return translateActionChunk(factory, rf, action, node); + } + + public static List<ActionChunk> translateActionChunk(OutputModelFactory factory, + RuleFunction rf, + String action, + ActionAST node) + { + Token tokenWithinAction = node.token; + ActionTranslator translator = new ActionTranslator(factory, node); + translator.rf = rf; + factory.getGrammar().tool.log("action-translator", "translate " + action); + String altLabel = node.getAltLabel(); + if ( rf!=null ) translator.nodeContext = rf.ruleCtx; + if ( altLabel!=null ) translator.nodeContext = rf.altLabelCtxs.get(altLabel); + ANTLRStringStream in = new ANTLRStringStream(action); + in.setLine(tokenWithinAction.getLine()); + in.setCharPositionInLine(tokenWithinAction.getCharPositionInLine()); + ActionSplitter trigger = new ActionSplitter(in, translator); + // forces eval, triggers listener methods + trigger.getActionTokens(); + return translator.chunks; + } + + @Override + public void attr(String expr, Token x) { + gen.g.tool.log("action-translator", "attr "+x); + Attribute a = node.resolver.resolveToAttribute(x.getText(), node); + if ( a!=null ) { + switch ( a.dict.type ) { + case ARG: chunks.add(new ArgRef(nodeContext,x.getText())); break; + case RET: chunks.add(new RetValueRef(rf.ruleCtx, x.getText())); break; + case LOCAL: chunks.add(new LocalRef(nodeContext,x.getText())); break; + case PREDEFINED_RULE: chunks.add(getRulePropertyRef(x)); break; + } + } + if ( node.resolver.resolvesToToken(x.getText(), node) ) { + chunks.add(new TokenRef(nodeContext,getTokenLabel(x.getText()))); // $label + return; + } + if ( node.resolver.resolvesToLabel(x.getText(), node) ) { + chunks.add(new LabelRef(nodeContext,getTokenLabel(x.getText()))); // $x for x=ID etc... + return; + } + if ( node.resolver.resolvesToListLabel(x.getText(), node) ) { + chunks.add(new ListLabelRef(nodeContext,x.getText())); // $ids for ids+=ID etc... + return; + } + Rule r = factory.getGrammar().getRule(x.getText()); + if ( r!=null ) { + chunks.add(new LabelRef(nodeContext,getRuleLabel(x.getText()))); // $r for r rule ref + } + } + + @Override + public void qualifiedAttr(String expr, Token x, Token y) { + gen.g.tool.log("action-translator", "qattr "+x+"."+y); + if ( node.resolver.resolveToAttribute(x.getText(), node)!=null ) { + // must be a member access to a predefined attribute like $ctx.foo + attr(expr, x); + chunks.add(new ActionText(nodeContext, "."+y.getText())); + return; + } + Attribute a = node.resolver.resolveToAttribute(x.getText(), y.getText(), node); + switch ( a.dict.type ) { + case ARG: chunks.add(new ArgRef(nodeContext,y.getText())); break; // has to be current rule + case RET: + if ( factory.getCurrentRuleFunction()!=null && + factory.getCurrentRuleFunction().name.equals(x.getText()) ) + { + chunks.add(new RetValueRef(rf.ruleCtx, y.getText())); break; + } + else { + chunks.add(new QRetValueRef(nodeContext, getRuleLabel(x.getText()), y.getText())); break; + } + case PREDEFINED_RULE: + if ( factory.getCurrentRuleFunction()!=null && + factory.getCurrentRuleFunction().name.equals(x.getText()) ) + { + chunks.add(getRulePropertyRef(y)); + } + else { + chunks.add(getRulePropertyRef(x, y)); + } + break; + case TOKEN: + chunks.add(getTokenPropertyRef(x, y)); + break; + } + } + + @Override + public void setAttr(String expr, Token x, Token rhs) { + gen.g.tool.log("action-translator", "setAttr "+x+" "+rhs); + List<ActionChunk> rhsChunks = translateActionChunk(factory,rf,rhs.getText(),node); + SetAttr s = new SetAttr(nodeContext, x.getText(), rhsChunks); + chunks.add(s); + } + + @Override + public void nonLocalAttr(String expr, Token x, Token y) { + gen.g.tool.log("action-translator", "nonLocalAttr "+x+"::"+y); + Rule r = factory.getGrammar().getRule(x.getText()); + chunks.add(new NonLocalAttrRef(nodeContext, x.getText(), y.getText(), r.index)); + } + + @Override + public void setNonLocalAttr(String expr, Token x, Token y, Token rhs) { + gen.g.tool.log("action-translator", "setNonLocalAttr "+x+"::"+y+"="+rhs); + Rule r = factory.getGrammar().getRule(x.getText()); + List<ActionChunk> rhsChunks = translateActionChunk(factory,rf,rhs.getText(),node); + SetNonLocalAttr s = new SetNonLocalAttr(nodeContext, x.getText(), y.getText(), r.index, rhsChunks); + chunks.add(s); + } + + @Override + public void text(String text) { + chunks.add(new ActionText(nodeContext,text)); + } + + TokenPropertyRef getTokenPropertyRef(Token x, Token y) { + try { + Class<? extends TokenPropertyRef> c = tokenPropToModelMap.get(y.getText()); + Constructor<? extends TokenPropertyRef> ctor = c.getConstructor(StructDecl.class, String.class); + TokenPropertyRef ref = + ctor.newInstance(nodeContext, getTokenLabel(x.getText())); + return ref; + } + catch (Exception e) { + factory.getGrammar().tool.errMgr.toolError(ErrorType.INTERNAL_ERROR, e); + } + return null; + } + + // $text + RulePropertyRef getRulePropertyRef(Token prop) { + try { + Class<? extends RulePropertyRef> c = thisRulePropToModelMap.get(prop.getText()); + Constructor<? extends RulePropertyRef> ctor = c.getConstructor(StructDecl.class, String.class); + RulePropertyRef ref = + ctor.newInstance(nodeContext, getRuleLabel(prop.getText())); + return ref; + } + catch (Exception e) { + factory.getGrammar().tool.errMgr.toolError(ErrorType.INTERNAL_ERROR, e); + } + return null; + } + + RulePropertyRef getRulePropertyRef(Token x, Token prop) { + Grammar g = factory.getGrammar(); + try { + Class<? extends RulePropertyRef> c = rulePropToModelMap.get(prop.getText()); + Constructor<? extends RulePropertyRef> ctor = c.getConstructor(StructDecl.class, String.class); + RulePropertyRef ref = + ctor.newInstance(nodeContext, getRuleLabel(x.getText())); + return ref; + } + catch (Exception e) { + g.tool.errMgr.toolError(ErrorType.INTERNAL_ERROR, e, prop.getText()); + } + return null; + } + + public String getTokenLabel(String x) { + if ( node.resolver.resolvesToLabel(x, node) ) return x; + return factory.getGenerator().getTarget().getImplicitTokenLabel(x); + } + + public String getRuleLabel(String x) { + if ( node.resolver.resolvesToLabel(x, node) ) return x; + return factory.getGenerator().getTarget().getImplicitRuleLabel(x); + } + +} diff --git a/tool/src/org/antlr/v4/codegen/BlankOutputModelFactory.java b/tool/src/org/antlr/v4/codegen/BlankOutputModelFactory.java new file mode 100644 index 0000000..bef03ac --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/BlankOutputModelFactory.java @@ -0,0 +1,130 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen; + +import org.antlr.v4.codegen.model.Choice; +import org.antlr.v4.codegen.model.CodeBlockForAlt; +import org.antlr.v4.codegen.model.LabeledOp; +import org.antlr.v4.codegen.model.Lexer; +import org.antlr.v4.codegen.model.LexerFile; +import org.antlr.v4.codegen.model.Parser; +import org.antlr.v4.codegen.model.ParserFile; +import org.antlr.v4.codegen.model.RuleFunction; +import org.antlr.v4.codegen.model.SrcOp; +import org.antlr.v4.runtime.misc.IntervalSet; +import org.antlr.v4.tool.Alternative; +import org.antlr.v4.tool.Rule; +import org.antlr.v4.tool.ast.ActionAST; +import org.antlr.v4.tool.ast.BlockAST; +import org.antlr.v4.tool.ast.GrammarAST; + +import java.util.List; + +public abstract class BlankOutputModelFactory implements OutputModelFactory { + @Override + public ParserFile parserFile(String fileName) { return null; } + + @Override + public Parser parser(ParserFile file) { return null; } + + @Override + public RuleFunction rule(Rule r) { return null; } + + @Override + public List<SrcOp> rulePostamble(RuleFunction function, Rule r) { return null; } + + @Override + public LexerFile lexerFile(String fileName) { return null; } + + @Override + public Lexer lexer(LexerFile file) { return null; } + + // ALTERNATIVES / ELEMENTS + + @Override + public CodeBlockForAlt alternative(Alternative alt, boolean outerMost) { return null; } + + @Override + public CodeBlockForAlt finishAlternative(CodeBlockForAlt blk, List<SrcOp> ops) { return blk; } + + @Override + public CodeBlockForAlt epsilon(Alternative alt, boolean outerMost) { return null; } + + @Override + public List<SrcOp> ruleRef(GrammarAST ID, GrammarAST label, GrammarAST args) { return null; } + + @Override + public List<SrcOp> tokenRef(GrammarAST ID, GrammarAST label, GrammarAST args) { return null; } + + @Override + public List<SrcOp> stringRef(GrammarAST ID, GrammarAST label) { return tokenRef(ID, label, null); } + + @Override + public List<SrcOp> set(GrammarAST setAST, GrammarAST label, boolean invert) { return null; } + + @Override + public List<SrcOp> wildcard(GrammarAST ast, GrammarAST labelAST) { return null; } + + // ACTIONS + + @Override + public List<SrcOp> action(ActionAST ast) { return null; } + + @Override + public List<SrcOp> sempred(ActionAST ast) { return null; } + + // BLOCKS + + @Override + public Choice getChoiceBlock(BlockAST blkAST, List<CodeBlockForAlt> alts, GrammarAST label) { return null; } + + @Override + public Choice getEBNFBlock(GrammarAST ebnfRoot, List<CodeBlockForAlt> alts) { return null; } + + @Override + public Choice getLL1ChoiceBlock(BlockAST blkAST, List<CodeBlockForAlt> alts) { return null; } + + @Override + public Choice getComplexChoiceBlock(BlockAST blkAST, List<CodeBlockForAlt> alts) { return null; } + + @Override + public Choice getLL1EBNFBlock(GrammarAST ebnfRoot, List<CodeBlockForAlt> alts) { return null; } + + @Override + public Choice getComplexEBNFBlock(GrammarAST ebnfRoot, List<CodeBlockForAlt> alts) { return null; } + + @Override + public List<SrcOp> getLL1Test(IntervalSet look, GrammarAST blkAST) { return null; } + + @Override + public boolean needsImplicitLabel(GrammarAST ID, LabeledOp op) { return false; } +} + diff --git a/tool/src/org/antlr/v4/codegen/CodeGenPipeline.java b/tool/src/org/antlr/v4/codegen/CodeGenPipeline.java new file mode 100644 index 0000000..85b779d --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/CodeGenPipeline.java @@ -0,0 +1,127 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen; + +import org.antlr.v4.parse.ANTLRParser; +import org.antlr.v4.runtime.misc.IntervalSet; +import org.antlr.v4.tool.ErrorType; +import org.antlr.v4.tool.Grammar; +import org.antlr.v4.tool.ast.GrammarAST; +import org.stringtemplate.v4.ST; +import org.stringtemplate.v4.gui.STViz; + +import java.util.List; + +public class CodeGenPipeline { + Grammar g; + + public CodeGenPipeline(Grammar g) { + this.g = g; + } + + public void process() { + if ( !CodeGenerator.targetExists(g.getOptionString("language")) ) return; + + CodeGenerator gen = new CodeGenerator(g); + IntervalSet idTypes = new IntervalSet(); + idTypes.add(ANTLRParser.ID); + idTypes.add(ANTLRParser.RULE_REF); + idTypes.add(ANTLRParser.TOKEN_REF); + List<GrammarAST> idNodes = g.ast.getNodesWithType(idTypes); + for (GrammarAST idNode : idNodes) { + if ( gen.getTarget().grammarSymbolCausesIssueInGeneratedCode(idNode) ) { + g.tool.errMgr.grammarError(ErrorType.USE_OF_BAD_WORD, + g.fileName, idNode.getToken(), + idNode.getText()); + } + } + + // all templates are generated in memory to report the most complete + // error information possible, but actually writing output files stops + // after the first error is reported + int errorCount = g.tool.errMgr.getNumErrors(); + + if ( g.isLexer() ) { + ST lexer = gen.generateLexer(); + if (g.tool.errMgr.getNumErrors() == errorCount) { + writeRecognizer(lexer, gen); + } + } + else { + ST parser = gen.generateParser(); + if (g.tool.errMgr.getNumErrors() == errorCount) { + writeRecognizer(parser, gen); + } + if ( g.tool.gen_listener ) { + ST listener = gen.generateListener(); + if (g.tool.errMgr.getNumErrors() == errorCount) { + gen.writeListener(listener); + } + if (gen.getTarget().wantsBaseListener()) { + ST baseListener = gen.generateBaseListener(); + if (g.tool.errMgr.getNumErrors() == errorCount) { + gen.writeBaseListener(baseListener); + } + } + } + if ( g.tool.gen_visitor ) { + ST visitor = gen.generateVisitor(); + if (g.tool.errMgr.getNumErrors() == errorCount) { + gen.writeVisitor(visitor); + } + if (gen.getTarget().wantsBaseVisitor()) { + ST baseVisitor = gen.generateBaseVisitor(); + if (g.tool.errMgr.getNumErrors() == errorCount) { + gen.writeBaseVisitor(baseVisitor); + } + } + } + gen.writeHeaderFile(); + } + gen.writeVocabFile(); + } + + protected void writeRecognizer(ST template, CodeGenerator gen) { + if ( g.tool.launch_ST_inspector ) { + STViz viz = template.inspect(); + if (g.tool.ST_inspector_wait_for_close) { + try { + viz.waitForClose(); + } + catch (InterruptedException ex) { + g.tool.errMgr.toolError(ErrorType.INTERNAL_ERROR, ex); + } + } + } + + gen.writeRecognizer(template); + } +} diff --git a/tool/src/org/antlr/v4/codegen/CodeGenerator.java b/tool/src/org/antlr/v4/codegen/CodeGenerator.java new file mode 100644 index 0000000..e75a4f1 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/CodeGenerator.java @@ -0,0 +1,302 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen; + +import org.antlr.v4.Tool; +import org.antlr.v4.codegen.model.OutputModelObject; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.tool.ErrorType; +import org.antlr.v4.tool.Grammar; +import org.stringtemplate.v4.AutoIndentWriter; +import org.stringtemplate.v4.ST; +import org.stringtemplate.v4.STGroup; +import org.stringtemplate.v4.STWriter; + +import java.io.IOException; +import java.io.Writer; +import java.lang.reflect.Constructor; +import java.util.LinkedHashMap; +import java.util.Map; + +/** General controller for code gen. Can instantiate sub generator(s). + */ +public class CodeGenerator { + public static final String TEMPLATE_ROOT = "org/antlr/v4/tool/templates/codegen"; + public static final String VOCAB_FILE_EXTENSION = ".tokens"; + public static final String DEFAULT_LANGUAGE = "Java"; + public static final String vocabFilePattern = + "<tokens.keys:{t | <t>=<tokens.(t)>\n}>" + + "<literals.keys:{t | <t>=<literals.(t)>\n}>"; + + + public final Grammar g; + + public final Tool tool; + + public final String language; + + private Target target; + + public int lineWidth = 72; + + private CodeGenerator(String language) { + this.g = null; + this.tool = null; + this.language = language; + } + + public CodeGenerator(Grammar g) { + this(g.tool, g, g.getOptionString("language")); + } + + public CodeGenerator(Tool tool, Grammar g, String language) { + this.g = g; + this.tool = tool; + this.language = language != null ? language : DEFAULT_LANGUAGE; + } + + public static boolean targetExists(String language) { + String targetName = "org.antlr.v4.codegen.target."+language+"Target"; + try { + Class<? extends Target> c = Class.forName(targetName).asSubclass(Target.class); + Constructor<? extends Target> ctor = c.getConstructor(CodeGenerator.class); + CodeGenerator gen = new CodeGenerator(language); + Target target = ctor.newInstance(gen); + return target.templatesExist(); + } + catch (Exception e) { // ignore errors; we're detecting presence only + } + return false; + } + + + public Target getTarget() { + if ( target == null && targetExists(language) ) { + loadLanguageTarget(language); + } + return target; + } + + + public STGroup getTemplates() { + Target t = getTarget(); + return t==null ? null : t.getTemplates(); + } + + protected void loadLanguageTarget(String language) { + String targetName = "org.antlr.v4.codegen.target."+language+"Target"; + try { + Class<? extends Target> c = Class.forName(targetName).asSubclass(Target.class); + Constructor<? extends Target> ctor = c.getConstructor(CodeGenerator.class); + target = ctor.newInstance(this); + } + catch (Exception e) { + tool.errMgr.toolError(ErrorType.CANNOT_CREATE_TARGET_GENERATOR, + e, + targetName); + } + } + + // CREATE TEMPLATES BY WALKING MODEL + + private OutputModelController createController() { + OutputModelFactory factory = new ParserFactory(this); + OutputModelController controller = new OutputModelController(factory); + factory.setController(controller); + return controller; + } + + private ST walk(OutputModelObject outputModel) { + OutputModelWalker walker = new OutputModelWalker(tool, getTemplates()); + return walker.walk(outputModel); + } + + public ST generateLexer() { return walk(createController().buildLexerOutputModel()); } + public ST generateParser() { return walk(createController().buildParserOutputModel()); } + public ST generateListener() { return walk(createController().buildListenerOutputModel()); } + public ST generateBaseListener() { return walk(createController().buildBaseListenerOutputModel()); } + public ST generateVisitor() { return walk(createController().buildVisitorOutputModel()); } + public ST generateBaseVisitor() { return walk(createController().buildBaseVisitorOutputModel()); } + + /** Generate a token vocab file with all the token names/types. For example: + * ID=7 + * FOR=8 + * 'for'=8 + * + * This is independent of the target language; used by antlr internally + */ + ST getTokenVocabOutput() { + ST vocabFileST = new ST(vocabFilePattern); + Map<String,Integer> tokens = new LinkedHashMap<String,Integer>(); + // make constants for the token names + for (String t : g.tokenNameToTypeMap.keySet()) { + int tokenType = g.tokenNameToTypeMap.get(t); + if ( tokenType>=Token.MIN_USER_TOKEN_TYPE) { + tokens.put(t, tokenType); + } + } + vocabFileST.add("tokens", tokens); + + // now dump the strings + Map<String,Integer> literals = new LinkedHashMap<String,Integer>(); + for (String literal : g.stringLiteralToTypeMap.keySet()) { + int tokenType = g.stringLiteralToTypeMap.get(literal); + if ( tokenType>=Token.MIN_USER_TOKEN_TYPE) { + literals.put(literal, tokenType); + } + } + vocabFileST.add("literals", literals); + + return vocabFileST; + } + + public void writeRecognizer(ST outputFileST) { + getTarget().genFile(g, outputFileST, getRecognizerFileName()); + } + + public void writeListener(ST outputFileST) { + getTarget().genFile(g, outputFileST, getListenerFileName()); + } + + public void writeBaseListener(ST outputFileST) { + getTarget().genFile(g, outputFileST, getBaseListenerFileName()); + } + + public void writeVisitor(ST outputFileST) { + getTarget().genFile(g, outputFileST, getVisitorFileName()); + } + + public void writeBaseVisitor(ST outputFileST) { + getTarget().genFile(g, outputFileST, getBaseVisitorFileName()); + } + + public void writeHeaderFile() { + String fileName = getHeaderFileName(); + if ( fileName==null ) return; + if ( getTemplates().isDefined("headerFile") ) { + ST extST = getTemplates().getInstanceOf("headerFileExtension"); + ST headerFileST = null; + // TODO: don't hide this header file generation here! + getTarget().genRecognizerHeaderFile(g, headerFileST, extST.render(lineWidth)); + } + } + + 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 ) { + getTarget().genFile(g, tokenVocabSerialization, 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(); + } + catch (IOException ioe) { + tool.errMgr.toolError(ErrorType.CANNOT_WRITE_FILE, + ioe, + fileName); + } + } + + /** Generate TParser.java and TLexer.java from T.g4 if combined, else + * just use T.java as output regardless of type. + */ + public String getRecognizerFileName() { + ST extST = getTemplates().getInstanceOf("codeFileExtension"); + String recognizerName = g.getRecognizerName(); + 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() { + assert g.name != null; + ST extST = getTemplates().getInstanceOf("codeFileExtension"); + String listenerName = g.name + "Listener"; + return listenerName+extST.render(); + } + + /** A given grammar T, return the visitor name such as + * TVisitor.java, if we're using the Java target. + */ + public String getVisitorFileName() { + assert g.name != null; + ST extST = getTemplates().getInstanceOf("codeFileExtension"); + String listenerName = g.name + "Visitor"; + return listenerName+extST.render(); + } + + /** A given grammar T, return a blank listener implementation + * such as TBaseListener.java, if we're using the Java target. + */ + public String getBaseListenerFileName() { + assert g.name != null; + ST extST = getTemplates().getInstanceOf("codeFileExtension"); + String listenerName = g.name + "BaseListener"; + return listenerName+extST.render(); + } + + /** A given grammar T, return a blank listener implementation + * such as TBaseListener.java, if we're using the Java target. + */ + public String getBaseVisitorFileName() { + assert g.name != null; + ST extST = getTemplates().getInstanceOf("codeFileExtension"); + String listenerName = g.name + "BaseVisitor"; + 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. + */ + public String getVocabFileName() { + return g.name+VOCAB_FILE_EXTENSION; + } + + public String getHeaderFileName() { + ST extST = getTemplates().getInstanceOf("headerFileExtension"); + if ( extST==null ) return null; + String recognizerName = g.getRecognizerName(); + return recognizerName+extST.render(); + } + +} diff --git a/tool/src/org/antlr/v4/codegen/CodeGeneratorExtension.java b/tool/src/org/antlr/v4/codegen/CodeGeneratorExtension.java new file mode 100644 index 0000000..e07180e --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/CodeGeneratorExtension.java @@ -0,0 +1,96 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen; + + +import org.antlr.v4.codegen.model.Choice; +import org.antlr.v4.codegen.model.CodeBlockForAlt; +import org.antlr.v4.codegen.model.LabeledOp; +import org.antlr.v4.codegen.model.Lexer; +import org.antlr.v4.codegen.model.LexerFile; +import org.antlr.v4.codegen.model.Parser; +import org.antlr.v4.codegen.model.ParserFile; +import org.antlr.v4.codegen.model.RuleFunction; +import org.antlr.v4.codegen.model.SrcOp; +import org.antlr.v4.tool.ast.GrammarAST; + +import java.util.List; + +/** Filter list of SrcOps and return; default is pass-through filter */ +public class CodeGeneratorExtension { + public OutputModelFactory factory; + + public CodeGeneratorExtension(OutputModelFactory factory) { + this.factory = factory; + } + + public ParserFile parserFile(ParserFile f) { return f; } + + public Parser parser(Parser p) { return p; } + + public LexerFile lexerFile(LexerFile f) { return f; } + + public Lexer lexer(Lexer l) { return l; } + + public RuleFunction rule(RuleFunction rf) { return rf; } + + public List<SrcOp> rulePostamble(List<SrcOp> ops) { return ops; } + + public CodeBlockForAlt alternative(CodeBlockForAlt blk, boolean outerMost) { return blk; } + + public CodeBlockForAlt finishAlternative(CodeBlockForAlt blk, boolean outerMost) { return blk; } + + public CodeBlockForAlt epsilon(CodeBlockForAlt blk) { return blk; } + + public List<SrcOp> ruleRef(List<SrcOp> ops) { return ops; } + + public List<SrcOp> tokenRef(List<SrcOp> ops) { return ops; } + + public List<SrcOp> set(List<SrcOp> ops) { return ops; } + + public List<SrcOp> stringRef(List<SrcOp> ops) { return ops; } + + public List<SrcOp> wildcard(List<SrcOp> ops) { return ops; } + + // ACTIONS + + public List<SrcOp> action(List<SrcOp> ops) { return ops; } + + public List<SrcOp> sempred(List<SrcOp> ops) { return ops; } + + // BLOCKS + + public Choice getChoiceBlock(Choice c) { return c; } + + public Choice getEBNFBlock(Choice c) { return c; } + + public boolean needsImplicitLabel(GrammarAST ID, LabeledOp op) { return false; } +} diff --git a/tool/src/org/antlr/v4/codegen/DefaultOutputModelFactory.java b/tool/src/org/antlr/v4/codegen/DefaultOutputModelFactory.java new file mode 100644 index 0000000..901e439 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/DefaultOutputModelFactory.java @@ -0,0 +1,127 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen; + +import org.antlr.v4.codegen.model.CodeBlockForOuterMostAlt; +import org.antlr.v4.codegen.model.OutputModelObject; +import org.antlr.v4.codegen.model.RuleFunction; +import org.antlr.v4.codegen.model.SrcOp; +import org.antlr.v4.codegen.model.decl.CodeBlock; +import org.antlr.v4.codegen.model.decl.Decl; +import org.antlr.v4.tool.Alternative; +import org.antlr.v4.tool.Grammar; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +/** Create output objects for elements *within* rule functions except + * buildOutputModel() which builds outer/root model object and any + * objects such as RuleFunction that surround elements in rule + * functions. + */ +public abstract class DefaultOutputModelFactory extends BlankOutputModelFactory { + // Interface to outside world + + public final Grammar g; + + public final CodeGenerator gen; + + public OutputModelController controller; + + protected DefaultOutputModelFactory(CodeGenerator gen) { + this.gen = gen; + this.g = gen.g; + } + + @Override + public void setController(OutputModelController controller) { + this.controller = controller; + } + + @Override + public OutputModelController getController() { + return controller; + } + + // Convenience methods + + + @Override + public Grammar getGrammar() { return g; } + + @Override + public CodeGenerator getGenerator() { return gen; } + + @Override + public OutputModelObject getRoot() { return controller.getRoot(); } + + @Override + public RuleFunction getCurrentRuleFunction() { return controller.getCurrentRuleFunction(); } + + @Override + public Alternative getCurrentOuterMostAlt() { return controller.getCurrentOuterMostAlt(); } + + @Override + public CodeBlock getCurrentBlock() { return controller.getCurrentBlock(); } + + @Override + public CodeBlockForOuterMostAlt getCurrentOuterMostAlternativeBlock() { return controller.getCurrentOuterMostAlternativeBlock(); } + + @Override + public int getCodeBlockLevel() { return controller.codeBlockLevel; } + + @Override + public int getTreeLevel() { return controller.treeLevel; } + + // MISC + + + public static List<SrcOp> list(SrcOp... values) { + return new ArrayList<SrcOp>(Arrays.asList(values)); + } + + + public static List<SrcOp> list(Collection<? extends SrcOp> values) { + return new ArrayList<SrcOp>(values); + } + + + public Decl getCurrentDeclForName(String name) { + if ( getCurrentBlock().locals==null ) return null; + for (Decl d : getCurrentBlock().locals.elements()) { + if ( d.name.equals(name) ) return d; + } + return null; + } + +} diff --git a/tool/src/org/antlr/v4/codegen/LexerFactory.java b/tool/src/org/antlr/v4/codegen/LexerFactory.java new file mode 100644 index 0000000..add1c8c --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/LexerFactory.java @@ -0,0 +1,36 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen; + +/** */ +public class LexerFactory extends DefaultOutputModelFactory { + public LexerFactory(CodeGenerator gen) { super(gen); } +} diff --git a/tool/src/org/antlr/v4/codegen/OutputModelController.java b/tool/src/org/antlr/v4/codegen/OutputModelController.java new file mode 100644 index 0000000..6b0d9db --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/OutputModelController.java @@ -0,0 +1,500 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen; + +import org.antlr.runtime.tree.CommonTreeNodeStream; +import org.antlr.v4.analysis.LeftRecursiveRuleAltInfo; +import org.antlr.v4.codegen.model.Action; +import org.antlr.v4.codegen.model.AltBlock; +import org.antlr.v4.codegen.model.BaseListenerFile; +import org.antlr.v4.codegen.model.BaseVisitorFile; +import org.antlr.v4.codegen.model.Choice; +import org.antlr.v4.codegen.model.CodeBlockForAlt; +import org.antlr.v4.codegen.model.CodeBlockForOuterMostAlt; +import org.antlr.v4.codegen.model.LabeledOp; +import org.antlr.v4.codegen.model.LeftRecursiveRuleFunction; +import org.antlr.v4.codegen.model.Lexer; +import org.antlr.v4.codegen.model.LexerFile; +import org.antlr.v4.codegen.model.ListenerFile; +import org.antlr.v4.codegen.model.OutputModelObject; +import org.antlr.v4.codegen.model.Parser; +import org.antlr.v4.codegen.model.ParserFile; +import org.antlr.v4.codegen.model.RuleActionFunction; +import org.antlr.v4.codegen.model.RuleFunction; +import org.antlr.v4.codegen.model.RuleSempredFunction; +import org.antlr.v4.codegen.model.SrcOp; +import org.antlr.v4.codegen.model.StarBlock; +import org.antlr.v4.codegen.model.VisitorFile; +import org.antlr.v4.codegen.model.decl.CodeBlock; +import org.antlr.v4.misc.Utils; +import org.antlr.v4.parse.ANTLRParser; +import org.antlr.v4.parse.GrammarASTAdaptor; +import org.antlr.v4.tool.Alternative; +import org.antlr.v4.tool.ErrorType; +import org.antlr.v4.tool.Grammar; +import org.antlr.v4.tool.LeftRecursiveRule; +import org.antlr.v4.tool.Rule; +import org.antlr.v4.tool.ast.ActionAST; +import org.antlr.v4.tool.ast.BlockAST; +import org.antlr.v4.tool.ast.GrammarAST; +import org.antlr.v4.tool.ast.PredAST; +import org.stringtemplate.v4.ST; +import org.stringtemplate.v4.STGroup; + +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. + **/ +public class OutputModelController { + /** Who does the work? Doesn't have to be CoreOutputModelFactory. */ + public OutputModelFactory delegate; + + /** Post-processing CodeGeneratorExtension objects; done in order given. */ + public List<CodeGeneratorExtension> extensions = new ArrayList<CodeGeneratorExtension>(); + + /** While walking code in rules, this is set to the tree walker that + * triggers actions. + */ + public SourceGenTriggers walker; + + /** Context set by the SourceGenTriggers.g */ + public int codeBlockLevel = -1; + public int treeLevel = -1; + public OutputModelObject root; // normally ParserFile, LexerFile, ... + public Stack<RuleFunction> currentRule = new Stack<RuleFunction>(); + public Alternative currentOuterMostAlt; + public CodeBlock currentBlock; + public CodeBlockForOuterMostAlt currentOuterMostAlternativeBlock; + + public OutputModelController(OutputModelFactory factory) { + this.delegate = factory; + } + + public void addExtension(CodeGeneratorExtension ext) { extensions.add(ext); } + + /** Build a file with a parser containing rule functions. Use the + * controller as factory in SourceGenTriggers so it triggers codegen + * extensions too, not just the factory functions in this factory. + */ + public OutputModelObject buildParserOutputModel() { + Grammar g = delegate.getGrammar(); + CodeGenerator gen = delegate.getGenerator(); + ParserFile file = parserFile(gen.getRecognizerFileName()); + setRoot(file); + Parser parser = parser(file); + file.parser = parser; + + for (Rule r : g.rules.values()) { + buildRuleFunction(parser, r); + } + + return file; + } + + public OutputModelObject buildLexerOutputModel() { + CodeGenerator gen = delegate.getGenerator(); + LexerFile file = lexerFile(gen.getRecognizerFileName()); + setRoot(file); + file.lexer = lexer(file); + + Grammar g = delegate.getGrammar(); + for (Rule r : g.rules.values()) { + buildLexerRuleActions(file.lexer, r); + } + + return file; + } + + public OutputModelObject buildListenerOutputModel() { + CodeGenerator gen = delegate.getGenerator(); + return new ListenerFile(delegate, gen.getListenerFileName()); + } + + public OutputModelObject buildBaseListenerOutputModel() { + CodeGenerator gen = delegate.getGenerator(); + return new BaseListenerFile(delegate, gen.getBaseListenerFileName()); + } + + public OutputModelObject buildVisitorOutputModel() { + CodeGenerator gen = delegate.getGenerator(); + return new VisitorFile(delegate, gen.getVisitorFileName()); + } + + public OutputModelObject buildBaseVisitorOutputModel() { + CodeGenerator gen = delegate.getGenerator(); + return new BaseVisitorFile(delegate, gen.getBaseVisitorFileName()); + } + + public ParserFile parserFile(String fileName) { + ParserFile f = delegate.parserFile(fileName); + for (CodeGeneratorExtension ext : extensions) f = ext.parserFile(f); + return f; + } + + public Parser parser(ParserFile file) { + Parser p = delegate.parser(file); + for (CodeGeneratorExtension ext : extensions) p = ext.parser(p); + return p; + } + + public LexerFile lexerFile(String fileName) { + return new LexerFile(delegate, fileName); + } + + public Lexer lexer(LexerFile file) { + return new Lexer(delegate, file); + } + + /** Create RuleFunction per rule and update sempreds,actions of parser + * output object with stuff found in r. + */ + public void buildRuleFunction(Parser parser, Rule r) { + RuleFunction function = rule(r); + parser.funcs.add(function); + pushCurrentRule(function); + function.fillNamedActions(delegate, r); + + if ( r instanceof LeftRecursiveRule ) { + buildLeftRecursiveRuleFunction((LeftRecursiveRule)r, + (LeftRecursiveRuleFunction)function); + } + else { + buildNormalRuleFunction(r, function); + } + + Grammar g = getGrammar(); + for (ActionAST a : r.actions) { + if ( a instanceof PredAST ) { + PredAST p = (PredAST)a; + RuleSempredFunction rsf = parser.sempredFuncs.get(r); + if ( rsf==null ) { + rsf = new RuleSempredFunction(delegate, r, function.ctxType); + parser.sempredFuncs.put(r, rsf); + } + rsf.actions.put(g.sempreds.get(p), new Action(delegate, p)); + } + } + + popCurrentRule(); + } + + public void buildLeftRecursiveRuleFunction(LeftRecursiveRule r, LeftRecursiveRuleFunction function) { + buildNormalRuleFunction(r, function); + + // now inject code to start alts + CodeGenerator gen = delegate.getGenerator(); + STGroup codegenTemplates = gen.getTemplates(); + + // pick out alt(s) for primaries + CodeBlockForOuterMostAlt outerAlt = (CodeBlockForOuterMostAlt)function.code.get(0); + List<CodeBlockForAlt> primaryAltsCode = new ArrayList<CodeBlockForAlt>(); + SrcOp primaryStuff = outerAlt.ops.get(0); + if ( primaryStuff instanceof Choice ) { + Choice primaryAltBlock = (Choice) primaryStuff; + primaryAltsCode.addAll(primaryAltBlock.alts); + } + else { // just a single alt I guess; no block + primaryAltsCode.add((CodeBlockForAlt)primaryStuff); + } + + // pick out alt(s) for op alts + StarBlock opAltStarBlock = (StarBlock)outerAlt.ops.get(1); + CodeBlockForAlt altForOpAltBlock = opAltStarBlock.alts.get(0); + List<CodeBlockForAlt> opAltsCode = new ArrayList<CodeBlockForAlt>(); + SrcOp opStuff = altForOpAltBlock.ops.get(0); + if ( opStuff instanceof AltBlock ) { + AltBlock opAltBlock = (AltBlock)opStuff; + opAltsCode.addAll(opAltBlock.alts); + } + else { // just a single alt I guess; no block + opAltsCode.add((CodeBlockForAlt)opStuff); + } + + // Insert code in front of each primary alt to create specialized ctx if there was a label + for (int i = 0; i < primaryAltsCode.size(); i++) { + LeftRecursiveRuleAltInfo altInfo = r.recPrimaryAlts.get(i); + if ( altInfo.altLabel==null ) continue; + ST altActionST = codegenTemplates.getInstanceOf("recRuleReplaceContext"); + altActionST.add("ctxName", Utils.capitalize(altInfo.altLabel)); + Action altAction = + new Action(delegate, function.altLabelCtxs.get(altInfo.altLabel), altActionST); + CodeBlockForAlt alt = primaryAltsCode.get(i); + alt.insertOp(0, altAction); + } + + // Insert code to set ctx.stop after primary block and before op * loop + ST setStopTokenAST = codegenTemplates.getInstanceOf("recRuleSetStopToken"); + Action setStopTokenAction = new Action(delegate, function.ruleCtx, setStopTokenAST); + outerAlt.insertOp(1, setStopTokenAction); + + // Insert code to set _prevctx at start of * loop + ST setPrevCtx = codegenTemplates.getInstanceOf("recRuleSetPrevCtx"); + Action setPrevCtxAction = new Action(delegate, function.ruleCtx, setPrevCtx); + opAltStarBlock.addIterationOp(setPrevCtxAction); + + // Insert code in front of each op alt to create specialized ctx if there was an alt label + for (int i = 0; i < opAltsCode.size(); i++) { + ST altActionST; + LeftRecursiveRuleAltInfo altInfo = r.recOpAlts.getElement(i); + String templateName; + if ( altInfo.altLabel!=null ) { + templateName = "recRuleLabeledAltStartAction"; + altActionST = codegenTemplates.getInstanceOf(templateName); + altActionST.add("currentAltLabel", altInfo.altLabel); + } + else { + templateName = "recRuleAltStartAction"; + altActionST = codegenTemplates.getInstanceOf(templateName); + altActionST.add("ctxName", Utils.capitalize(r.name)); + } + altActionST.add("ruleName", r.name); + // add label of any lr ref we deleted + altActionST.add("label", altInfo.leftRecursiveRuleRefLabel); + if (altActionST.impl.formalArguments.containsKey("isListLabel")) { + altActionST.add("isListLabel", altInfo.isListLabel); + } + else if (altInfo.isListLabel) { + delegate.getGenerator().tool.errMgr.toolError(ErrorType.CODE_TEMPLATE_ARG_ISSUE, templateName, "isListLabel"); + } + Action altAction = + new Action(delegate, function.altLabelCtxs.get(altInfo.altLabel), altActionST); + CodeBlockForAlt alt = opAltsCode.get(i); + alt.insertOp(0, altAction); + } + } + + public void buildNormalRuleFunction(Rule r, RuleFunction function) { + CodeGenerator gen = delegate.getGenerator(); + // TRIGGER factory functions for rule alts, elements + GrammarASTAdaptor adaptor = new GrammarASTAdaptor(r.ast.token.getInputStream()); + GrammarAST blk = (GrammarAST)r.ast.getFirstChildWithType(ANTLRParser.BLOCK); + CommonTreeNodeStream nodes = new CommonTreeNodeStream(adaptor,blk); + walker = new SourceGenTriggers(nodes, this); + try { + // walk AST of rule alts/elements + function.code = DefaultOutputModelFactory.list(walker.block(null, null)); + function.hasLookaheadBlock = walker.hasLookaheadBlock; + } + catch (org.antlr.runtime.RecognitionException e){ + e.printStackTrace(System.err); + } + + function.ctxType = gen.getTarget().getRuleFunctionContextStructName(function); + + function.postamble = rulePostamble(function, r); + } + + public void buildLexerRuleActions(Lexer lexer, final Rule r) { + if (r.actions.isEmpty()) { + return; + } + + CodeGenerator gen = delegate.getGenerator(); + Grammar g = delegate.getGrammar(); + String ctxType = gen.getTarget().getRuleFunctionContextStructName(r); + RuleActionFunction raf = lexer.actionFuncs.get(r); + if ( raf==null ) { + raf = new RuleActionFunction(delegate, r, ctxType); + } + + for (ActionAST a : r.actions) { + if ( a instanceof PredAST ) { + PredAST p = (PredAST)a; + RuleSempredFunction rsf = lexer.sempredFuncs.get(r); + if ( rsf==null ) { + rsf = new RuleSempredFunction(delegate, r, ctxType); + lexer.sempredFuncs.put(r, rsf); + } + rsf.actions.put(g.sempreds.get(p), new Action(delegate, p)); + } + else if ( a.getType()== ANTLRParser.ACTION ) { + raf.actions.put(g.lexerActions.get(a), new Action(delegate, a)); + } + } + + if (!raf.actions.isEmpty() && !lexer.actionFuncs.containsKey(r)) { + // only add to lexer if the function actually contains actions + lexer.actionFuncs.put(r, raf); + } + } + + public RuleFunction rule(Rule r) { + RuleFunction rf = delegate.rule(r); + for (CodeGeneratorExtension ext : extensions) rf = ext.rule(rf); + return rf; + } + + public List<SrcOp> rulePostamble(RuleFunction function, Rule r) { + List<SrcOp> ops = delegate.rulePostamble(function, r); + for (CodeGeneratorExtension ext : extensions) ops = ext.rulePostamble(ops); + return ops; + } + + public Grammar getGrammar() { return delegate.getGrammar(); } + + public CodeGenerator getGenerator() { return delegate.getGenerator(); } + + public CodeBlockForAlt alternative(Alternative alt, boolean outerMost) { + CodeBlockForAlt blk = delegate.alternative(alt, outerMost); + if ( outerMost ) { + currentOuterMostAlternativeBlock = (CodeBlockForOuterMostAlt)blk; + } + for (CodeGeneratorExtension ext : extensions) blk = ext.alternative(blk, outerMost); + return blk; + } + + public CodeBlockForAlt finishAlternative(CodeBlockForAlt blk, List<SrcOp> ops, + boolean outerMost) + { + blk = delegate.finishAlternative(blk, ops); + for (CodeGeneratorExtension ext : extensions) blk = ext.finishAlternative(blk, outerMost); + return blk; + } + + public List<SrcOp> ruleRef(GrammarAST ID, GrammarAST label, GrammarAST args) { + List<SrcOp> ops = delegate.ruleRef(ID, label, args); + for (CodeGeneratorExtension ext : extensions) { + ops = ext.ruleRef(ops); + } + return ops; + } + + public List<SrcOp> tokenRef(GrammarAST ID, GrammarAST label, GrammarAST args) + { + List<SrcOp> ops = delegate.tokenRef(ID, label, args); + for (CodeGeneratorExtension ext : extensions) { + ops = ext.tokenRef(ops); + } + return ops; + } + + public List<SrcOp> stringRef(GrammarAST ID, GrammarAST label) { + List<SrcOp> ops = delegate.stringRef(ID, label); + for (CodeGeneratorExtension ext : extensions) { + ops = ext.stringRef(ops); + } + return ops; + } + + /** (A|B|C) possibly with ebnfRoot and label */ + public List<SrcOp> set(GrammarAST setAST, GrammarAST labelAST, boolean invert) { + List<SrcOp> ops = delegate.set(setAST, labelAST, invert); + for (CodeGeneratorExtension ext : extensions) { + ops = ext.set(ops); + } + return ops; + } + + public CodeBlockForAlt epsilon(Alternative alt, boolean outerMost) { + CodeBlockForAlt blk = delegate.epsilon(alt, outerMost); + for (CodeGeneratorExtension ext : extensions) blk = ext.epsilon(blk); + return blk; + } + + public List<SrcOp> wildcard(GrammarAST ast, GrammarAST labelAST) { + List<SrcOp> ops = delegate.wildcard(ast, labelAST); + for (CodeGeneratorExtension ext : extensions) { + ops = ext.set(ops); + } + return ops; + } + + public List<SrcOp> action(ActionAST ast) { + List<SrcOp> ops = delegate.action(ast); + for (CodeGeneratorExtension ext : extensions) ops = ext.action(ops); + return ops; + } + + public List<SrcOp> sempred(ActionAST ast) { + List<SrcOp> ops = delegate.sempred(ast); + for (CodeGeneratorExtension ext : extensions) ops = ext.sempred(ops); + return ops; + } + + public Choice getChoiceBlock(BlockAST blkAST, List<CodeBlockForAlt> alts, GrammarAST label) { + Choice c = delegate.getChoiceBlock(blkAST, alts, label); + for (CodeGeneratorExtension ext : extensions) c = ext.getChoiceBlock(c); + return c; + } + + public Choice getEBNFBlock(GrammarAST ebnfRoot, List<CodeBlockForAlt> alts) { + Choice c = delegate.getEBNFBlock(ebnfRoot, alts); + for (CodeGeneratorExtension ext : extensions) c = ext.getEBNFBlock(c); + return c; + } + + public boolean needsImplicitLabel(GrammarAST ID, LabeledOp op) { + boolean needs = delegate.needsImplicitLabel(ID, op); + for (CodeGeneratorExtension ext : extensions) needs |= ext.needsImplicitLabel(ID, op); + return needs; + } + + public OutputModelObject getRoot() { return root; } + + public void setRoot(OutputModelObject root) { this.root = root; } + + public RuleFunction getCurrentRuleFunction() { + if ( !currentRule.isEmpty() ) return currentRule.peek(); + return null; + } + + public void pushCurrentRule(RuleFunction r) { currentRule.push(r); } + + public RuleFunction popCurrentRule() { + if ( !currentRule.isEmpty() ) return currentRule.pop(); + return null; + } + + public Alternative getCurrentOuterMostAlt() { return currentOuterMostAlt; } + + public void setCurrentOuterMostAlt(Alternative currentOuterMostAlt) { this.currentOuterMostAlt = currentOuterMostAlt; } + + public void setCurrentBlock(CodeBlock blk) { + currentBlock = blk; + } + + public CodeBlock getCurrentBlock() { + return currentBlock; + } + + public void setCurrentOuterMostAlternativeBlock(CodeBlockForOuterMostAlt currentOuterMostAlternativeBlock) { + this.currentOuterMostAlternativeBlock = currentOuterMostAlternativeBlock; + } + + public CodeBlockForOuterMostAlt getCurrentOuterMostAlternativeBlock() { + return currentOuterMostAlternativeBlock; + } + + public int getCodeBlockLevel() { return codeBlockLevel; } +} diff --git a/tool/src/org/antlr/v4/codegen/OutputModelFactory.java b/tool/src/org/antlr/v4/codegen/OutputModelFactory.java new file mode 100644 index 0000000..86f9ae0 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/OutputModelFactory.java @@ -0,0 +1,130 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen; + +import org.antlr.v4.codegen.model.Choice; +import org.antlr.v4.codegen.model.CodeBlockForAlt; +import org.antlr.v4.codegen.model.CodeBlockForOuterMostAlt; +import org.antlr.v4.codegen.model.LabeledOp; +import org.antlr.v4.codegen.model.Lexer; +import org.antlr.v4.codegen.model.LexerFile; +import org.antlr.v4.codegen.model.OutputModelObject; +import org.antlr.v4.codegen.model.Parser; +import org.antlr.v4.codegen.model.ParserFile; +import org.antlr.v4.codegen.model.RuleFunction; +import org.antlr.v4.codegen.model.SrcOp; +import org.antlr.v4.codegen.model.decl.CodeBlock; +import org.antlr.v4.runtime.misc.IntervalSet; +import org.antlr.v4.tool.Alternative; +import org.antlr.v4.tool.Grammar; +import org.antlr.v4.tool.Rule; +import org.antlr.v4.tool.ast.ActionAST; +import org.antlr.v4.tool.ast.BlockAST; +import org.antlr.v4.tool.ast.GrammarAST; + +import java.util.List; + +public interface OutputModelFactory { + Grammar getGrammar(); + + CodeGenerator getGenerator(); + + void setController(OutputModelController controller); + + OutputModelController getController(); + + ParserFile parserFile(String fileName); + + Parser parser(ParserFile file); + + LexerFile lexerFile(String fileName); + + Lexer lexer(LexerFile file); + + RuleFunction rule(Rule r); + + List<SrcOp> rulePostamble(RuleFunction function, Rule r); + + // ELEMENT TRIGGERS + + CodeBlockForAlt alternative(Alternative alt, boolean outerMost); + + CodeBlockForAlt finishAlternative(CodeBlockForAlt blk, List<SrcOp> ops); + + CodeBlockForAlt epsilon(Alternative alt, boolean outerMost); + + List<SrcOp> ruleRef(GrammarAST ID, GrammarAST label, GrammarAST args); + + List<SrcOp> tokenRef(GrammarAST ID, GrammarAST label, GrammarAST args); + + List<SrcOp> stringRef(GrammarAST ID, GrammarAST label); + + List<SrcOp> set(GrammarAST setAST, GrammarAST label, boolean invert); + + List<SrcOp> wildcard(GrammarAST ast, GrammarAST labelAST); + + List<SrcOp> action(ActionAST ast); + + List<SrcOp> sempred(ActionAST ast); + + Choice getChoiceBlock(BlockAST blkAST, List<CodeBlockForAlt> alts, GrammarAST label); + + Choice getEBNFBlock(GrammarAST ebnfRoot, List<CodeBlockForAlt> alts); + + Choice getLL1ChoiceBlock(BlockAST blkAST, List<CodeBlockForAlt> alts); + + Choice getComplexChoiceBlock(BlockAST blkAST, List<CodeBlockForAlt> alts); + + Choice getLL1EBNFBlock(GrammarAST ebnfRoot, List<CodeBlockForAlt> alts); + + Choice getComplexEBNFBlock(GrammarAST ebnfRoot, List<CodeBlockForAlt> alts); + + List<SrcOp> getLL1Test(IntervalSet look, GrammarAST blkAST); + + boolean needsImplicitLabel(GrammarAST ID, LabeledOp op); + + // CONTEXT INFO + + OutputModelObject getRoot(); + + RuleFunction getCurrentRuleFunction(); + + Alternative getCurrentOuterMostAlt(); + + CodeBlock getCurrentBlock(); + + CodeBlockForOuterMostAlt getCurrentOuterMostAlternativeBlock(); + + int getCodeBlockLevel(); + + int getTreeLevel(); + +} diff --git a/tool/src/org/antlr/v4/codegen/OutputModelWalker.java b/tool/src/org/antlr/v4/codegen/OutputModelWalker.java new file mode 100644 index 0000000..8a03895 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/OutputModelWalker.java @@ -0,0 +1,166 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen; + +import org.antlr.v4.Tool; +import org.antlr.v4.codegen.model.ModelElement; +import org.antlr.v4.codegen.model.OutputModelObject; +import org.antlr.v4.tool.ErrorType; +import org.stringtemplate.v4.ST; +import org.stringtemplate.v4.STGroup; +import org.stringtemplate.v4.compiler.FormalArgument; + +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +/** Convert an output model tree to template hierarchy by walking + * the output model. Each output model object has a corresponding template + * of the same name. An output model object can have nested objects. + * We identify those nested objects by the list of arguments in the template + * definition. For example, here is the definition of the parser template: + * + * Parser(parser, scopes, funcs) ::= <<...>> + * + * The first template argument is always the output model object from which + * this walker will create the template. Any other arguments identify + * the field names within the output model object of nested model objects. + * So, in this case, template Parser is saying that output model object + * Parser has two fields the walker should chase called a scopes and funcs. + * + * This simple mechanism means we don't have to include code in every + * output model object that says how to create the corresponding template. + */ +public class OutputModelWalker { + Tool tool; + STGroup templates; + + public OutputModelWalker(Tool tool, + STGroup templates) + { + this.tool = tool; + this.templates = templates; + } + + public ST walk(OutputModelObject omo) { + // CREATE TEMPLATE FOR THIS OUTPUT OBJECT + Class<? extends OutputModelObject> cl = omo.getClass(); + String templateName = cl.getSimpleName(); + if ( templateName == null ) { + tool.errMgr.toolError(ErrorType.NO_MODEL_TO_TEMPLATE_MAPPING, cl.getSimpleName()); + return new ST("["+templateName+" invalid]"); + } + ST st = templates.getInstanceOf(templateName); + if ( st == null ) { + tool.errMgr.toolError(ErrorType.CODE_GEN_TEMPLATES_INCOMPLETE, templateName); + return new ST("["+templateName+" invalid]"); + } + if ( st.impl.formalArguments == null ) { + tool.errMgr.toolError(ErrorType.CODE_TEMPLATE_ARG_ISSUE, templateName, "<none>"); + return st; + } + + Map<String,FormalArgument> formalArgs = st.impl.formalArguments; + + // PASS IN OUTPUT MODEL OBJECT TO TEMPLATE AS FIRST ARG + Set<String> argNames = formalArgs.keySet(); + Iterator<String> arg_it = argNames.iterator(); + String modelArgName = arg_it.next(); // ordered so this is first arg + st.add(modelArgName, omo); + + // COMPUTE STs FOR EACH NESTED MODEL OBJECT MARKED WITH @ModelElement AND MAKE ST ATTRIBUTE + Set<String> usedFieldNames = new HashSet<String>(); + Field fields[] = cl.getFields(); + for (Field fi : fields) { + ModelElement annotation = fi.getAnnotation(ModelElement.class); + if (annotation == null) { + continue; + } + + String fieldName = fi.getName(); + + if (!usedFieldNames.add(fieldName)) { + tool.errMgr.toolError(ErrorType.INTERNAL_ERROR, "Model object " + omo.getClass().getSimpleName() + " has multiple fields named '" + fieldName + "'"); + continue; + } + + // Just don't set @ModelElement fields w/o formal arg in target ST + if ( formalArgs.get(fieldName)==null ) continue; + + try { + Object o = fi.get(omo); + if ( o instanceof OutputModelObject ) { // SINGLE MODEL OBJECT? + OutputModelObject nestedOmo = (OutputModelObject)o; + ST nestedST = walk(nestedOmo); +// System.out.println("set ModelElement "+fieldName+"="+nestedST+" in "+templateName); + st.add(fieldName, nestedST); + } + else if ( o instanceof Collection || o instanceof OutputModelObject[] ) { + // LIST OF MODEL OBJECTS? + if ( o instanceof OutputModelObject[] ) { + o = Arrays.asList((OutputModelObject[])o); + } + Collection<?> nestedOmos = (Collection<?>)o; + for (Object nestedOmo : nestedOmos) { + if ( nestedOmo==null ) continue; + ST nestedST = walk((OutputModelObject)nestedOmo); +// System.out.println("set ModelElement "+fieldName+"="+nestedST+" in "+templateName); + st.add(fieldName, nestedST); + } + } + else if ( o instanceof Map ) { + Map<?, ?> nestedOmoMap = (Map<?, ?>)o; + Map<Object, ST> m = new LinkedHashMap<Object, ST>(); + for (Map.Entry<?, ?> entry : nestedOmoMap.entrySet()) { + ST nestedST = walk((OutputModelObject)entry.getValue()); +// System.out.println("set ModelElement "+fieldName+"="+nestedST+" in "+templateName); + m.put(entry.getKey(), nestedST); + } + st.add(fieldName, m); + } + else if ( o!=null ) { + tool.errMgr.toolError(ErrorType.INTERNAL_ERROR, "not recognized nested model element: "+fieldName); + } + } + catch (IllegalAccessException iae) { + tool.errMgr.toolError(ErrorType.CODE_TEMPLATE_ARG_ISSUE, templateName, fieldName); + } + } + //st.impl.dump(); + return st; + } + +} diff --git a/tool/src/org/antlr/v4/codegen/ParserFactory.java b/tool/src/org/antlr/v4/codegen/ParserFactory.java new file mode 100644 index 0000000..dfb33f5 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/ParserFactory.java @@ -0,0 +1,367 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen; + +import org.antlr.v4.analysis.AnalysisPipeline; +import org.antlr.v4.codegen.model.Action; +import org.antlr.v4.codegen.model.AddToLabelList; +import org.antlr.v4.codegen.model.AltBlock; +import org.antlr.v4.codegen.model.Choice; +import org.antlr.v4.codegen.model.CodeBlockForAlt; +import org.antlr.v4.codegen.model.CodeBlockForOuterMostAlt; +import org.antlr.v4.codegen.model.InvokeRule; +import org.antlr.v4.codegen.model.LL1AltBlock; +import org.antlr.v4.codegen.model.LL1OptionalBlock; +import org.antlr.v4.codegen.model.LL1OptionalBlockSingleAlt; +import org.antlr.v4.codegen.model.LL1PlusBlockSingleAlt; +import org.antlr.v4.codegen.model.LL1StarBlockSingleAlt; +import org.antlr.v4.codegen.model.LabeledOp; +import org.antlr.v4.codegen.model.LeftRecursiveRuleFunction; +import org.antlr.v4.codegen.model.MatchNotSet; +import org.antlr.v4.codegen.model.MatchSet; +import org.antlr.v4.codegen.model.MatchToken; +import org.antlr.v4.codegen.model.OptionalBlock; +import org.antlr.v4.codegen.model.Parser; +import org.antlr.v4.codegen.model.ParserFile; +import org.antlr.v4.codegen.model.PlusBlock; +import org.antlr.v4.codegen.model.RuleFunction; +import org.antlr.v4.codegen.model.SemPred; +import org.antlr.v4.codegen.model.SrcOp; +import org.antlr.v4.codegen.model.StarBlock; +import org.antlr.v4.codegen.model.TestSetInline; +import org.antlr.v4.codegen.model.decl.Decl; +import org.antlr.v4.codegen.model.decl.RuleContextDecl; +import org.antlr.v4.codegen.model.decl.TokenDecl; +import org.antlr.v4.codegen.model.decl.TokenListDecl; +import org.antlr.v4.parse.ANTLRParser; +import org.antlr.v4.runtime.atn.DecisionState; +import org.antlr.v4.runtime.atn.PlusLoopbackState; +import org.antlr.v4.runtime.atn.StarLoopEntryState; +import org.antlr.v4.runtime.misc.IntervalSet; +import org.antlr.v4.tool.Alternative; +import org.antlr.v4.tool.LeftRecursiveRule; +import org.antlr.v4.tool.Rule; +import org.antlr.v4.tool.ast.ActionAST; +import org.antlr.v4.tool.ast.BlockAST; +import org.antlr.v4.tool.ast.GrammarAST; +import org.antlr.v4.tool.ast.TerminalAST; + +import java.util.List; + +/** */ +public class ParserFactory extends DefaultOutputModelFactory { + public ParserFactory(CodeGenerator gen) { super(gen); } + + @Override + public ParserFile parserFile(String fileName) { + return new ParserFile(this, fileName); + } + + @Override + public Parser parser(ParserFile file) { + return new Parser(this, file); + } + + @Override + public RuleFunction rule(Rule r) { + if ( r instanceof LeftRecursiveRule ) { + return new LeftRecursiveRuleFunction(this, (LeftRecursiveRule)r); + } + else { + RuleFunction rf = new RuleFunction(this, r); + return rf; + } + } + + @Override + public CodeBlockForAlt epsilon(Alternative alt, boolean outerMost) { + return alternative(alt, outerMost); + } + + @Override + public CodeBlockForAlt alternative(Alternative alt, boolean outerMost) { + if ( outerMost ) return new CodeBlockForOuterMostAlt(this, alt); + return new CodeBlockForAlt(this); + } + + @Override + public CodeBlockForAlt finishAlternative(CodeBlockForAlt blk, List<SrcOp> ops) { + blk.ops = ops; + return blk; + } + + @Override + public List<SrcOp> action(ActionAST ast) { return list(new Action(this, ast)); } + + @Override + public List<SrcOp> sempred(ActionAST ast) { return list(new SemPred(this, ast)); } + + @Override + public List<SrcOp> ruleRef(GrammarAST ID, GrammarAST label, GrammarAST args) { + InvokeRule invokeOp = new InvokeRule(this, ID, label); + // If no manual label and action refs as token/rule not label, we need to define implicit label + if ( controller.needsImplicitLabel(ID, invokeOp) ) defineImplicitLabel(ID, invokeOp); + AddToLabelList listLabelOp = getAddToListOpIfListLabelPresent(invokeOp, label); + return list(invokeOp, listLabelOp); + } + + @Override + public List<SrcOp> tokenRef(GrammarAST ID, GrammarAST labelAST, GrammarAST args) { + MatchToken matchOp = new MatchToken(this, (TerminalAST) ID); + if ( labelAST!=null ) { + String label = labelAST.getText(); + RuleFunction rf = getCurrentRuleFunction(); + if ( labelAST.parent.getType() == ANTLRParser.PLUS_ASSIGN ) { + // add Token _X and List<Token> X decls + defineImplicitLabel(ID, matchOp); // adds _X + TokenListDecl l = getTokenListLabelDecl(label); + rf.addContextDecl(ID.getAltLabel(), l); + } + else { + Decl d = getTokenLabelDecl(label); + matchOp.labels.add(d); + rf.addContextDecl(ID.getAltLabel(), d); + } + +// Decl d = getTokenLabelDecl(label); +// ((MatchToken)matchOp).labels.add(d); +// getCurrentRuleFunction().addContextDecl(ID.getAltLabel(), d); +// if ( labelAST.parent.getType() == ANTLRParser.PLUS_ASSIGN ) { +// TokenListDecl l = getTokenListLabelDecl(label); +// getCurrentRuleFunction().addContextDecl(ID.getAltLabel(), l); +// } + } + if ( controller.needsImplicitLabel(ID, matchOp) ) defineImplicitLabel(ID, matchOp); + AddToLabelList listLabelOp = getAddToListOpIfListLabelPresent(matchOp, labelAST); + return list(matchOp, listLabelOp); + } + + public Decl getTokenLabelDecl(String label) { + return new TokenDecl(this, label); + } + + public TokenListDecl getTokenListLabelDecl(String label) { + return new TokenListDecl(this, gen.getTarget().getListLabel(label)); + } + + @Override + public List<SrcOp> set(GrammarAST setAST, GrammarAST labelAST, boolean invert) { + MatchSet matchOp; + if ( invert ) matchOp = new MatchNotSet(this, setAST); + else matchOp = new MatchSet(this, setAST); + if ( labelAST!=null ) { + String label = labelAST.getText(); + RuleFunction rf = getCurrentRuleFunction(); + if ( labelAST.parent.getType() == ANTLRParser.PLUS_ASSIGN ) { + defineImplicitLabel(setAST, matchOp); + TokenListDecl l = getTokenListLabelDecl(label); + rf.addContextDecl(setAST.getAltLabel(), l); + } + else { + Decl d = getTokenLabelDecl(label); + matchOp.labels.add(d); + rf.addContextDecl(setAST.getAltLabel(), d); + } + } + if ( controller.needsImplicitLabel(setAST, matchOp) ) defineImplicitLabel(setAST, matchOp); + AddToLabelList listLabelOp = getAddToListOpIfListLabelPresent(matchOp, labelAST); + return list(matchOp, listLabelOp); + } + + @Override + public List<SrcOp> wildcard(GrammarAST ast, GrammarAST labelAST) { + Wildcard wild = new Wildcard(this, ast); + // TODO: dup with tokenRef + if ( labelAST!=null ) { + String label = labelAST.getText(); + Decl d = getTokenLabelDecl(label); + wild.labels.add(d); + getCurrentRuleFunction().addContextDecl(ast.getAltLabel(), d); + if ( labelAST.parent.getType() == ANTLRParser.PLUS_ASSIGN ) { + TokenListDecl l = getTokenListLabelDecl(label); + getCurrentRuleFunction().addContextDecl(ast.getAltLabel(), l); + } + } + if ( controller.needsImplicitLabel(ast, wild) ) defineImplicitLabel(ast, wild); + AddToLabelList listLabelOp = getAddToListOpIfListLabelPresent(wild, labelAST); + return list(wild, listLabelOp); + } + + @Override + public Choice getChoiceBlock(BlockAST blkAST, List<CodeBlockForAlt> alts, GrammarAST labelAST) { + int decision = ((DecisionState)blkAST.atnState).decision; + Choice c; + if ( !g.tool.force_atn && AnalysisPipeline.disjoint(g.decisionLOOK.get(decision)) ) { + c = getLL1ChoiceBlock(blkAST, alts); + } + else { + c = getComplexChoiceBlock(blkAST, alts); + } + + if ( labelAST!=null ) { // for x=(...), define x or x_list + String label = labelAST.getText(); + Decl d = getTokenLabelDecl(label); + c.label = d; + getCurrentRuleFunction().addContextDecl(labelAST.getAltLabel(), d); + if ( labelAST.parent.getType() == ANTLRParser.PLUS_ASSIGN ) { + String listLabel = gen.getTarget().getListLabel(label); + TokenListDecl l = new TokenListDecl(this, listLabel); + getCurrentRuleFunction().addContextDecl(labelAST.getAltLabel(), l); + } + } + + return c; + } + + @Override + public Choice getEBNFBlock(GrammarAST ebnfRoot, List<CodeBlockForAlt> alts) { + if (!g.tool.force_atn) { + int decision; + if ( ebnfRoot.getType()==ANTLRParser.POSITIVE_CLOSURE ) { + decision = ((PlusLoopbackState)ebnfRoot.atnState).decision; + } + else if ( ebnfRoot.getType()==ANTLRParser.CLOSURE ) { + decision = ((StarLoopEntryState)ebnfRoot.atnState).decision; + } + else { + decision = ((DecisionState)ebnfRoot.atnState).decision; + } + + if ( AnalysisPipeline.disjoint(g.decisionLOOK.get(decision)) ) { + return getLL1EBNFBlock(ebnfRoot, alts); + } + } + + return getComplexEBNFBlock(ebnfRoot, alts); + } + + @Override + public Choice getLL1ChoiceBlock(BlockAST blkAST, List<CodeBlockForAlt> alts) { + return new LL1AltBlock(this, blkAST, alts); + } + + @Override + public Choice getComplexChoiceBlock(BlockAST blkAST, List<CodeBlockForAlt> alts) { + return new AltBlock(this, blkAST, alts); + } + + @Override + public Choice getLL1EBNFBlock(GrammarAST ebnfRoot, List<CodeBlockForAlt> alts) { + int ebnf = 0; + if ( ebnfRoot!=null ) ebnf = ebnfRoot.getType(); + Choice c = null; + switch ( ebnf ) { + case ANTLRParser.OPTIONAL : + if ( alts.size()==1 ) c = new LL1OptionalBlockSingleAlt(this, ebnfRoot, alts); + else c = new LL1OptionalBlock(this, ebnfRoot, alts); + break; + case ANTLRParser.CLOSURE : + if ( alts.size()==1 ) c = new LL1StarBlockSingleAlt(this, ebnfRoot, alts); + else c = getComplexEBNFBlock(ebnfRoot, alts); + break; + case ANTLRParser.POSITIVE_CLOSURE : + if ( alts.size()==1 ) c = new LL1PlusBlockSingleAlt(this, ebnfRoot, alts); + else c = getComplexEBNFBlock(ebnfRoot, alts); + break; + } + return c; + } + + @Override + public Choice getComplexEBNFBlock(GrammarAST ebnfRoot, List<CodeBlockForAlt> alts) { + int ebnf = 0; + if ( ebnfRoot!=null ) ebnf = ebnfRoot.getType(); + Choice c = null; + switch ( ebnf ) { + case ANTLRParser.OPTIONAL : + c = new OptionalBlock(this, ebnfRoot, alts); + break; + case ANTLRParser.CLOSURE : + c = new StarBlock(this, ebnfRoot, alts); + break; + case ANTLRParser.POSITIVE_CLOSURE : + c = new PlusBlock(this, ebnfRoot, alts); + break; + } + return c; + } + + @Override + public List<SrcOp> getLL1Test(IntervalSet look, GrammarAST blkAST) { + return list(new TestSetInline(this, blkAST, look, gen.getTarget().getInlineTestSetWordSize())); + } + + @Override + public boolean needsImplicitLabel(GrammarAST ID, LabeledOp op) { + Alternative currentOuterMostAlt = getCurrentOuterMostAlt(); + boolean actionRefsAsToken = currentOuterMostAlt.tokenRefsInActions.containsKey(ID.getText()); + boolean actionRefsAsRule = currentOuterMostAlt.ruleRefsInActions.containsKey(ID.getText()); + return op.getLabels().isEmpty() && (actionRefsAsToken || actionRefsAsRule); + } + + // support + + public void defineImplicitLabel(GrammarAST ast, LabeledOp op) { + Decl d; + if ( ast.getType()==ANTLRParser.SET || ast.getType()==ANTLRParser.WILDCARD ) { + String implLabel = + gen.getTarget().getImplicitSetLabel(String.valueOf(ast.token.getTokenIndex())); + d = getTokenLabelDecl(implLabel); + ((TokenDecl)d).isImplicit = true; + } + else if ( ast.getType()==ANTLRParser.RULE_REF ) { // a rule reference? + Rule r = g.getRule(ast.getText()); + String implLabel = gen.getTarget().getImplicitRuleLabel(ast.getText()); + String ctxName = + gen.getTarget().getRuleFunctionContextStructName(r); + d = new RuleContextDecl(this, implLabel, ctxName); + ((RuleContextDecl)d).isImplicit = true; + } + else { + String implLabel = gen.getTarget().getImplicitTokenLabel(ast.getText()); + d = getTokenLabelDecl(implLabel); + ((TokenDecl)d).isImplicit = true; + } + op.getLabels().add(d); + // all labels must be in scope struct in case we exec action out of context + getCurrentRuleFunction().addContextDecl(ast.getAltLabel(), d); + } + + public AddToLabelList getAddToListOpIfListLabelPresent(LabeledOp op, GrammarAST label) { + AddToLabelList labelOp = null; + if ( label!=null && label.parent.getType()==ANTLRParser.PLUS_ASSIGN ) { + String listLabel = gen.getTarget().getListLabel(label.getText()); + labelOp = new AddToLabelList(this, listLabel, op.getLabels().get(0)); + } + return labelOp; + } + +} diff --git a/tool/src/org/antlr/v4/codegen/SourceGenTriggers.g b/tool/src/org/antlr/v4/codegen/SourceGenTriggers.g new file mode 100644 index 0000000..de3154f --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/SourceGenTriggers.g @@ -0,0 +1,198 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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. + */ + +tree grammar SourceGenTriggers; +options { + language = Java; + tokenVocab = ANTLRParser; + ASTLabelType = GrammarAST; +} + +@header { +package org.antlr.v4.codegen; +import org.antlr.v4.misc.Utils; +import org.antlr.v4.codegen.model.*; +import org.antlr.v4.codegen.model.decl.*; +import org.antlr.v4.tool.*; +import org.antlr.v4.tool.ast.*; +import java.util.Collections; +import java.util.Map; +import java.util.HashMap; +} + +@members { + public OutputModelController controller; + public boolean hasLookaheadBlock; + public SourceGenTriggers(TreeNodeStream input, OutputModelController controller) { + this(input); + this.controller = controller; + } +} + +dummy : block[null, null] ; + +block[GrammarAST label, GrammarAST ebnfRoot] returns [List<? extends SrcOp> omos] + : ^( blk=BLOCK (^(OPTIONS .+))? + {List<CodeBlockForAlt> alts = new ArrayList<CodeBlockForAlt>();} + ( alternative {alts.add($alternative.altCodeBlock);} )+ + ) + { + if ( alts.size()==1 && ebnfRoot==null) return alts; + if ( ebnfRoot==null ) { + $omos = DefaultOutputModelFactory.list(controller.getChoiceBlock((BlockAST)$blk, alts, $label)); + } + else { + Choice choice = controller.getEBNFBlock($ebnfRoot, alts); + hasLookaheadBlock |= choice instanceof PlusBlock || choice instanceof StarBlock; + $omos = DefaultOutputModelFactory.list(choice); + } + } + ; + +alternative returns [CodeBlockForAlt altCodeBlock, List<SrcOp> ops] +@init { + boolean outerMost = inContext("RULE BLOCK"); +} +@after { + controller.finishAlternative($altCodeBlock, $ops, outerMost); +} + : a=alt[outerMost] {$altCodeBlock=$a.altCodeBlock; $ops=$a.ops;} + ; + +alt[boolean outerMost] returns [CodeBlockForAlt altCodeBlock, List<SrcOp> ops] +@init { + // set alt if outer ALT only (the only ones with alt field set to Alternative object) + AltAST altAST = (AltAST)retval.start; + if ( outerMost ) controller.setCurrentOuterMostAlt(altAST.alt); +} + : { + List<SrcOp> elems = new ArrayList<SrcOp>(); + // TODO: shouldn't we pass $start to controller.alternative()? + $altCodeBlock = controller.alternative(controller.getCurrentOuterMostAlt(), outerMost); + $altCodeBlock.ops = $ops = elems; + controller.setCurrentBlock($altCodeBlock); + } + ^( ALT elementOptions? ( element {if ($element.omos!=null) elems.addAll($element.omos);} )+ ) + + | ^(ALT elementOptions? EPSILON) + {$altCodeBlock = controller.epsilon(controller.getCurrentOuterMostAlt(), outerMost);} + ; + +element returns [List<? extends SrcOp> omos] + : labeledElement {$omos = $labeledElement.omos;} + | atom[null,false] {$omos = $atom.omos;} + | subrule {$omos = $subrule.omos;} + | ACTION {$omos = controller.action((ActionAST)$ACTION);} + | SEMPRED {$omos = controller.sempred((ActionAST)$SEMPRED);} + | ^(ACTION elementOptions) {$omos = controller.action((ActionAST)$ACTION);} + | ^(SEMPRED elementOptions) {$omos = controller.sempred((ActionAST)$SEMPRED);} + ; + +labeledElement returns [List<? extends SrcOp> omos] + : ^(ASSIGN ID atom[$ID,false] ) {$omos = $atom.omos;} + | ^(PLUS_ASSIGN ID atom[$ID,false]) {$omos = $atom.omos;} + | ^(ASSIGN ID block[$ID,null] ) {$omos = $block.omos;} + | ^(PLUS_ASSIGN ID block[$ID,null]) {$omos = $block.omos;} + ; + +subrule returns [List<? extends SrcOp> omos] + : ^(OPTIONAL b=block[null,$OPTIONAL]) + { + $omos = $block.omos; + } + | ( ^(op=CLOSURE b=block[null,null]) + | ^(op=POSITIVE_CLOSURE b=block[null,null]) + ) + { + List<CodeBlockForAlt> alts = new ArrayList<CodeBlockForAlt>(); + SrcOp blk = $b.omos.get(0); + CodeBlockForAlt alt = new CodeBlockForAlt(controller.delegate); + alt.addOp(blk); + alts.add(alt); + SrcOp loop = controller.getEBNFBlock($op, alts); // "star it" + hasLookaheadBlock |= loop instanceof PlusBlock || loop instanceof StarBlock; + $omos = DefaultOutputModelFactory.list(loop); + } + | block[null, null] {$omos = $block.omos;} + ; + +blockSet[GrammarAST label, boolean invert] returns [List<SrcOp> omos] + : ^(SET atom[label,invert]+) {$omos = controller.set($SET, $label, invert);} + ; + +/* +setElement + : STRING_LITERAL + | TOKEN_REF + | ^(RANGE STRING_LITERAL STRING_LITERAL) + ; +*/ + +// TODO: combine ROOT/BANG into one then just make new op ref'ing return value of atom/terminal... +// TODO: same for NOT +atom[GrammarAST label, boolean invert] returns [List<SrcOp> omos] + : ^(NOT a=atom[$label, true]) {$omos = $a.omos;} + | range[label] {$omos = $range.omos;} + | ^(DOT ID terminal[$label]) + | ^(DOT ID ruleref[$label]) + | ^(WILDCARD .) {$omos = controller.wildcard($WILDCARD, $label);} + | WILDCARD {$omos = controller.wildcard($WILDCARD, $label);} + | terminal[label] {$omos = $terminal.omos;} + | ruleref[label] {$omos = $ruleref.omos;} + | blockSet[$label, invert] {$omos = $blockSet.omos;} + ; + +ruleref[GrammarAST label] returns [List<SrcOp> omos] + : ^(RULE_REF ARG_ACTION? elementOptions?) {$omos = controller.ruleRef($RULE_REF, $label, $ARG_ACTION);} + ; + +range[GrammarAST label] returns [List<SrcOp> omos] + : ^(RANGE a=STRING_LITERAL b=STRING_LITERAL) + ; + +terminal[GrammarAST label] returns [List<SrcOp> omos] + : ^(STRING_LITERAL .) {$omos = controller.stringRef($STRING_LITERAL, $label);} + | STRING_LITERAL {$omos = controller.stringRef($STRING_LITERAL, $label);} + | ^(TOKEN_REF ARG_ACTION .) {$omos = controller.tokenRef($TOKEN_REF, $label, $ARG_ACTION);} + | ^(TOKEN_REF .) {$omos = controller.tokenRef($TOKEN_REF, $label, null);} + | TOKEN_REF {$omos = controller.tokenRef($TOKEN_REF, $label, null);} + ; + +elementOptions + : ^(ELEMENT_OPTIONS elementOption+) + ; + +elementOption + : ID + | ^(ASSIGN ID ID) + | ^(ASSIGN ID STRING_LITERAL) + | ^(ASSIGN ID ACTION) + | ^(ASSIGN ID INT) + ; diff --git a/tool/src/org/antlr/v4/codegen/Target.java b/tool/src/org/antlr/v4/codegen/Target.java new file mode 100644 index 0000000..1f2d748 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/Target.java @@ -0,0 +1,527 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen; + +import org.antlr.v4.Tool; +import org.antlr.v4.codegen.model.RuleFunction; +import org.antlr.v4.codegen.model.SerializedATN; +import org.antlr.v4.misc.Utils; +import org.antlr.v4.parse.ANTLRParser; +import org.antlr.v4.runtime.RuntimeMetaData; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.tool.ErrorType; +import org.antlr.v4.tool.Grammar; +import org.antlr.v4.tool.Rule; +import org.antlr.v4.tool.ast.GrammarAST; +import org.stringtemplate.v4.NumberRenderer; +import org.stringtemplate.v4.ST; +import org.stringtemplate.v4.STErrorListener; +import org.stringtemplate.v4.STGroup; +import org.stringtemplate.v4.STGroupFile; +import org.stringtemplate.v4.StringRenderer; +import org.stringtemplate.v4.misc.STMessage; + +/** */ +public abstract class Target { + /** For pure strings of Java 16-bit Unicode char, how can we display + * it in the target language as a literal. Useful for dumping + * predicates and such that may refer to chars that need to be escaped + * when represented as strings. Also, templates need to be escaped so + * that the target language can hold them as a string. + * <p/> + * I have defined (via the constructor) the set of typical escapes, + * but your {@link Target} subclass is free to alter the translated chars + * or add more definitions. This is non-static so each target can have + * a different set in memory at same time. + */ + protected String[] targetCharValueEscape = new String[255]; + + private final CodeGenerator gen; + private final String language; + private STGroup templates; + + protected Target(CodeGenerator gen, String language) { + targetCharValueEscape['\n'] = "\\n"; + targetCharValueEscape['\r'] = "\\r"; + targetCharValueEscape['\t'] = "\\t"; + targetCharValueEscape['\b'] = "\\b"; + targetCharValueEscape['\f'] = "\\f"; + targetCharValueEscape['\\'] = "\\\\"; + targetCharValueEscape['\''] = "\\'"; + targetCharValueEscape['"'] = "\\\""; + this.gen = gen; + this.language = language; + } + + public CodeGenerator getCodeGenerator() { + return gen; + } + + public String getLanguage() { + return language; + } + + /** ANTLR tool should check output templates / target are compatible with tool code generation. + * For now, a simple string match used on x.y of x.y.z scheme. We use a method to avoid mismatches + * between a template called VERSION. This value is checked against Tool.VERSION during load of templates. + * + * This additional method forces all targets 4.3 and beyond to add this method. + * + * @since 4.3 + */ + public abstract String getVersion(); + + + public STGroup getTemplates() { + if (templates == null) { + String version = getVersion(); + if ( version==null || + !RuntimeMetaData.getMajorMinorVersion(version).equals(RuntimeMetaData.getMajorMinorVersion(Tool.VERSION))) + { + gen.tool.errMgr.toolError(ErrorType.INCOMPATIBLE_TOOL_AND_TEMPLATES, version, Tool.VERSION, language); + } + templates = loadTemplates(); + } + + return templates; + } + + protected void genFile(Grammar g, + ST outputFileST, + String fileName) + { + getCodeGenerator().write(outputFileST, fileName); + } + + protected void genListenerFile(Grammar g, + ST outputFileST) + { + String fileName = getCodeGenerator().getListenerFileName(); + getCodeGenerator().write(outputFileST, fileName); + } + + protected void genRecognizerHeaderFile(Grammar g, + ST headerFileST, + String extName) // e.g., ".h" + { + // no header file by default + } + + /** Get a meaningful name for a token type useful during code generation. + * Literals without associated names are converted to the string equivalent + * of their integer values. Used to generate x==ID and x==34 type comparisons + * etc... Essentially we are looking for the most obvious way to refer + * to a token type in the generated code. + */ + public String getTokenTypeAsTargetLabel(Grammar g, int ttype) { + String name = g.getTokenName(ttype); + // If name is not valid, return the token type instead + if ( Grammar.INVALID_TOKEN_NAME.equals(name) ) { + return String.valueOf(ttype); + } + + return name; + } + + public String[] getTokenTypesAsTargetLabels(Grammar g, int[] ttypes) { + String[] labels = new String[ttypes.length]; + for (int i=0; i<ttypes.length; i++) { + labels[i] = getTokenTypeAsTargetLabel(g, ttypes[i]); + } + return labels; + } + + /** Given a random string of Java unicode chars, return a new string with + * optionally appropriate quote characters for target language and possibly + * with some escaped characters. For example, if the incoming string has + * actual newline characters, the output of this method would convert them + * to the two char sequence \n for Java, C, C++, ... The new string has + * double-quotes around it as well. Example String in memory: + * + * a"[newlinechar]b'c[carriagereturnchar]d[tab]e\f + * + * would be converted to the valid Java s: + * + * "a\"\nb'c\rd\te\\f" + * + * or + * + * a\"\nb'c\rd\te\\f + * + * depending on the quoted arg. + */ + public String getTargetStringLiteralFromString(String s, boolean quoted) { + if ( s==null ) { + return null; + } + + StringBuilder buf = new StringBuilder(); + if ( quoted ) { + buf.append('"'); + } + for (int i=0; i<s.length(); i++) { + int c = s.charAt(i); + if ( c!='\'' && // don't escape single quotes in strings for java + c<targetCharValueEscape.length && + targetCharValueEscape[c]!=null ) + { + buf.append(targetCharValueEscape[c]); + } + else { + buf.append((char)c); + } + } + if ( quoted ) { + buf.append('"'); + } + return buf.toString(); + } + + public String getTargetStringLiteralFromString(String s) { + return getTargetStringLiteralFromString(s, true); + } + + /** + * <p>Convert from an ANTLR string literal found in a grammar file to an + * equivalent string literal in the target language. + *</p> + * <p> + * For Java, this is the translation {@code 'a\n"'} → {@code "a\n\""}. + * Expect single quotes around the incoming literal. Just flip the quotes + * and replace double quotes with {@code \"}. + * </p> + * <p> + * Note that we have decided to allow people to use '\"' without penalty, so + * we must build the target string in a loop as {@link String#replace} + * cannot handle both {@code \"} and {@code "} without a lot of messing + * around. + * </p> + */ + public String getTargetStringLiteralFromANTLRStringLiteral( + CodeGenerator generator, + String literal, + boolean addQuotes) + { + StringBuilder sb = new StringBuilder(); + String is = literal; + + if ( addQuotes ) sb.append('"'); + + for (int i = 1; i < is.length() -1; i++) { + if (is.charAt(i) == '\\') { + // Anything escaped is what it is! We assume that + // people know how to escape characters correctly. However + // we catch anything that does not need an escape in Java (which + // is what the default implementation is dealing with and remove + // the escape. The C target does this for instance. + // + switch (is.charAt(i+1)) { + // Pass through any escapes that Java also needs + // + case '"': + case 'n': + case 'r': + case 't': + case 'b': + case 'f': + case '\\': + // Pass the escape through + sb.append('\\'); + break; + + case 'u': // Assume unnnn + // Pass the escape through as double \\ + // so that Java leaves as \u0000 string not char + sb.append('\\'); + sb.append('\\'); + break; + + default: + // Remove the escape by virtue of not adding it here + // Thus \' becomes ' and so on + break; + } + + // Go past the \ character + i++; + } else { + // Characters that don't need \ in ANTLR 'strings' but do in Java + if (is.charAt(i) == '"') { + // We need to escape " in Java + sb.append('\\'); + } + } + // Add in the next character, which may have been escaped + sb.append(is.charAt(i)); + } + + if ( addQuotes ) sb.append('"'); + + return sb.toString(); + } + + /** Assume 16-bit char */ + public String encodeIntAsCharEscape(int v) { + if (v < Character.MIN_VALUE || v > Character.MAX_VALUE) { + throw new IllegalArgumentException(String.format("Cannot encode the specified value: %d", v)); + } + + if (v >= 0 && v < targetCharValueEscape.length && targetCharValueEscape[v] != null) { + return targetCharValueEscape[v]; + } + + if (v >= 0x20 && v < 127 && (!Character.isDigit(v) || v == '8' || v == '9')) { + return String.valueOf((char)v); + } + + if ( v>=0 && v<=127 ) { + String oct = Integer.toOctalString(v); + return "\\"+ oct; + } + + String hex = Integer.toHexString(v|0x10000).substring(1,5); + return "\\u"+hex; + } + + public String getLoopLabel(GrammarAST ast) { + return "loop"+ ast.token.getTokenIndex(); + } + + public String getLoopCounter(GrammarAST ast) { + return "cnt"+ ast.token.getTokenIndex(); + } + + public String getListLabel(String label) { + ST st = getTemplates().getInstanceOf("ListLabelName"); + st.add("label", label); + return st.render(); + } + + public String getRuleFunctionContextStructName(Rule r) { + if ( r.g.isLexer() ) { + return getTemplates().getInstanceOf("LexerRuleContext").render(); + } + return Utils.capitalize(r.name)+getTemplates().getInstanceOf("RuleContextNameSuffix").render(); + } + + public String getAltLabelContextStructName(String label) { + return Utils.capitalize(label)+getTemplates().getInstanceOf("RuleContextNameSuffix").render(); + } + + /** If we know which actual function, we can provide the actual ctx type. + * This will contain implicit labels etc... From outside, though, we + * see only ParserRuleContext unless there are externally visible stuff + * like args, locals, explicit labels, etc... + */ + public String getRuleFunctionContextStructName(RuleFunction function) { + Rule r = function.rule; + if ( r.g.isLexer() ) { + return getTemplates().getInstanceOf("LexerRuleContext").render(); + } + return Utils.capitalize(r.name)+getTemplates().getInstanceOf("RuleContextNameSuffix").render(); + } + + // should be same for all refs to same token like ctx.ID within single rule function + // for literals like 'while', we gen _s<ttype> + public String getImplicitTokenLabel(String tokenName) { + ST st = getTemplates().getInstanceOf("ImplicitTokenLabel"); + int ttype = getCodeGenerator().g.getTokenType(tokenName); + if ( tokenName.startsWith("'") ) { + return "s"+ttype; + } + String text = getTokenTypeAsTargetLabel(getCodeGenerator().g, ttype); + st.add("tokenName", text); + return st.render(); + } + + // x=(A|B) + public String getImplicitSetLabel(String id) { + ST st = getTemplates().getInstanceOf("ImplicitSetLabel"); + st.add("id", id); + return st.render(); + } + + public String getImplicitRuleLabel(String ruleName) { + ST st = getTemplates().getInstanceOf("ImplicitRuleLabel"); + st.add("ruleName", ruleName); + return st.render(); + } + + public String getElementListName(String name) { + ST st = getTemplates().getInstanceOf("ElementListName"); + st.add("elemName", getElementName(name)); + return st.render(); + } + + public String getElementName(String name) { + if (".".equals(name)) { + return "_wild"; + } + + if ( getCodeGenerator().g.getRule(name)!=null ) return name; + int ttype = getCodeGenerator().g.getTokenType(name); + if ( ttype==Token.INVALID_TYPE ) return name; + return getTokenTypeAsTargetLabel(getCodeGenerator().g, ttype); + } + + /** + * Gets the maximum number of 16-bit unsigned integers that can be encoded + * in a single segment of the serialized ATN. + * + * @see SerializedATN#getSegments + * + * @return the serialized ATN segment limit + */ + public int getSerializedATNSegmentLimit() { + return Integer.MAX_VALUE; + } + + /** How many bits should be used to do inline token type tests? Java assumes + * a 64-bit word for bitsets. Must be a valid wordsize for your target like + * 8, 16, 32, 64, etc... + * + * @since 4.5 + */ + public int getInlineTestSetWordSize() { return 64; } + + public boolean grammarSymbolCausesIssueInGeneratedCode(GrammarAST idNode) { + switch (idNode.getParent().getType()) { + case ANTLRParser.ASSIGN: + switch (idNode.getParent().getParent().getType()) { + case ANTLRParser.ELEMENT_OPTIONS: + case ANTLRParser.OPTIONS: + return false; + + default: + break; + } + + break; + + case ANTLRParser.AT: + case ANTLRParser.ELEMENT_OPTIONS: + return false; + + case ANTLRParser.LEXER_ACTION_CALL: + if (idNode.getChildIndex() == 0) { + // first child is the command name which is part of the ANTLR language + return false; + } + + // arguments to the command should be checked + break; + + default: + break; + } + + return visibleGrammarSymbolCausesIssueInGeneratedCode(idNode); + } + + protected abstract boolean visibleGrammarSymbolCausesIssueInGeneratedCode(GrammarAST idNode); + + public boolean templatesExist() { + String groupFileName = CodeGenerator.TEMPLATE_ROOT + "/" + getLanguage() + "/" + getLanguage() + STGroup.GROUP_FILE_EXTENSION; + STGroup result = null; + try { + result = new STGroupFile(groupFileName); + } + catch (IllegalArgumentException iae) { + result = null; + } + return result!=null; + } + + + protected STGroup loadTemplates() { + String groupFileName = CodeGenerator.TEMPLATE_ROOT + "/" + getLanguage() + "/" + getLanguage() + STGroup.GROUP_FILE_EXTENSION; + STGroup result = null; + try { + result = new STGroupFile(groupFileName); + } + catch (IllegalArgumentException iae) { + gen.tool.errMgr.toolError(ErrorType.MISSING_CODE_GEN_TEMPLATES, + iae, + language); + } + if ( result==null ) return null; + result.registerRenderer(Integer.class, new NumberRenderer()); + result.registerRenderer(String.class, new StringRenderer()); + result.setListener(new STErrorListener() { + @Override + public void compileTimeError(STMessage msg) { + reportError(msg); + } + + @Override + public void runTimeError(STMessage msg) { + reportError(msg); + } + + @Override + public void IOError(STMessage msg) { + reportError(msg); + } + + @Override + public void internalError(STMessage msg) { + reportError(msg); + } + + private void reportError(STMessage msg) { + getCodeGenerator().tool.errMgr.toolError(ErrorType.STRING_TEMPLATE_WARNING, msg.cause, msg.toString()); + } + }); + + return result; + } + + /** + * @since 4.3 + */ + public boolean wantsBaseListener() { + return true; + } + + /** + * @since 4.3 + */ + public boolean wantsBaseVisitor() { + return true; + } + + /** + * @since 4.3 + */ + public boolean supportsOverloadedMethods() { + return true; + } +} diff --git a/tool/src/org/antlr/v4/codegen/Wildcard.java b/tool/src/org/antlr/v4/codegen/Wildcard.java new file mode 100644 index 0000000..5cff64e --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/Wildcard.java @@ -0,0 +1,40 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen; + +import org.antlr.v4.codegen.model.MatchToken; +import org.antlr.v4.tool.ast.GrammarAST; + +public class Wildcard extends MatchToken { + public Wildcard(OutputModelFactory factory, GrammarAST ast) { + super(factory, ast); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/Action.java b/tool/src/org/antlr/v4/codegen/model/Action.java new file mode 100644 index 0000000..0b9c692 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/Action.java @@ -0,0 +1,83 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.runtime.CommonToken; +import org.antlr.v4.codegen.ActionTranslator; +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.codegen.model.chunk.ActionChunk; +import org.antlr.v4.codegen.model.chunk.ActionTemplate; +import org.antlr.v4.codegen.model.chunk.ActionText; +import org.antlr.v4.codegen.model.decl.StructDecl; +import org.antlr.v4.parse.ANTLRParser; +import org.antlr.v4.tool.ast.ActionAST; +import org.stringtemplate.v4.ST; + +import java.util.ArrayList; +import java.util.List; + +/** */ +public class Action extends RuleElement { + @ModelElement public List<ActionChunk> chunks; + + public Action(OutputModelFactory factory, ActionAST ast) { + super(factory,ast); + RuleFunction rf = factory.getCurrentRuleFunction(); + if (ast != null) { + chunks = ActionTranslator.translateAction(factory, rf, ast.token, ast); + } + else { + chunks = new ArrayList<ActionChunk>(); + } + //System.out.println("actions="+chunks); + } + + public Action(OutputModelFactory factory, StructDecl ctx, String action) { + super(factory,null); + ActionAST ast = new ActionAST(new CommonToken(ANTLRParser.ACTION, action)); + RuleFunction rf = factory.getCurrentRuleFunction(); + if ( rf!=null ) { // we can translate + ast.resolver = rf.rule; + chunks = ActionTranslator.translateActionChunk(factory, rf, action, ast); + } + else { + chunks = new ArrayList<ActionChunk>(); + chunks.add(new ActionText(ctx, action)); + } + } + + public Action(OutputModelFactory factory, StructDecl ctx, ST actionST) { + super(factory, null); + chunks = new ArrayList<ActionChunk>(); + chunks.add(new ActionTemplate(ctx, actionST)); + } + +} diff --git a/tool/src/org/antlr/v4/codegen/model/AddToLabelList.java b/tool/src/org/antlr/v4/codegen/model/AddToLabelList.java new file mode 100644 index 0000000..066b858 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/AddToLabelList.java @@ -0,0 +1,46 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.codegen.model.decl.Decl; + +/** */ +public class AddToLabelList extends SrcOp { + public Decl label; + public String listName; + + public AddToLabelList(OutputModelFactory factory, String listName, Decl label) { + super(factory); + this.label = label; + this.listName = listName; + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/AltBlock.java b/tool/src/org/antlr/v4/codegen/model/AltBlock.java new file mode 100644 index 0000000..b3459e9 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/AltBlock.java @@ -0,0 +1,51 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.runtime.atn.BlockStartState; +import org.antlr.v4.tool.ast.GrammarAST; + +import java.util.List; + +public class AltBlock extends Choice { +// @ModelElement public ThrowNoViableAlt error; + + public AltBlock(OutputModelFactory factory, + GrammarAST blkOrEbnfRootAST, + List<CodeBlockForAlt> alts) + { + super(factory, blkOrEbnfRootAST, alts); + decision = ((BlockStartState)blkOrEbnfRootAST.atnState).decision; + // interp.predict() throws exception +// this.error = new ThrowNoViableAlt(factory, blkOrEbnfRootAST, null); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/ArgAction.java b/tool/src/org/antlr/v4/codegen/model/ArgAction.java new file mode 100644 index 0000000..38ef4d9 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/ArgAction.java @@ -0,0 +1,43 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.tool.ast.ActionAST; + +public class ArgAction extends Action { + /** Context type of invoked rule */ + public String ctxType; + public ArgAction(OutputModelFactory factory, ActionAST ast, String ctxType) { + super(factory, ast); + this.ctxType = ctxType; + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/BaseListenerFile.java b/tool/src/org/antlr/v4/codegen/model/BaseListenerFile.java new file mode 100644 index 0000000..505d505 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/BaseListenerFile.java @@ -0,0 +1,38 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; + +public class BaseListenerFile extends ListenerFile { + public BaseListenerFile(OutputModelFactory factory, String fileName) { + super(factory, fileName); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/BaseVisitorFile.java b/tool/src/org/antlr/v4/codegen/model/BaseVisitorFile.java new file mode 100644 index 0000000..62e669b --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/BaseVisitorFile.java @@ -0,0 +1,38 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; + +public class BaseVisitorFile extends VisitorFile { + public BaseVisitorFile(OutputModelFactory factory, String fileName) { + super(factory, fileName); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/CaptureNextToken.java b/tool/src/org/antlr/v4/codegen/model/CaptureNextToken.java new file mode 100644 index 0000000..63dd3db --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/CaptureNextToken.java @@ -0,0 +1,41 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; + +public class CaptureNextToken extends SrcOp { + public String varName; + public CaptureNextToken(OutputModelFactory factory, String varName) { + super(factory); + this.varName = varName; + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/CaptureNextTokenType.java b/tool/src/org/antlr/v4/codegen/model/CaptureNextTokenType.java new file mode 100644 index 0000000..ffb4cfc --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/CaptureNextTokenType.java @@ -0,0 +1,42 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; + +/** */ +public class CaptureNextTokenType extends SrcOp { + public String varName; + public CaptureNextTokenType(OutputModelFactory factory, String varName) { + super(factory); + this.varName = varName; + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/Choice.java b/tool/src/org/antlr/v4/codegen/model/Choice.java new file mode 100644 index 0000000..bef2c45 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/Choice.java @@ -0,0 +1,97 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.codegen.model.decl.Decl; +import org.antlr.v4.codegen.model.decl.TokenTypeDecl; +import org.antlr.v4.misc.Utils; +import org.antlr.v4.runtime.misc.IntervalSet; +import org.antlr.v4.tool.ast.GrammarAST; + +import java.util.ArrayList; +import java.util.List; + +/** The class hierarchy underneath SrcOp is pretty deep but makes sense that, + * for example LL1StarBlock is a kind of LL1Loop which is a kind of Choice. + * The problem is it's impossible to figure + * out how to construct one of these deeply nested objects because of the + * long super constructor call chain. Instead, I decided to in-line all of + * this and then look for opportunities to re-factor code into functions. + * It makes sense to use a class hierarchy to share data fields, but I don't + * think it makes sense to factor code using super constructors because + * it has too much work to do. + */ +public abstract class Choice extends RuleElement { + public int decision = -1; + public Decl label; + + @ModelElement public List<CodeBlockForAlt> alts; + @ModelElement public List<SrcOp> preamble = new ArrayList<SrcOp>(); + + public Choice(OutputModelFactory factory, + GrammarAST blkOrEbnfRootAST, + List<CodeBlockForAlt> alts) + { + super(factory, blkOrEbnfRootAST); + this.alts = alts; + } + + public void addPreambleOp(SrcOp op) { + preamble.add(op); + } + + public List<String[]> getAltLookaheadAsStringLists(IntervalSet[] altLookSets) { + List<String[]> altLook = new ArrayList<String[]>(); + for (IntervalSet s : altLookSets) { + altLook.add(factory.getGenerator().getTarget().getTokenTypesAsTargetLabels(factory.getGrammar(), s.toArray())); + } + return altLook; + } + + public TestSetInline addCodeForLookaheadTempVar(IntervalSet look) { + List<SrcOp> testOps = factory.getLL1Test(look, ast); + TestSetInline expr = Utils.find(testOps, TestSetInline.class); + if (expr != null) { + Decl d = new TokenTypeDecl(factory, expr.varName); + factory.getCurrentRuleFunction().addLocalDecl(d); + CaptureNextTokenType nextType = new CaptureNextTokenType(factory,expr.varName); + addPreambleOp(nextType); + } + return expr; + } + + public ThrowNoViableAlt getThrowNoViableAlt(OutputModelFactory factory, + GrammarAST blkAST, + IntervalSet expecting) { + return new ThrowNoViableAlt(factory, blkAST, expecting); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/CodeBlockForAlt.java b/tool/src/org/antlr/v4/codegen/model/CodeBlockForAlt.java new file mode 100644 index 0000000..7c2c8b7 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/CodeBlockForAlt.java @@ -0,0 +1,42 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.codegen.model.decl.CodeBlock; + +/** Contains Rewrite block (usually as last op) */ +public class CodeBlockForAlt extends CodeBlock { + + public CodeBlockForAlt(OutputModelFactory factory) { + super(factory); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/CodeBlockForOuterMostAlt.java b/tool/src/org/antlr/v4/codegen/model/CodeBlockForOuterMostAlt.java new file mode 100644 index 0000000..856d15a --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/CodeBlockForOuterMostAlt.java @@ -0,0 +1,54 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.tool.Alternative; + +/** The code associated with the outermost alternative of a rule. + * Sometimes we might want to treat them differently in the + * code generation. + */ +public class CodeBlockForOuterMostAlt extends CodeBlockForAlt { + /** + * The label for the alternative; or null if the alternative is not labeled. + */ + public String altLabel; + /** + * The alternative. + */ + public Alternative alt; + + public CodeBlockForOuterMostAlt(OutputModelFactory factory, Alternative alt) { + super(factory); + this.alt = alt; + altLabel = alt.ast.altLabel!=null ? alt.ast.altLabel.getText() : null; + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/DispatchMethod.java b/tool/src/org/antlr/v4/codegen/model/DispatchMethod.java new file mode 100644 index 0000000..ab119f6 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/DispatchMethod.java @@ -0,0 +1,38 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; + +public class DispatchMethod extends OutputModelObject { + public DispatchMethod(OutputModelFactory factory) { + super(factory); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/ElementFrequenciesVisitor.java b/tool/src/org/antlr/v4/codegen/model/ElementFrequenciesVisitor.java new file mode 100644 index 0000000..2226d37 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/ElementFrequenciesVisitor.java @@ -0,0 +1,166 @@ +package org.antlr.v4.codegen.model; + +import org.antlr.runtime.tree.TreeNodeStream; +import org.antlr.v4.misc.FrequencySet; +import org.antlr.v4.misc.MutableInt; +import org.antlr.v4.parse.GrammarTreeVisitor; +import org.antlr.v4.tool.ErrorManager; +import org.antlr.v4.tool.ast.ActionAST; +import org.antlr.v4.tool.ast.AltAST; +import org.antlr.v4.tool.ast.GrammarAST; +import org.antlr.v4.tool.ast.TerminalAST; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.Map; + +public class ElementFrequenciesVisitor extends GrammarTreeVisitor { + final Deque<FrequencySet<String>> frequencies; + + public ElementFrequenciesVisitor(TreeNodeStream input) { + super(input); + frequencies = new ArrayDeque<FrequencySet<String>>(); + frequencies.push(new FrequencySet<String>()); + } + + /** During code gen, we can assume tree is in good shape */ + @Override + public ErrorManager getErrorManager() { return super.getErrorManager(); } + + /* + * Common + */ + + /** + * Generate a frequency set as the union of two input sets. If an + * element is contained in both sets, the value for the output will be + * the maximum of the two input values. + * + * @param a The first set. + * @param b The second set. + * @return The union of the two sets, with the maximum value chosen + * whenever both sets contain the same key. + */ + protected static FrequencySet<String> combineMax(FrequencySet<String> a, FrequencySet<String> b) { + FrequencySet<String> result = combineAndClip(a, b, 1); + for (Map.Entry<String, MutableInt> entry : a.entrySet()) { + result.get(entry.getKey()).v = entry.getValue().v; + } + + for (Map.Entry<String, MutableInt> entry : b.entrySet()) { + MutableInt slot = result.get(entry.getKey()); + slot.v = Math.max(slot.v, entry.getValue().v); + } + + return result; + } + + /** + * Generate a frequency set as the union of two input sets, with the + * values clipped to a specified maximum value. If an element is + * contained in both sets, the value for the output, prior to clipping, + * will be the sum of the two input values. + * + * @param a The first set. + * @param b The second set. + * @param clip The maximum value to allow for any output. + * @return The sum of the two sets, with the individual elements clipped + * to the maximum value gived by {@code clip}. + */ + protected static FrequencySet<String> combineAndClip(FrequencySet<String> a, FrequencySet<String> b, int clip) { + FrequencySet<String> result = new FrequencySet<String>(); + for (Map.Entry<String, MutableInt> entry : a.entrySet()) { + for (int i = 0; i < entry.getValue().v; i++) { + result.add(entry.getKey()); + } + } + + for (Map.Entry<String, MutableInt> entry : b.entrySet()) { + for (int i = 0; i < entry.getValue().v; i++) { + result.add(entry.getKey()); + } + } + + for (Map.Entry<String, MutableInt> entry : result.entrySet()) { + entry.getValue().v = Math.min(entry.getValue().v, clip); + } + + return result; + } + + @Override + public void tokenRef(TerminalAST ref) { + frequencies.peek().add(ref.getText()); + } + + @Override + public void ruleRef(GrammarAST ref, ActionAST arg) { + frequencies.peek().add(ref.getText()); + } + + /* + * Parser rules + */ + + @Override + protected void enterAlternative(AltAST tree) { + frequencies.push(new FrequencySet<String>()); + } + + @Override + protected void exitAlternative(AltAST tree) { + frequencies.push(combineMax(frequencies.pop(), frequencies.pop())); + } + + @Override + protected void enterElement(GrammarAST tree) { + frequencies.push(new FrequencySet<String>()); + } + + @Override + protected void exitElement(GrammarAST tree) { + frequencies.push(combineAndClip(frequencies.pop(), frequencies.pop(), 2)); + } + + @Override + protected void exitSubrule(GrammarAST tree) { + if (tree.getType() == CLOSURE || tree.getType() == POSITIVE_CLOSURE) { + for (Map.Entry<String, MutableInt> entry : frequencies.peek().entrySet()) { + entry.getValue().v = 2; + } + } + } + + /* + * Lexer rules + */ + + @Override + protected void enterLexerAlternative(GrammarAST tree) { + frequencies.push(new FrequencySet<String>()); + } + + @Override + protected void exitLexerAlternative(GrammarAST tree) { + frequencies.push(combineMax(frequencies.pop(), frequencies.pop())); + } + + @Override + protected void enterLexerElement(GrammarAST tree) { + frequencies.push(new FrequencySet<String>()); + } + + @Override + protected void exitLexerElement(GrammarAST tree) { + frequencies.push(combineAndClip(frequencies.pop(), frequencies.pop(), 2)); + } + + @Override + protected void exitLexerSubrule(GrammarAST tree) { + if (tree.getType() == CLOSURE || tree.getType() == POSITIVE_CLOSURE) { + for (Map.Entry<String, MutableInt> entry : frequencies.peek().entrySet()) { + entry.getValue().v = 2; + } + } + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/ExceptionClause.java b/tool/src/org/antlr/v4/codegen/model/ExceptionClause.java new file mode 100644 index 0000000..3009a7d --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/ExceptionClause.java @@ -0,0 +1,48 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.tool.ast.ActionAST; + +public class ExceptionClause extends SrcOp { + @ModelElement public Action catchArg; + @ModelElement public Action catchAction; + + public ExceptionClause(OutputModelFactory factory, + ActionAST catchArg, + ActionAST catchAction) + { + super(factory, catchArg); + this.catchArg = new Action(factory, catchArg); + this.catchAction = new Action(factory, catchAction); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/InvokeRule.java b/tool/src/org/antlr/v4/codegen/model/InvokeRule.java new file mode 100644 index 0000000..70c9f29 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/InvokeRule.java @@ -0,0 +1,105 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.ActionTranslator; +import org.antlr.v4.codegen.CodeGenerator; +import org.antlr.v4.codegen.ParserFactory; +import org.antlr.v4.codegen.model.chunk.ActionChunk; +import org.antlr.v4.codegen.model.decl.Decl; +import org.antlr.v4.codegen.model.decl.RuleContextDecl; +import org.antlr.v4.codegen.model.decl.RuleContextListDecl; +import org.antlr.v4.parse.ANTLRParser; +import org.antlr.v4.runtime.atn.RuleTransition; +import org.antlr.v4.runtime.misc.OrderedHashSet; +import org.antlr.v4.tool.Rule; +import org.antlr.v4.tool.ast.ActionAST; +import org.antlr.v4.tool.ast.GrammarAST; + +import java.util.List; + +/** */ +public class InvokeRule extends RuleElement implements LabeledOp { + public String name; + public OrderedHashSet<Decl> labels = new OrderedHashSet<Decl>(); // TODO: should need just 1 + public String ctxName; + + @ModelElement public List<ActionChunk> argExprsChunks; + + public InvokeRule(ParserFactory factory, GrammarAST ast, GrammarAST labelAST) { + super(factory, ast); + if ( ast.atnState!=null ) { + RuleTransition ruleTrans = (RuleTransition)ast.atnState.transition(0); + stateNumber = ast.atnState.stateNumber; + } + + this.name = ast.getText(); + CodeGenerator gen = factory.getGenerator(); + Rule r = factory.getGrammar().getRule(name); + ctxName = gen.getTarget().getRuleFunctionContextStructName(r); + + // TODO: move to factory + RuleFunction rf = factory.getCurrentRuleFunction(); + if ( labelAST!=null ) { + // for x=r, define <rule-context-type> x and list_x + String label = labelAST.getText(); + if ( labelAST.parent.getType() == ANTLRParser.PLUS_ASSIGN ) { + factory.defineImplicitLabel(ast, this); + String listLabel = gen.getTarget().getListLabel(label); + RuleContextListDecl l = new RuleContextListDecl(factory, listLabel, ctxName); + rf.addContextDecl(ast.getAltLabel(), l); + } + else { + RuleContextDecl d = new RuleContextDecl(factory,label,ctxName); + labels.add(d); + rf.addContextDecl(ast.getAltLabel(), d); + } + } + + ActionAST arg = (ActionAST)ast.getFirstChildWithType(ANTLRParser.ARG_ACTION); + if ( arg != null ) { + argExprsChunks = ActionTranslator.translateAction(factory, rf, arg.token, arg); + } + + // If action refs rule as rulename not label, we need to define implicit label + if ( factory.getCurrentOuterMostAlt().ruleRefsInActions.containsKey(ast.getText()) ) { + String label = gen.getTarget().getImplicitRuleLabel(ast.getText()); + RuleContextDecl d = new RuleContextDecl(factory,label,ctxName); + labels.add(d); + rf.addContextDecl(ast.getAltLabel(), d); + } + } + + @Override + public List<Decl> getLabels() { + return labels.elements(); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/LL1AltBlock.java b/tool/src/org/antlr/v4/codegen/model/LL1AltBlock.java new file mode 100644 index 0000000..1bac9a4 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/LL1AltBlock.java @@ -0,0 +1,53 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.runtime.atn.DecisionState; +import org.antlr.v4.runtime.misc.IntervalSet; +import org.antlr.v4.tool.ast.GrammarAST; + +import java.util.List; + +/** (A | B | C) */ +public class LL1AltBlock extends LL1Choice { + public LL1AltBlock(OutputModelFactory factory, GrammarAST blkAST, List<CodeBlockForAlt> alts) { + super(factory, blkAST, alts); + this.decision = ((DecisionState)blkAST.atnState).decision; + + /** Lookahead for each alt 1..n */ + IntervalSet[] altLookSets = factory.getGrammar().decisionLOOK.get(decision); + altLook = getAltLookaheadAsStringLists(altLookSets); + + IntervalSet expecting = IntervalSet.or(altLookSets); // combine alt sets + this.error = getThrowNoViableAlt(factory, blkAST, expecting); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/LL1Choice.java b/tool/src/org/antlr/v4/codegen/model/LL1Choice.java new file mode 100644 index 0000000..3153ad9 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/LL1Choice.java @@ -0,0 +1,48 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.tool.ast.GrammarAST; + +import java.util.List; + +public abstract class LL1Choice extends Choice { + /** Token names for each alt 0..n-1 */ + public List<String[]> altLook; + @ModelElement public ThrowNoViableAlt error; + + public LL1Choice(OutputModelFactory factory, GrammarAST blkAST, + List<CodeBlockForAlt> alts) + { + super(factory, blkAST, alts); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/LL1Loop.java b/tool/src/org/antlr/v4/codegen/model/LL1Loop.java new file mode 100644 index 0000000..6cd0779 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/LL1Loop.java @@ -0,0 +1,71 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.runtime.misc.IntervalSet; +import org.antlr.v4.tool.ast.GrammarAST; + +import java.util.ArrayList; +import java.util.List; + +/** */ +public abstract class LL1Loop extends Choice { + /** The state associated wih the (A|B|...) block not loopback, which + * is super.stateNumber + */ + public int blockStartStateNumber; + public int loopBackStateNumber; + + @ModelElement public OutputModelObject loopExpr; + @ModelElement public List<SrcOp> iteration; + + public LL1Loop(OutputModelFactory factory, + GrammarAST blkAST, + List<CodeBlockForAlt> alts) + { + super(factory, blkAST, alts); + } + + public void addIterationOp(SrcOp op) { + if ( iteration==null ) iteration = new ArrayList<SrcOp>(); + iteration.add(op); + } + + public SrcOp addCodeForLoopLookaheadTempVar(IntervalSet look) { + TestSetInline expr = addCodeForLookaheadTempVar(look); + if (expr != null) { + CaptureNextTokenType nextType = new CaptureNextTokenType(factory, expr.varName); + addIterationOp(nextType); + } + return expr; + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/LL1OptionalBlock.java b/tool/src/org/antlr/v4/codegen/model/LL1OptionalBlock.java new file mode 100644 index 0000000..74ca312 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/LL1OptionalBlock.java @@ -0,0 +1,47 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.tool.ast.GrammarAST; + +import java.util.List; + +/** An optional block is just an alternative block where the last alternative + * is epsilon. The analysis takes care of adding to the empty alternative. + * + * (A | B | C)? + */ +public class LL1OptionalBlock extends LL1AltBlock { + public LL1OptionalBlock(OutputModelFactory factory, GrammarAST blkAST, List<CodeBlockForAlt> alts) { + super(factory, blkAST, alts); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/LL1OptionalBlockSingleAlt.java b/tool/src/org/antlr/v4/codegen/model/LL1OptionalBlockSingleAlt.java new file mode 100644 index 0000000..eacfd5a --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/LL1OptionalBlockSingleAlt.java @@ -0,0 +1,65 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.runtime.atn.DecisionState; +import org.antlr.v4.runtime.misc.IntervalSet; +import org.antlr.v4.tool.ast.GrammarAST; + +import java.util.List; + +/** (A B C)? */ +public class LL1OptionalBlockSingleAlt extends LL1Choice { + @ModelElement public SrcOp expr; + @ModelElement public List<SrcOp> followExpr; // might not work in template if size>1 + + public LL1OptionalBlockSingleAlt(OutputModelFactory factory, + GrammarAST blkAST, + List<CodeBlockForAlt> alts) + { + super(factory, blkAST, alts); + this.decision = ((DecisionState)blkAST.atnState).decision; + + /** Lookahead for each alt 1..n */ +// IntervalSet[] altLookSets = LinearApproximator.getLL1LookaheadSets(dfa); + IntervalSet[] altLookSets = factory.getGrammar().decisionLOOK.get(decision); + altLook = getAltLookaheadAsStringLists(altLookSets); + IntervalSet look = altLookSets[0]; + IntervalSet followLook = altLookSets[1]; + + IntervalSet expecting = look.or(followLook); + this.error = getThrowNoViableAlt(factory, blkAST, expecting); + + expr = addCodeForLookaheadTempVar(look); + followExpr = factory.getLL1Test(followLook, blkAST); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/LL1PlusBlockSingleAlt.java b/tool/src/org/antlr/v4/codegen/model/LL1PlusBlockSingleAlt.java new file mode 100644 index 0000000..a7076ce --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/LL1PlusBlockSingleAlt.java @@ -0,0 +1,58 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.runtime.atn.PlusBlockStartState; +import org.antlr.v4.runtime.misc.IntervalSet; +import org.antlr.v4.tool.ast.BlockAST; +import org.antlr.v4.tool.ast.GrammarAST; + +import java.util.List; + +/** */ +public class LL1PlusBlockSingleAlt extends LL1Loop { + public LL1PlusBlockSingleAlt(OutputModelFactory factory, GrammarAST plusRoot, List<CodeBlockForAlt> alts) { + super(factory, plusRoot, alts); + + BlockAST blkAST = (BlockAST)plusRoot.getChild(0); + PlusBlockStartState blkStart = (PlusBlockStartState)blkAST.atnState; + + stateNumber = blkStart.loopBackState.stateNumber; + blockStartStateNumber = blkStart.stateNumber; + PlusBlockStartState plus = (PlusBlockStartState)blkAST.atnState; + this.decision = plus.loopBackState.decision; + IntervalSet[] altLookSets = factory.getGrammar().decisionLOOK.get(decision); + + IntervalSet loopBackLook = altLookSets[0]; + loopExpr = addCodeForLoopLookaheadTempVar(loopBackLook); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/LL1StarBlockSingleAlt.java b/tool/src/org/antlr/v4/codegen/model/LL1StarBlockSingleAlt.java new file mode 100644 index 0000000..4c1bf94 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/LL1StarBlockSingleAlt.java @@ -0,0 +1,54 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.runtime.atn.StarLoopEntryState; +import org.antlr.v4.runtime.misc.IntervalSet; +import org.antlr.v4.tool.ast.GrammarAST; + +import java.util.List; + +/** */ +public class LL1StarBlockSingleAlt extends LL1Loop { + public LL1StarBlockSingleAlt(OutputModelFactory factory, GrammarAST starRoot, List<CodeBlockForAlt> alts) { + super(factory, starRoot, alts); + + StarLoopEntryState star = (StarLoopEntryState)starRoot.atnState; + loopBackStateNumber = star.loopBackState.stateNumber; + this.decision = star.decision; + IntervalSet[] altLookSets = factory.getGrammar().decisionLOOK.get(decision); + assert altLookSets.length == 2; + IntervalSet enterLook = altLookSets[0]; + IntervalSet exitLook = altLookSets[1]; + loopExpr = addCodeForLoopLookaheadTempVar(enterLook); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/LabeledOp.java b/tool/src/org/antlr/v4/codegen/model/LabeledOp.java new file mode 100644 index 0000000..26ad2d3 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/LabeledOp.java @@ -0,0 +1,40 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.model.decl.Decl; + +import java.util.List; + +/** All the rule elements we can label like tokens, rules, sets, wildcard. */ +public interface LabeledOp { + public List<Decl> getLabels(); +} diff --git a/tool/src/org/antlr/v4/codegen/model/LeftRecursiveRuleFunction.java b/tool/src/org/antlr/v4/codegen/model/LeftRecursiveRuleFunction.java new file mode 100644 index 0000000..adbc3a6 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/LeftRecursiveRuleFunction.java @@ -0,0 +1,76 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.CodeGenerator; +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.codegen.model.decl.RuleContextDecl; +import org.antlr.v4.codegen.model.decl.RuleContextListDecl; +import org.antlr.v4.codegen.model.decl.StructDecl; +import org.antlr.v4.parse.ANTLRParser; +import org.antlr.v4.runtime.misc.Pair; +import org.antlr.v4.tool.LeftRecursiveRule; +import org.antlr.v4.tool.Rule; +import org.antlr.v4.tool.ast.GrammarAST; + +public class LeftRecursiveRuleFunction extends RuleFunction { + public LeftRecursiveRuleFunction(OutputModelFactory factory, LeftRecursiveRule r) { + super(factory, r); + + CodeGenerator gen = factory.getGenerator(); + // Since we delete x=lr, we have to manually add decls for all labels + // on left-recur refs to proper structs + for (Pair<GrammarAST,String> pair : r.leftRecursiveRuleRefLabels) { + GrammarAST idAST = pair.a; + String altLabel = pair.b; + String label = idAST.getText(); + GrammarAST rrefAST = (GrammarAST)idAST.getParent().getChild(1); + if ( rrefAST.getType() == ANTLRParser.RULE_REF ) { + Rule targetRule = factory.getGrammar().getRule(rrefAST.getText()); + String ctxName = gen.getTarget().getRuleFunctionContextStructName(targetRule); + RuleContextDecl d; + if (idAST.getParent().getType() == ANTLRParser.ASSIGN) { + d = new RuleContextDecl(factory, label, ctxName); + } + else { + d = new RuleContextListDecl(factory, label, ctxName); + } + + StructDecl struct = ruleCtx; + if ( altLabelCtxs!=null ) { + StructDecl s = altLabelCtxs.get(altLabel); + if ( s!=null ) struct = s; // if alt label, use subctx + } + struct.addDecl(d); // stick in overall rule's ctx + } + } + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/Lexer.java b/tool/src/org/antlr/v4/codegen/model/Lexer.java new file mode 100644 index 0000000..ef44e4c --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/Lexer.java @@ -0,0 +1,58 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.tool.Grammar; +import org.antlr.v4.tool.LexerGrammar; +import org.antlr.v4.tool.Rule; + +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.Map; + +public class Lexer extends Recognizer { + public Map<String,Integer> channels; + public LexerFile file; + public Collection<String> modes; + + @ModelElement public LinkedHashMap<Rule, RuleActionFunction> actionFuncs = + new LinkedHashMap<Rule, RuleActionFunction>(); + + public Lexer(OutputModelFactory factory, LexerFile file) { + super(factory); + this.file = file; // who contains us? + + Grammar g = factory.getGrammar(); + channels = new LinkedHashMap<String, Integer>(g.channelNameToValueMap); + modes = ((LexerGrammar)g).modes.keySet(); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/LexerFile.java b/tool/src/org/antlr/v4/codegen/model/LexerFile.java new file mode 100644 index 0000000..8bec073 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/LexerFile.java @@ -0,0 +1,55 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.tool.Grammar; +import org.antlr.v4.tool.ast.ActionAST; + +import java.util.HashMap; +import java.util.Map; + +public class LexerFile extends OutputFile { + public String genPackage; // from -package cmd-line + @ModelElement public Lexer lexer; + @ModelElement public Map<String, Action> namedActions; + + public LexerFile(OutputModelFactory factory, String fileName) { + super(factory, fileName); + namedActions = new HashMap<String, Action>(); + Grammar g = factory.getGrammar(); + for (String name : g.namedActions.keySet()) { + ActionAST ast = g.namedActions.get(name); + namedActions.put(name, new Action(factory, ast)); + } + genPackage = factory.getGrammar().tool.genPackage; + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/ListenerDispatchMethod.java b/tool/src/org/antlr/v4/codegen/model/ListenerDispatchMethod.java new file mode 100644 index 0000000..9332d13 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/ListenerDispatchMethod.java @@ -0,0 +1,41 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; + +public class ListenerDispatchMethod extends DispatchMethod { + public boolean isEnter; + + public ListenerDispatchMethod(OutputModelFactory factory, boolean isEnter) { + super(factory); + this.isEnter = isEnter; + } +} 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 0000000..99f6ebf --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/ListenerFile.java @@ -0,0 +1,87 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.runtime.misc.Pair; +import org.antlr.v4.tool.Grammar; +import org.antlr.v4.tool.Rule; +import org.antlr.v4.tool.ast.ActionAST; +import org.antlr.v4.tool.ast.AltAST; + +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** 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 OutputFile { + public String genPackage; // from -package cmd-line + public String grammarName; + public String parserName; + /** + * The names of all listener contexts. + */ + public Set<String> listenerNames = new LinkedHashSet<String>(); + /** + * For listener contexts created for a labeled outer alternative, maps from + * a listener context name to the name of the rule which defines the + * context. + */ + public Map<String, String> listenerLabelRuleNames = new LinkedHashMap<String, String>(); + + @ModelElement public Action header; + + public ListenerFile(OutputModelFactory factory, String fileName) { + super(factory, fileName); + Grammar g = factory.getGrammar(); + parserName = g.getRecognizerName(); + grammarName = g.name; + for (Rule r : g.rules.values()) { + Map<String, List<Pair<Integer,AltAST>>> labels = r.getAltLabels(); + if ( labels!=null ) { + for (Map.Entry<String, List<Pair<Integer, AltAST>>> pair : labels.entrySet()) { + listenerNames.add(pair.getKey()); + listenerLabelRuleNames.put(pair.getKey(), r.name); + } + } + else { + // only add rule context if no labels + listenerNames.add(r.name); + } + } + ActionAST ast = g.namedActions.get("header"); + if ( ast!=null ) header = new Action(factory, ast); + genPackage = factory.getGrammar().tool.genPackage; + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/Loop.java b/tool/src/org/antlr/v4/codegen/model/Loop.java new file mode 100644 index 0000000..50fbd6f --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/Loop.java @@ -0,0 +1,60 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.tool.ast.GrammarAST; +import org.antlr.v4.tool.ast.QuantifierAST; + +import java.util.ArrayList; +import java.util.List; + +public class Loop extends Choice { + public int blockStartStateNumber; + public int loopBackStateNumber; + public final int exitAlt; + + @ModelElement public List<SrcOp> iteration; + + public Loop(OutputModelFactory factory, + GrammarAST blkOrEbnfRootAST, + List<CodeBlockForAlt> alts) + { + super(factory, blkOrEbnfRootAST, alts); + boolean nongreedy = (blkOrEbnfRootAST instanceof QuantifierAST) && !((QuantifierAST)blkOrEbnfRootAST).isGreedy(); + exitAlt = nongreedy ? 1 : alts.size() + 1; + } + + public void addIterationOp(SrcOp op) { + if ( iteration==null ) iteration = new ArrayList<SrcOp>(); + iteration.add(op); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/MatchNotSet.java b/tool/src/org/antlr/v4/codegen/model/MatchNotSet.java new file mode 100644 index 0000000..8df741c --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/MatchNotSet.java @@ -0,0 +1,41 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.tool.ast.GrammarAST; + +public class MatchNotSet extends MatchSet { + public String varName = "_la"; + public MatchNotSet(OutputModelFactory factory, GrammarAST ast) { + super(factory, ast); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/MatchSet.java b/tool/src/org/antlr/v4/codegen/model/MatchSet.java new file mode 100644 index 0000000..e6d02a1 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/MatchSet.java @@ -0,0 +1,52 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.codegen.model.decl.Decl; +import org.antlr.v4.codegen.model.decl.TokenTypeDecl; +import org.antlr.v4.runtime.atn.SetTransition; +import org.antlr.v4.tool.ast.GrammarAST; + +public class MatchSet extends MatchToken { + @ModelElement public TestSetInline expr; + @ModelElement public CaptureNextTokenType capture; + + public MatchSet(OutputModelFactory factory, GrammarAST ast) { + super(factory, ast); + SetTransition st = (SetTransition)ast.atnState.transition(0); + int wordSize = factory.getGenerator().getTarget().getInlineTestSetWordSize(); + expr = new TestSetInline(factory, null, st.set, wordSize); + Decl d = new TokenTypeDecl(factory, expr.varName); + factory.getCurrentRuleFunction().addLocalDecl(d); + capture = new CaptureNextTokenType(factory,expr.varName); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/MatchToken.java b/tool/src/org/antlr/v4/codegen/model/MatchToken.java new file mode 100644 index 0000000..8405e78 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/MatchToken.java @@ -0,0 +1,63 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.CodeGenerator; +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.codegen.model.decl.Decl; +import org.antlr.v4.tool.Grammar; +import org.antlr.v4.tool.ast.GrammarAST; +import org.antlr.v4.tool.ast.TerminalAST; + +import java.util.ArrayList; +import java.util.List; + +/** */ +public class MatchToken extends RuleElement implements LabeledOp { + public String name; + public int ttype; + public List<Decl> labels = new ArrayList<Decl>(); + + public MatchToken(OutputModelFactory factory, TerminalAST ast) { + super(factory, ast); + Grammar g = factory.getGrammar(); + CodeGenerator gen = factory.getGenerator(); + ttype = g.getTokenType(ast.getText()); + name = gen.getTarget().getTokenTypeAsTargetLabel(g, ttype); + } + + public MatchToken(OutputModelFactory factory, GrammarAST ast) { + super(factory, ast); + } + + @Override + public List<Decl> getLabels() { return labels; } +} diff --git a/tool/src/org/antlr/v4/codegen/model/ModelElement.java b/tool/src/org/antlr/v4/codegen/model/ModelElement.java new file mode 100644 index 0000000..0dc38c2 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/ModelElement.java @@ -0,0 +1,41 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** Indicate field of OutputModelObject is an element to be walked by + * OutputModelWalker. + */ +@Retention(RetentionPolicy.RUNTIME) +public @interface ModelElement { +} diff --git a/tool/src/org/antlr/v4/codegen/model/OptionalBlock.java b/tool/src/org/antlr/v4/codegen/model/OptionalBlock.java new file mode 100644 index 0000000..c1f2cb3 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/OptionalBlock.java @@ -0,0 +1,46 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.tool.ast.GrammarAST; + +import java.util.List; + +/** */ +public class OptionalBlock extends AltBlock { + public OptionalBlock(OutputModelFactory factory, + GrammarAST questionAST, + List<CodeBlockForAlt> alts) + { + super(factory, questionAST, alts); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/OutputFile.java b/tool/src/org/antlr/v4/codegen/model/OutputFile.java new file mode 100644 index 0000000..16b2384 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/OutputFile.java @@ -0,0 +1,53 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.Tool; +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.tool.Grammar; + +public abstract class OutputFile extends OutputModelObject { + public final String fileName; + public final String grammarFileName; + public final String ANTLRVersion; + public final String TokenLabelType; + public final String InputSymbolType; + + public OutputFile(OutputModelFactory factory, String fileName) { + super(factory); + this.fileName = fileName; + Grammar g = factory.getGrammar(); + grammarFileName = g.fileName; + ANTLRVersion = Tool.VERSION; + TokenLabelType = g.getOptionString("TokenLabelType"); + InputSymbolType = TokenLabelType; + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/OutputModelObject.java b/tool/src/org/antlr/v4/codegen/model/OutputModelObject.java new file mode 100644 index 0000000..6f793be --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/OutputModelObject.java @@ -0,0 +1,49 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.tool.ast.GrammarAST; + +/** */ +public abstract class OutputModelObject { + public OutputModelFactory factory; + public GrammarAST ast; + + public OutputModelObject() {} + + public OutputModelObject(OutputModelFactory factory) { this(factory, null); } + + public OutputModelObject(OutputModelFactory factory, GrammarAST ast) { + this.factory = factory; + this.ast = ast; + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/Parser.java b/tool/src/org/antlr/v4/codegen/model/Parser.java new file mode 100644 index 0000000..770d904 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/Parser.java @@ -0,0 +1,47 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; + +import java.util.ArrayList; +import java.util.List; + +public class Parser extends Recognizer { + public ParserFile file; + + @ModelElement public List<RuleFunction> funcs = new ArrayList<RuleFunction>(); + + public Parser(OutputModelFactory factory, ParserFile file) { + super(factory); + this.file = file; // who contains us? + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/ParserFile.java b/tool/src/org/antlr/v4/codegen/model/ParserFile.java new file mode 100644 index 0000000..28ef200 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/ParserFile.java @@ -0,0 +1,63 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.tool.Grammar; +import org.antlr.v4.tool.ast.ActionAST; + +import java.util.HashMap; +import java.util.Map; + +/** */ +public class ParserFile extends OutputFile { + public String genPackage; // from -package cmd-line + @ModelElement public Parser parser; + @ModelElement public Map<String, Action> namedActions; + public Boolean genListener = false; + public Boolean genVisitor = false; + public String grammarName; + + public ParserFile(OutputModelFactory factory, String fileName) { + super(factory, fileName); + Grammar g = factory.getGrammar(); + namedActions = new HashMap<String, Action>(); + for (String name : g.namedActions.keySet()) { + ActionAST ast = g.namedActions.get(name); + namedActions.put(name, new Action(factory, ast)); + } + genPackage = g.tool.genPackage; + // need the below members in the ST for Python + genListener = g.tool.gen_listener; + genVisitor = g.tool.gen_visitor; + grammarName = g.name; + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/PlusBlock.java b/tool/src/org/antlr/v4/codegen/model/PlusBlock.java new file mode 100644 index 0000000..a200d30 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/PlusBlock.java @@ -0,0 +1,58 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.runtime.atn.PlusBlockStartState; +import org.antlr.v4.runtime.atn.PlusLoopbackState; +import org.antlr.v4.tool.ast.BlockAST; +import org.antlr.v4.tool.ast.GrammarAST; + +import java.util.List; + +public class PlusBlock extends Loop { + @ModelElement public ThrowNoViableAlt error; + + public PlusBlock(OutputModelFactory factory, + GrammarAST plusRoot, + List<CodeBlockForAlt> alts) + { + super(factory, plusRoot, alts); + BlockAST blkAST = (BlockAST)plusRoot.getChild(0); + PlusBlockStartState blkStart = (PlusBlockStartState)blkAST.atnState; + PlusLoopbackState loop = blkStart.loopBackState; + stateNumber = blkStart.loopBackState.stateNumber; + blockStartStateNumber = blkStart.stateNumber; + loopBackStateNumber = loop.stateNumber; + this.error = getThrowNoViableAlt(factory, plusRoot, null); + decision = loop.decision; + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/Recognizer.java b/tool/src/org/antlr/v4/codegen/model/Recognizer.java new file mode 100644 index 0000000..7e3f95b --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/Recognizer.java @@ -0,0 +1,135 @@ +/* + * [The "BSD license"] + * Copyright (c) 2014 Terence Parr + * Copyright (c) 2014 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.CodeGenerator; +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.codegen.model.chunk.ActionChunk; +import org.antlr.v4.codegen.model.chunk.ActionText; +import org.antlr.v4.tool.Grammar; +import org.antlr.v4.tool.Rule; + +import java.io.File; +import java.util.Arrays; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +public abstract class Recognizer extends OutputModelObject { + public String name; + public String grammarName; + public String grammarFileName; + public Map<String,Integer> tokens; + + /** + * @deprecated This field is provided only for compatibility with code + * generation targets which have not yet been updated to use + * {@link #literalNames} and {@link #symbolicNames}. + */ + @Deprecated + public String[] tokenNames; + + public String[] literalNames; + public String[] symbolicNames; + public Set<String> ruleNames; + public Collection<Rule> rules; + @ModelElement public ActionChunk superClass; + + @ModelElement public SerializedATN atn; + @ModelElement public LinkedHashMap<Rule, RuleSempredFunction> sempredFuncs = + new LinkedHashMap<Rule, RuleSempredFunction>(); + + public Recognizer(OutputModelFactory factory) { + super(factory); + + Grammar g = factory.getGrammar(); + grammarFileName = new File(g.fileName).getName(); + grammarName = g.name; + name = g.getRecognizerName(); + tokens = new LinkedHashMap<String,Integer>(); + for (Map.Entry<String, Integer> entry : g.tokenNameToTypeMap.entrySet()) { + Integer ttype = entry.getValue(); + if ( ttype>0 ) { + tokens.put(entry.getKey(), ttype); + } + } + + ruleNames = g.rules.keySet(); + rules = g.rules.values(); + atn = new SerializedATN(factory, g.atn); + if (g.getOptionString("superClass") != null) { + superClass = new ActionText(null, g.getOptionString("superClass")); + } + else { + superClass = null; + } + + CodeGenerator gen = factory.getGenerator(); + tokenNames = translateTokenStringsToTarget(g.getTokenDisplayNames(), gen); + literalNames = translateTokenStringsToTarget(g.getTokenLiteralNames(), gen); + symbolicNames = translateTokenStringsToTarget(g.getTokenSymbolicNames(), gen); + } + + protected static String[] translateTokenStringsToTarget(String[] tokenStrings, CodeGenerator gen) { + String[] result = tokenStrings.clone(); + for (int i = 0; i < tokenStrings.length; i++) { + result[i] = translateTokenStringToTarget(tokenStrings[i], gen); + } + + int lastTrueEntry = result.length - 1; + while (lastTrueEntry >= 0 && result[lastTrueEntry] == null) { + lastTrueEntry --; + } + + if (lastTrueEntry < result.length - 1) { + result = Arrays.copyOf(result, lastTrueEntry + 1); + } + + return result; + } + + protected static String translateTokenStringToTarget(String tokenName, CodeGenerator gen) { + if (tokenName == null) { + return null; + } + + if (tokenName.charAt(0) == '\'') { + boolean addQuotes = false; + String targetString = + gen.getTarget().getTargetStringLiteralFromANTLRStringLiteral(gen, tokenName, addQuotes); + return "\"'" + targetString + "'\""; + } + else { + return gen.getTarget().getTargetStringLiteralFromString(tokenName, true); + } + } + +} diff --git a/tool/src/org/antlr/v4/codegen/model/RuleActionFunction.java b/tool/src/org/antlr/v4/codegen/model/RuleActionFunction.java new file mode 100644 index 0000000..8e65462 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/RuleActionFunction.java @@ -0,0 +1,53 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.tool.Rule; + +import java.util.LinkedHashMap; + +public class RuleActionFunction extends OutputModelObject { + public String name; + public String ctxType; + public int ruleIndex; + + /** Map actionIndex to Action */ + @ModelElement public LinkedHashMap<Integer, Action> actions = + new LinkedHashMap<Integer, Action>(); + + public RuleActionFunction(OutputModelFactory factory, Rule r, String ctxType) { + super(factory); + name = r.name; + ruleIndex = r.index; + this.ctxType = ctxType; + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/RuleElement.java b/tool/src/org/antlr/v4/codegen/model/RuleElement.java new file mode 100644 index 0000000..c2b2f52 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/RuleElement.java @@ -0,0 +1,45 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.tool.ast.GrammarAST; + +public class RuleElement extends SrcOp { + /** Associated ATN state for this rule elements (action, token, ruleref, ...) */ + public int stateNumber; + + public RuleElement(OutputModelFactory factory, GrammarAST ast) { + super(factory, ast); + if ( ast != null && ast.atnState!=null ) stateNumber = ast.atnState.stateNumber; + } + +} diff --git a/tool/src/org/antlr/v4/codegen/model/RuleFunction.java b/tool/src/org/antlr/v4/codegen/model/RuleFunction.java new file mode 100644 index 0000000..25d6d0f --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/RuleFunction.java @@ -0,0 +1,290 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.runtime.RecognitionException; +import org.antlr.runtime.tree.CommonTreeNodeStream; +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.codegen.model.decl.AltLabelStructDecl; +import org.antlr.v4.codegen.model.decl.AttributeDecl; +import org.antlr.v4.codegen.model.decl.ContextRuleGetterDecl; +import org.antlr.v4.codegen.model.decl.ContextRuleListGetterDecl; +import org.antlr.v4.codegen.model.decl.ContextRuleListIndexedGetterDecl; +import org.antlr.v4.codegen.model.decl.ContextTokenGetterDecl; +import org.antlr.v4.codegen.model.decl.ContextTokenListGetterDecl; +import org.antlr.v4.codegen.model.decl.ContextTokenListIndexedGetterDecl; +import org.antlr.v4.codegen.model.decl.Decl; +import org.antlr.v4.codegen.model.decl.StructDecl; +import org.antlr.v4.misc.FrequencySet; +import org.antlr.v4.misc.Utils; +import org.antlr.v4.parse.GrammarASTAdaptor; +import org.antlr.v4.runtime.atn.ATNState; +import org.antlr.v4.runtime.misc.IntervalSet; +import org.antlr.v4.runtime.misc.OrderedHashSet; +import org.antlr.v4.runtime.misc.Pair; +import org.antlr.v4.tool.Attribute; +import org.antlr.v4.tool.ErrorType; +import org.antlr.v4.tool.Rule; +import org.antlr.v4.tool.ast.ActionAST; +import org.antlr.v4.tool.ast.AltAST; +import org.antlr.v4.tool.ast.GrammarAST; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.antlr.v4.parse.ANTLRParser.RULE_REF; +import static org.antlr.v4.parse.ANTLRParser.TOKEN_REF; + +/** */ +public class RuleFunction extends OutputModelObject { + public String name; + public List<String> modifiers; + public String ctxType; + public Collection<String> ruleLabels; + public Collection<String> tokenLabels; + public ATNState startState; + public int index; + public Rule rule; + public AltLabelStructDecl[] altToContext; + public boolean hasLookaheadBlock; + + @ModelElement public List<SrcOp> code; + @ModelElement public OrderedHashSet<Decl> locals; // TODO: move into ctx? + @ModelElement public Collection<AttributeDecl> args = null; + @ModelElement public StructDecl ruleCtx; + @ModelElement public Map<String,AltLabelStructDecl> altLabelCtxs; + @ModelElement public Map<String,Action> namedActions; + @ModelElement public Action finallyAction; + @ModelElement public List<ExceptionClause> exceptions; + @ModelElement public List<SrcOp> postamble; + + public RuleFunction(OutputModelFactory factory, Rule r) { + super(factory); + this.name = r.name; + this.rule = r; + if ( r.modifiers!=null && !r.modifiers.isEmpty() ) { + this.modifiers = new ArrayList<String>(); + for (GrammarAST t : r.modifiers) modifiers.add(t.getText()); + } + modifiers = Utils.nodesToStrings(r.modifiers); + + index = r.index; + + ruleCtx = new StructDecl(factory, r); + altToContext = new AltLabelStructDecl[r.getOriginalNumberOfAlts()+1]; + addContextGetters(factory, r); + + if ( r.args!=null ) { + Collection<Attribute> decls = r.args.attributes.values(); + if ( decls.size()>0 ) { + args = new ArrayList<AttributeDecl>(); + ruleCtx.addDecls(decls); + for (Attribute a : decls) { + args.add(new AttributeDecl(factory, a)); + } + ruleCtx.ctorAttrs = args; + } + } + if ( r.retvals!=null ) { + ruleCtx.addDecls(r.retvals.attributes.values()); + } + if ( r.locals!=null ) { + ruleCtx.addDecls(r.locals.attributes.values()); + } + + ruleLabels = r.getElementLabelNames(); + tokenLabels = r.getTokenRefs(); + if ( r.exceptions!=null ) { + exceptions = new ArrayList<ExceptionClause>(); + for (GrammarAST e : r.exceptions) { + ActionAST catchArg = (ActionAST)e.getChild(0); + ActionAST catchAction = (ActionAST)e.getChild(1); + exceptions.add(new ExceptionClause(factory, catchArg, catchAction)); + } + } + + startState = factory.getGrammar().atn.ruleToStartState[r.index]; + } + + public void addContextGetters(OutputModelFactory factory, Rule r) { + // Add ctx labels for elements in alts with no -> label + List<AltAST> altsNoLabels = r.getUnlabeledAltASTs(); + if ( altsNoLabels!=null ) { + Set<Decl> decls = getDeclsForAllElements(altsNoLabels); + // we know to put in rule ctx, so do it directly + for (Decl d : decls) ruleCtx.addDecl(d); + } + + // make structs for -> labeled alts, define ctx labels for elements + altLabelCtxs = new HashMap<String,AltLabelStructDecl>(); + Map<String, List<Pair<Integer, AltAST>>> labels = r.getAltLabels(); + if ( labels!=null ) { + for (Map.Entry<String, List<Pair<Integer, AltAST>>> entry : labels.entrySet()) { + String label = entry.getKey(); + List<AltAST> alts = new ArrayList<AltAST>(); + for (Pair<Integer, AltAST> pair : entry.getValue()) { + alts.add(pair.b); + } + + Set<Decl> decls = getDeclsForAllElements(alts); + for (Pair<Integer, AltAST> pair : entry.getValue()) { + Integer altNum = pair.a; + altToContext[altNum] = new AltLabelStructDecl(factory, r, altNum, label); + if (!altLabelCtxs.containsKey(label)) { + altLabelCtxs.put(label, altToContext[altNum]); + } + + // we know which ctx to put in, so do it directly + for (Decl d : decls) { + altToContext[altNum].addDecl(d); + } + } + } + } + } + + public void fillNamedActions(OutputModelFactory factory, Rule r) { + if ( r.finallyAction!=null ) { + finallyAction = new Action(factory, r.finallyAction); + } + + namedActions = new HashMap<String, Action>(); + for (String name : r.namedActions.keySet()) { + ActionAST ast = r.namedActions.get(name); + namedActions.put(name, new Action(factory, ast)); + } + } + + /** for all alts, find which ref X or r needs List + Must see across alts. If any alt needs X or r as list, then + define as list. + */ + public Set<Decl> getDeclsForAllElements(List<AltAST> altASTs) { + Set<String> needsList = new HashSet<String>(); + List<GrammarAST> allRefs = new ArrayList<GrammarAST>(); + for (AltAST ast : altASTs) { + IntervalSet reftypes = new IntervalSet(RULE_REF, TOKEN_REF); + List<GrammarAST> refs = ast.getNodesWithType(reftypes); + allRefs.addAll(refs); + FrequencySet<String> altFreq = getElementFrequenciesForAlt(ast); + for (GrammarAST t : refs) { + String refLabelName = t.getText(); + if ( altFreq.count(refLabelName)>1 ) { + needsList.add(refLabelName); + } + } + } + Set<Decl> decls = new LinkedHashSet<Decl>(); + for (GrammarAST t : allRefs) { + String refLabelName = t.getText(); + List<Decl> d = getDeclForAltElement(t, + refLabelName, + needsList.contains(refLabelName)); + decls.addAll(d); + } + return decls; + } + + /** Given list of X and r refs in alt, compute how many of each there are */ + protected FrequencySet<String> getElementFrequenciesForAlt(AltAST ast) { + try { + ElementFrequenciesVisitor visitor = new ElementFrequenciesVisitor(new CommonTreeNodeStream(new GrammarASTAdaptor(), ast)); + visitor.outerAlternative(); + if (visitor.frequencies.size() != 1) { + factory.getGrammar().tool.errMgr.toolError(ErrorType.INTERNAL_ERROR); + return new FrequencySet<String>(); + } + + return visitor.frequencies.peek(); + } + catch (RecognitionException ex) { + factory.getGrammar().tool.errMgr.toolError(ErrorType.INTERNAL_ERROR, ex); + return new FrequencySet<String>(); + } + } + + public List<Decl> getDeclForAltElement(GrammarAST t, String refLabelName, boolean needList) { + List<Decl> decls = new ArrayList<Decl>(); + if ( t.getType()==RULE_REF ) { + Rule rref = factory.getGrammar().getRule(t.getText()); + String ctxName = factory.getGenerator().getTarget() + .getRuleFunctionContextStructName(rref); + if ( needList) { + if(factory.getGenerator().getTarget().supportsOverloadedMethods()) + decls.add( new ContextRuleListGetterDecl(factory, refLabelName, ctxName) ); + decls.add( new ContextRuleListIndexedGetterDecl(factory, refLabelName, ctxName) ); + } + else { + decls.add( new ContextRuleGetterDecl(factory, refLabelName, ctxName) ); + } + } + else { + if ( needList ) { + if(factory.getGenerator().getTarget().supportsOverloadedMethods()) + decls.add( new ContextTokenListGetterDecl(factory, refLabelName) ); + decls.add( new ContextTokenListIndexedGetterDecl(factory, refLabelName) ); + } + else { + decls.add( new ContextTokenGetterDecl(factory, refLabelName) ); + } + } + return decls; + } + + /** Add local var decl */ + public void addLocalDecl(Decl d) { + if ( locals ==null ) locals = new OrderedHashSet<Decl>(); + locals.add(d); + d.isLocal = true; + } + + /** Add decl to struct ctx for rule or alt if labeled */ + public void addContextDecl(String altLabel, Decl d) { + CodeBlockForOuterMostAlt alt = d.getOuterMostAltCodeBlock(); + // if we found code blk and might be alt label, try to add to that label ctx + if ( alt!=null && altLabelCtxs!=null ) { +// System.out.println(d.name+" lives in alt "+alt.alt.altNum); + AltLabelStructDecl altCtx = altLabelCtxs.get(altLabel); + if ( altCtx!=null ) { // we have an alt ctx +// System.out.println("ctx is "+ altCtx.name); + altCtx.addDecl(d); + return; + } + } + ruleCtx.addDecl(d); // stick in overall rule's ctx + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/RuleSempredFunction.java b/tool/src/org/antlr/v4/codegen/model/RuleSempredFunction.java new file mode 100644 index 0000000..2b1f6a1 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/RuleSempredFunction.java @@ -0,0 +1,40 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.tool.Rule; + +public class RuleSempredFunction extends RuleActionFunction { + public RuleSempredFunction(OutputModelFactory factory, Rule r, String ctxType) { + super(factory, r, ctxType); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/SemPred.java b/tool/src/org/antlr/v4/codegen/model/SemPred.java new file mode 100644 index 0000000..605091f --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/SemPred.java @@ -0,0 +1,97 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.ActionTranslator; +import org.antlr.v4.codegen.CodeGenerator; +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.codegen.model.chunk.ActionChunk; +import org.antlr.v4.runtime.atn.AbstractPredicateTransition; +import org.antlr.v4.tool.ast.ActionAST; +import org.antlr.v4.tool.ast.GrammarAST; + +import java.util.List; + +/** */ +public class SemPred extends Action { + /** + * The user-specified terminal option {@code fail}, if it was used and the + * value is a string literal. For example: + * + * <p> + * {@code {pred}?<fail='message'>}</p> + */ + public String msg; + /** + * The predicate string with <code>{</code> and <code>}?</code> stripped from the ends. + */ + public String predicate; + + /** + * The translated chunks of the user-specified terminal option {@code fail}, + * if it was used and the value is an action. For example: + * + * <p> + * {@code {pred}?<fail={"Java literal"}>}</p> + */ + @ModelElement public List<ActionChunk> failChunks; + + public SemPred(OutputModelFactory factory, ActionAST ast) { + super(factory,ast); + + assert ast.atnState != null + && ast.atnState.getNumberOfTransitions() == 1 + && ast.atnState.transition(0) instanceof AbstractPredicateTransition; + + GrammarAST failNode = ast.getOptionAST("fail"); + CodeGenerator gen = factory.getGenerator(); + predicate = ast.getText(); + if (predicate.startsWith("{") && predicate.endsWith("}?")) { + predicate = predicate.substring(1, predicate.length() - 2); + } + predicate = gen.getTarget().getTargetStringLiteralFromString(predicate); + + if ( failNode==null ) return; + + if ( failNode instanceof ActionAST ) { + ActionAST failActionNode = (ActionAST)failNode; + RuleFunction rf = factory.getCurrentRuleFunction(); + failChunks = ActionTranslator.translateAction(factory, rf, + failActionNode.token, + failActionNode); + } + else { + msg = gen.getTarget().getTargetStringLiteralFromANTLRStringLiteral(gen, + failNode.getText(), + true); + } + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/SerializedATN.java b/tool/src/org/antlr/v4/codegen/model/SerializedATN.java new file mode 100644 index 0000000..ae64c43 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/SerializedATN.java @@ -0,0 +1,65 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.runtime.atn.ATN; +import org.antlr.v4.runtime.atn.ATNSerializer; +import org.antlr.v4.runtime.misc.IntegerList; + +import java.util.ArrayList; +import java.util.List; + +public class SerializedATN extends OutputModelObject { + // TODO: make this into a kind of decl or multiple? + public List<String> serialized; + public SerializedATN(OutputModelFactory factory, ATN atn) { + super(factory); + IntegerList data = ATNSerializer.getSerialized(atn); + serialized = new ArrayList<String>(data.size()); + for (int c : data.toArray()) { + String encoded = factory.getGenerator().getTarget().encodeIntAsCharEscape(c == -1 ? Character.MAX_VALUE : c); + serialized.add(encoded); + } +// System.out.println(ATNSerializer.getDecoded(factory.getGrammar(), atn)); + } + + public String[][] getSegments() { + List<String[]> segments = new ArrayList<String[]>(); + int segmentLimit = factory.getGenerator().getTarget().getSerializedATNSegmentLimit(); + for (int i = 0; i < serialized.size(); i += segmentLimit) { + List<String> currentSegment = serialized.subList(i, Math.min(i + segmentLimit, serialized.size())); + segments.add(currentSegment.toArray(new String[currentSegment.size()])); + } + + return segments.toArray(new String[segments.size()][]); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/SrcOp.java b/tool/src/org/antlr/v4/codegen/model/SrcOp.java new file mode 100644 index 0000000..d4a9834 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/SrcOp.java @@ -0,0 +1,82 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.codegen.model.decl.CodeBlock; +import org.antlr.v4.tool.ast.GrammarAST; + +/** */ +public abstract class SrcOp extends OutputModelObject { + /** Used to create unique var names etc... */ + public int uniqueID; // TODO: do we need? + + /** All operations know in which block they live: + * + * CodeBlock, CodeBlockForAlt + * + * Templates might need to know block nesting level or find + * a specific declaration, etc... + */ + public CodeBlock enclosingBlock; + + public RuleFunction enclosingRuleRunction; + + public SrcOp(OutputModelFactory factory) { this(factory,null); } + public SrcOp(OutputModelFactory factory, GrammarAST ast) { + super(factory,ast); + if ( ast!=null ) uniqueID = ast.token.getTokenIndex(); + enclosingBlock = factory.getCurrentBlock(); + enclosingRuleRunction = factory.getCurrentRuleFunction(); + } + + /** Walk upwards in model tree, looking for outer alt's code block */ + public CodeBlockForOuterMostAlt getOuterMostAltCodeBlock() { + if ( this instanceof CodeBlockForOuterMostAlt ) { + return (CodeBlockForOuterMostAlt)this; + } + CodeBlock p = enclosingBlock; + while ( p!=null ) { + if ( p instanceof CodeBlockForOuterMostAlt ) { + return (CodeBlockForOuterMostAlt)p; + } + p = p.enclosingBlock; + } + return null; + } + + /** Return label alt or return name of rule */ + public String getContextName() { + CodeBlockForOuterMostAlt alt = getOuterMostAltCodeBlock(); + if ( alt!=null && alt.altLabel!=null ) return alt.altLabel; + return enclosingRuleRunction.name; + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/StarBlock.java b/tool/src/org/antlr/v4/codegen/model/StarBlock.java new file mode 100644 index 0000000..e61ef72 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/StarBlock.java @@ -0,0 +1,52 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.runtime.atn.StarLoopEntryState; +import org.antlr.v4.tool.ast.GrammarAST; + +import java.util.List; + +public class StarBlock extends Loop { + public String loopLabel; + + public StarBlock(OutputModelFactory factory, + GrammarAST blkOrEbnfRootAST, + List<CodeBlockForAlt> alts) + { + super(factory, blkOrEbnfRootAST, alts); + loopLabel = factory.getGenerator().getTarget().getLoopLabel(blkOrEbnfRootAST); + StarLoopEntryState star = (StarLoopEntryState)blkOrEbnfRootAST.atnState; + loopBackStateNumber = star.loopBackState.stateNumber; + decision = star.decision; + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/Sync.java b/tool/src/org/antlr/v4/codegen/model/Sync.java new file mode 100644 index 0000000..b9c20fc --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/Sync.java @@ -0,0 +1,52 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.runtime.misc.IntervalSet; +import org.antlr.v4.tool.ast.GrammarAST; + +/** */ +public class Sync extends SrcOp { + public int decision; +// public BitSetDecl expecting; + public Sync(OutputModelFactory factory, + GrammarAST blkOrEbnfRootAST, + IntervalSet expecting, + int decision, + String position) + { + super(factory, blkOrEbnfRootAST); + this.decision = decision; +// this.expecting = factory.createExpectingBitSet(ast, decision, expecting, position); +// factory.defineBitSet(this.expecting); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/TestSetInline.java b/tool/src/org/antlr/v4/codegen/model/TestSetInline.java new file mode 100644 index 0000000..0b3956c --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/TestSetInline.java @@ -0,0 +1,84 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.runtime.misc.IntervalSet; +import org.antlr.v4.tool.ast.GrammarAST; + +import java.util.ArrayList; +import java.util.List; + +/** */ +public class TestSetInline extends SrcOp { + public int bitsetWordSize; + public String varName; + public Bitset[] bitsets; + + public TestSetInline(OutputModelFactory factory, GrammarAST ast, IntervalSet set, int wordSize) { + super(factory, ast); + bitsetWordSize = wordSize; + Bitset[] withZeroOffset = createBitsets(factory, set, wordSize, true); + Bitset[] withoutZeroOffset = createBitsets(factory, set, wordSize, false); + this.bitsets = withZeroOffset.length <= withoutZeroOffset.length ? withZeroOffset : withoutZeroOffset; + this.varName = "_la"; + } + + private static Bitset[] createBitsets(OutputModelFactory factory, + IntervalSet set, + int wordSize, + boolean useZeroOffset) { + List<Bitset> bitsetList = new ArrayList<Bitset>(); + for (int ttype : set.toArray()) { + Bitset current = !bitsetList.isEmpty() ? bitsetList.get(bitsetList.size() - 1) : null; + if (current == null || ttype > (current.shift + wordSize-1)) { + current = new Bitset(); + if (useZeroOffset && ttype >= 0 && ttype < wordSize-1) { + current.shift = 0; + } + else { + current.shift = ttype; + } + + bitsetList.add(current); + } + + current.ttypes.add(factory.getGenerator().getTarget().getTokenTypeAsTargetLabel(factory.getGrammar(), ttype)); + } + + return bitsetList.toArray(new Bitset[bitsetList.size()]); + } + + public static final class Bitset { + public int shift; + public final List<String> ttypes = new ArrayList<String>(); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/ThrowEarlyExitException.java b/tool/src/org/antlr/v4/codegen/model/ThrowEarlyExitException.java new file mode 100644 index 0000000..17f8574 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/ThrowEarlyExitException.java @@ -0,0 +1,42 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.runtime.misc.IntervalSet; +import org.antlr.v4.tool.ast.GrammarAST; + +/** */ +public class ThrowEarlyExitException extends ThrowRecognitionException { + public ThrowEarlyExitException(OutputModelFactory factory, GrammarAST ast, IntervalSet expecting) { + super(factory, ast, expecting); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/ThrowNoViableAlt.java b/tool/src/org/antlr/v4/codegen/model/ThrowNoViableAlt.java new file mode 100644 index 0000000..eb07be7 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/ThrowNoViableAlt.java @@ -0,0 +1,44 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.runtime.misc.IntervalSet; +import org.antlr.v4.tool.ast.GrammarAST; + +/** */ +public class ThrowNoViableAlt extends ThrowRecognitionException { + public ThrowNoViableAlt(OutputModelFactory factory, GrammarAST blkOrEbnfRootAST, + IntervalSet expecting) + { + super(factory, blkOrEbnfRootAST, expecting); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/ThrowRecognitionException.java b/tool/src/org/antlr/v4/codegen/model/ThrowRecognitionException.java new file mode 100644 index 0000000..8815764 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/ThrowRecognitionException.java @@ -0,0 +1,53 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.runtime.misc.IntervalSet; +import org.antlr.v4.tool.ast.GrammarAST; + +/** */ +public class ThrowRecognitionException extends SrcOp { + public int decision; + public String grammarFile; + public int grammarLine; + public int grammarCharPosInLine; + + public ThrowRecognitionException(OutputModelFactory factory, GrammarAST ast, IntervalSet expecting) { + super(factory, ast); + //this.decision = ((BlockStartState)ast.ATNState).decision; + grammarLine = ast.getLine(); + grammarLine = ast.getCharPositionInLine(); + grammarFile = factory.getGrammar().fileName; + //this.expecting = factory.createExpectingBitSet(ast, decision, expecting, "error"); +// factory.defineBitSet(this.expecting); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/VisitorDispatchMethod.java b/tool/src/org/antlr/v4/codegen/model/VisitorDispatchMethod.java new file mode 100644 index 0000000..a56dc6e --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/VisitorDispatchMethod.java @@ -0,0 +1,38 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; + +public class VisitorDispatchMethod extends DispatchMethod { + public VisitorDispatchMethod(OutputModelFactory factory) { + super(factory); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/VisitorFile.java b/tool/src/org/antlr/v4/codegen/model/VisitorFile.java new file mode 100644 index 0000000..e1370a9 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/VisitorFile.java @@ -0,0 +1,84 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.runtime.misc.Pair; +import org.antlr.v4.tool.Grammar; +import org.antlr.v4.tool.Rule; +import org.antlr.v4.tool.ast.ActionAST; +import org.antlr.v4.tool.ast.AltAST; + +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class VisitorFile extends OutputFile { + public String genPackage; // from -package cmd-line + public String grammarName; + public String parserName; + /** + * The names of all rule contexts which may need to be visited. + */ + public Set<String> visitorNames = new LinkedHashSet<String>(); + /** + * For rule contexts created for a labeled outer alternative, maps from + * a listener context name to the name of the rule which defines the + * context. + */ + public Map<String, String> visitorLabelRuleNames = new LinkedHashMap<String, String>(); + + @ModelElement public Action header; + + public VisitorFile(OutputModelFactory factory, String fileName) { + super(factory, fileName); + Grammar g = factory.getGrammar(); + parserName = g.getRecognizerName(); + grammarName = g.name; + for (Rule r : g.rules.values()) { + Map<String, List<Pair<Integer, AltAST>>> labels = r.getAltLabels(); + if ( labels!=null ) { + for (Map.Entry<String, List<Pair<Integer, AltAST>>> pair : labels.entrySet()) { + visitorNames.add(pair.getKey()); + visitorLabelRuleNames.put(pair.getKey(), r.name); + } + } + else { + // if labels, must label all. no need for generic rule visitor then + visitorNames.add(r.name); + } + } + ActionAST ast = g.namedActions.get("header"); + if ( ast!=null ) header = new Action(factory, ast); + genPackage = factory.getGrammar().tool.genPackage; + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/chunk/ActionChunk.java b/tool/src/org/antlr/v4/codegen/model/chunk/ActionChunk.java new file mode 100644 index 0000000..3c7e364 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/chunk/ActionChunk.java @@ -0,0 +1,44 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.chunk; + +import org.antlr.v4.codegen.model.OutputModelObject; +import org.antlr.v4.codegen.model.decl.StructDecl; + +/** */ +public class ActionChunk extends OutputModelObject { + /** Where is the ctx that defines attrs,labels etc... for this action? */ + public StructDecl ctx; + + public ActionChunk(StructDecl ctx) { + this.ctx = ctx; + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/chunk/ActionTemplate.java b/tool/src/org/antlr/v4/codegen/model/chunk/ActionTemplate.java new file mode 100644 index 0000000..2c2edc1 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/chunk/ActionTemplate.java @@ -0,0 +1,43 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.chunk; + +import org.antlr.v4.codegen.model.decl.StructDecl; +import org.stringtemplate.v4.ST; + +public class ActionTemplate extends ActionChunk { + public ST st; + + public ActionTemplate(StructDecl ctx, ST st) { + super(ctx); + this.st = st; + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/chunk/ActionText.java b/tool/src/org/antlr/v4/codegen/model/chunk/ActionText.java new file mode 100644 index 0000000..de3acff --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/chunk/ActionText.java @@ -0,0 +1,43 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.chunk; + +import org.antlr.v4.codegen.model.decl.StructDecl; + +/** */ +public class ActionText extends ActionChunk { + public String text; + + public ActionText(StructDecl ctx, String text) { + super(ctx); + this.text = text; + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/chunk/ArgRef.java b/tool/src/org/antlr/v4/codegen/model/chunk/ArgRef.java new file mode 100644 index 0000000..2d39271 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/chunk/ArgRef.java @@ -0,0 +1,40 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.chunk; + +import org.antlr.v4.codegen.model.decl.StructDecl; + +/** */ +public class ArgRef extends LocalRef { + public ArgRef(StructDecl ctx, String name) { + super(ctx, name); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/chunk/LabelRef.java b/tool/src/org/antlr/v4/codegen/model/chunk/LabelRef.java new file mode 100644 index 0000000..eae4a65 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/chunk/LabelRef.java @@ -0,0 +1,42 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.chunk; + +import org.antlr.v4.codegen.model.decl.StructDecl; + +public class LabelRef extends ActionChunk { + public String name; + + public LabelRef(StructDecl ctx, String name) { + super(ctx); + this.name = name; + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/chunk/ListLabelRef.java b/tool/src/org/antlr/v4/codegen/model/chunk/ListLabelRef.java new file mode 100644 index 0000000..c7af76a --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/chunk/ListLabelRef.java @@ -0,0 +1,37 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.chunk; + +import org.antlr.v4.codegen.model.decl.StructDecl; + +public class ListLabelRef extends LabelRef { + public ListLabelRef(StructDecl ctx, String name) { super(ctx, name); } +} diff --git a/tool/src/org/antlr/v4/codegen/model/chunk/LocalRef.java b/tool/src/org/antlr/v4/codegen/model/chunk/LocalRef.java new file mode 100644 index 0000000..3463dc8 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/chunk/LocalRef.java @@ -0,0 +1,42 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.chunk; + +import org.antlr.v4.codegen.model.decl.StructDecl; + +public class LocalRef extends ActionChunk { + public String name; + + public LocalRef(StructDecl ctx, String name) { + super(ctx); + this.name = name; + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/chunk/NonLocalAttrRef.java b/tool/src/org/antlr/v4/codegen/model/chunk/NonLocalAttrRef.java new file mode 100644 index 0000000..d9a28f7 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/chunk/NonLocalAttrRef.java @@ -0,0 +1,47 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.chunk; + +import org.antlr.v4.codegen.model.decl.StructDecl; + +public class NonLocalAttrRef extends ActionChunk { + public String ruleName; + public String name; + public int ruleIndex; + + public NonLocalAttrRef(StructDecl ctx, String ruleName, String name, int ruleIndex) { + super(ctx); + this.name = name; + this.ruleName = ruleName; + this.ruleIndex = ruleIndex; + } + +} diff --git a/tool/src/org/antlr/v4/codegen/model/chunk/QRetValueRef.java b/tool/src/org/antlr/v4/codegen/model/chunk/QRetValueRef.java new file mode 100644 index 0000000..d799e78 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/chunk/QRetValueRef.java @@ -0,0 +1,42 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.chunk; + +import org.antlr.v4.codegen.model.decl.StructDecl; + +/** */ +public class QRetValueRef extends RetValueRef { + public String dict; + public QRetValueRef(StructDecl ctx, String dict, String name) { + super(ctx,name); + this.dict = dict; + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/chunk/RetValueRef.java b/tool/src/org/antlr/v4/codegen/model/chunk/RetValueRef.java new file mode 100644 index 0000000..59e45fe --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/chunk/RetValueRef.java @@ -0,0 +1,44 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.chunk; + +import org.antlr.v4.codegen.model.decl.StructDecl; + +/** */ +public class RetValueRef extends ActionChunk { + public String name; + + public RetValueRef(StructDecl ctx, String name) { + super(ctx); + this.name = name; + } + +} diff --git a/tool/src/org/antlr/v4/codegen/model/chunk/RulePropertyRef.java b/tool/src/org/antlr/v4/codegen/model/chunk/RulePropertyRef.java new file mode 100644 index 0000000..ae14863 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/chunk/RulePropertyRef.java @@ -0,0 +1,43 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.chunk; + +import org.antlr.v4.codegen.model.decl.StructDecl; + +/** */ +public class RulePropertyRef extends ActionChunk { + public String label; + + public RulePropertyRef(StructDecl ctx, String label) { + super(ctx); + this.label = label; + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/chunk/RulePropertyRef_ctx.java b/tool/src/org/antlr/v4/codegen/model/chunk/RulePropertyRef_ctx.java new file mode 100644 index 0000000..aebded7 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/chunk/RulePropertyRef_ctx.java @@ -0,0 +1,39 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.chunk; + +import org.antlr.v4.codegen.model.decl.StructDecl; + +public class RulePropertyRef_ctx extends RulePropertyRef { + public RulePropertyRef_ctx(StructDecl ctx, String label) { + super(ctx, label); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/chunk/RulePropertyRef_parser.java b/tool/src/org/antlr/v4/codegen/model/chunk/RulePropertyRef_parser.java new file mode 100644 index 0000000..a427810 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/chunk/RulePropertyRef_parser.java @@ -0,0 +1,40 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.chunk; + +import org.antlr.v4.codegen.model.decl.StructDecl; + +/** */ +public class RulePropertyRef_parser extends RulePropertyRef { + public RulePropertyRef_parser(StructDecl ctx, String label) { + super(ctx, label); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/chunk/RulePropertyRef_start.java b/tool/src/org/antlr/v4/codegen/model/chunk/RulePropertyRef_start.java new file mode 100644 index 0000000..826c5d0 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/chunk/RulePropertyRef_start.java @@ -0,0 +1,40 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.chunk; + +import org.antlr.v4.codegen.model.decl.StructDecl; + +/** */ +public class RulePropertyRef_start extends RulePropertyRef { + public RulePropertyRef_start(StructDecl ctx, String label) { + super(ctx, label); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/chunk/RulePropertyRef_stop.java b/tool/src/org/antlr/v4/codegen/model/chunk/RulePropertyRef_stop.java new file mode 100644 index 0000000..7b9e3f2 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/chunk/RulePropertyRef_stop.java @@ -0,0 +1,40 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.chunk; + +import org.antlr.v4.codegen.model.decl.StructDecl; + +/** */ +public class RulePropertyRef_stop extends RulePropertyRef { + public RulePropertyRef_stop(StructDecl ctx, String label) { + super(ctx, label); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/chunk/RulePropertyRef_text.java b/tool/src/org/antlr/v4/codegen/model/chunk/RulePropertyRef_text.java new file mode 100644 index 0000000..1230105 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/chunk/RulePropertyRef_text.java @@ -0,0 +1,40 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.chunk; + +import org.antlr.v4.codegen.model.decl.StructDecl; + +/** */ +public class RulePropertyRef_text extends RulePropertyRef { + public RulePropertyRef_text(StructDecl ctx, String label) { + super(ctx, label); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/chunk/SetAttr.java b/tool/src/org/antlr/v4/codegen/model/chunk/SetAttr.java new file mode 100644 index 0000000..1b6e15b --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/chunk/SetAttr.java @@ -0,0 +1,48 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.chunk; + +import org.antlr.v4.codegen.model.ModelElement; +import org.antlr.v4.codegen.model.decl.StructDecl; + +import java.util.List; + +/** */ +public class SetAttr extends ActionChunk { + public String name; + @ModelElement public List<ActionChunk> rhsChunks; + + public SetAttr(StructDecl ctx, String name, List<ActionChunk> rhsChunks) { + super(ctx); + this.name = name; + this.rhsChunks = rhsChunks; + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/chunk/SetNonLocalAttr.java b/tool/src/org/antlr/v4/codegen/model/chunk/SetNonLocalAttr.java new file mode 100644 index 0000000..9872b6e --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/chunk/SetNonLocalAttr.java @@ -0,0 +1,49 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.chunk; + +import org.antlr.v4.codegen.model.decl.StructDecl; + +import java.util.List; + +public class SetNonLocalAttr extends SetAttr { + public String ruleName; + public int ruleIndex; + + public SetNonLocalAttr(StructDecl ctx, + String ruleName, String name, int ruleIndex, + List<ActionChunk> rhsChunks) + { + super(ctx, name, rhsChunks); + this.ruleName = ruleName; + this.ruleIndex = ruleIndex; + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/chunk/ThisRulePropertyRef_ctx.java b/tool/src/org/antlr/v4/codegen/model/chunk/ThisRulePropertyRef_ctx.java new file mode 100644 index 0000000..e8dc913 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/chunk/ThisRulePropertyRef_ctx.java @@ -0,0 +1,39 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.chunk; + +import org.antlr.v4.codegen.model.decl.StructDecl; + +public class ThisRulePropertyRef_ctx extends RulePropertyRef { + public ThisRulePropertyRef_ctx(StructDecl ctx, String label) { + super(ctx, label); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/chunk/ThisRulePropertyRef_parser.java b/tool/src/org/antlr/v4/codegen/model/chunk/ThisRulePropertyRef_parser.java new file mode 100644 index 0000000..7cbf5d6 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/chunk/ThisRulePropertyRef_parser.java @@ -0,0 +1,40 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.chunk; + +import org.antlr.v4.codegen.model.decl.StructDecl; + +/** */ +public class ThisRulePropertyRef_parser extends RulePropertyRef { + public ThisRulePropertyRef_parser(StructDecl ctx, String label) { + super(ctx, label); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/chunk/ThisRulePropertyRef_start.java b/tool/src/org/antlr/v4/codegen/model/chunk/ThisRulePropertyRef_start.java new file mode 100644 index 0000000..614c973 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/chunk/ThisRulePropertyRef_start.java @@ -0,0 +1,40 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.chunk; + +import org.antlr.v4.codegen.model.decl.StructDecl; + +/** */ +public class ThisRulePropertyRef_start extends RulePropertyRef { + public ThisRulePropertyRef_start(StructDecl ctx, String label) { + super(ctx, label); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/chunk/ThisRulePropertyRef_stop.java b/tool/src/org/antlr/v4/codegen/model/chunk/ThisRulePropertyRef_stop.java new file mode 100644 index 0000000..65b5322 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/chunk/ThisRulePropertyRef_stop.java @@ -0,0 +1,40 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.chunk; + +import org.antlr.v4.codegen.model.decl.StructDecl; + +/** */ +public class ThisRulePropertyRef_stop extends RulePropertyRef { + public ThisRulePropertyRef_stop(StructDecl ctx, String label) { + super(ctx, label); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/chunk/ThisRulePropertyRef_text.java b/tool/src/org/antlr/v4/codegen/model/chunk/ThisRulePropertyRef_text.java new file mode 100644 index 0000000..7ee4d8e --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/chunk/ThisRulePropertyRef_text.java @@ -0,0 +1,40 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.chunk; + +import org.antlr.v4.codegen.model.decl.StructDecl; + +/** */ +public class ThisRulePropertyRef_text extends RulePropertyRef { + public ThisRulePropertyRef_text(StructDecl ctx, String label) { + super(ctx, label); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/chunk/TokenPropertyRef.java b/tool/src/org/antlr/v4/codegen/model/chunk/TokenPropertyRef.java new file mode 100644 index 0000000..8a3f7d5 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/chunk/TokenPropertyRef.java @@ -0,0 +1,43 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.chunk; + +import org.antlr.v4.codegen.model.decl.StructDecl; + +/** */ +public class TokenPropertyRef extends ActionChunk { + public String label; + + public TokenPropertyRef(StructDecl ctx, String label) { + super(ctx); + this.label = label; + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/chunk/TokenPropertyRef_channel.java b/tool/src/org/antlr/v4/codegen/model/chunk/TokenPropertyRef_channel.java new file mode 100644 index 0000000..9c93690 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/chunk/TokenPropertyRef_channel.java @@ -0,0 +1,40 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.chunk; + +import org.antlr.v4.codegen.model.decl.StructDecl; + +/** */ +public class TokenPropertyRef_channel extends TokenPropertyRef { + public TokenPropertyRef_channel(StructDecl ctx, String label) { + super(ctx, label); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/chunk/TokenPropertyRef_index.java b/tool/src/org/antlr/v4/codegen/model/chunk/TokenPropertyRef_index.java new file mode 100644 index 0000000..a456876 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/chunk/TokenPropertyRef_index.java @@ -0,0 +1,40 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.chunk; + +import org.antlr.v4.codegen.model.decl.StructDecl; + +/** */ +public class TokenPropertyRef_index extends TokenPropertyRef { + public TokenPropertyRef_index(StructDecl ctx, String label) { + super(ctx, label); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/chunk/TokenPropertyRef_int.java b/tool/src/org/antlr/v4/codegen/model/chunk/TokenPropertyRef_int.java new file mode 100644 index 0000000..91c13bd --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/chunk/TokenPropertyRef_int.java @@ -0,0 +1,40 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.chunk; + +import org.antlr.v4.codegen.model.decl.StructDecl; + +/** */ +public class TokenPropertyRef_int extends TokenPropertyRef { + public TokenPropertyRef_int(StructDecl ctx, String label) { + super(ctx, label); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/chunk/TokenPropertyRef_line.java b/tool/src/org/antlr/v4/codegen/model/chunk/TokenPropertyRef_line.java new file mode 100644 index 0000000..f9c08f4 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/chunk/TokenPropertyRef_line.java @@ -0,0 +1,40 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.chunk; + +import org.antlr.v4.codegen.model.decl.StructDecl; + +/** */ +public class TokenPropertyRef_line extends TokenPropertyRef { + public TokenPropertyRef_line(StructDecl ctx, String label) { + super(ctx, label); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/chunk/TokenPropertyRef_pos.java b/tool/src/org/antlr/v4/codegen/model/chunk/TokenPropertyRef_pos.java new file mode 100644 index 0000000..469b228 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/chunk/TokenPropertyRef_pos.java @@ -0,0 +1,40 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.chunk; + +import org.antlr.v4.codegen.model.decl.StructDecl; + +/** */ +public class TokenPropertyRef_pos extends TokenPropertyRef { + public TokenPropertyRef_pos(StructDecl ctx, String label) { + super(ctx, label); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/chunk/TokenPropertyRef_text.java b/tool/src/org/antlr/v4/codegen/model/chunk/TokenPropertyRef_text.java new file mode 100644 index 0000000..3da4f53 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/chunk/TokenPropertyRef_text.java @@ -0,0 +1,40 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.chunk; + +import org.antlr.v4.codegen.model.decl.StructDecl; + +/** */ +public class TokenPropertyRef_text extends TokenPropertyRef { + public TokenPropertyRef_text(StructDecl ctx, String label) { + super(ctx, label); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/chunk/TokenPropertyRef_type.java b/tool/src/org/antlr/v4/codegen/model/chunk/TokenPropertyRef_type.java new file mode 100644 index 0000000..d495e19 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/chunk/TokenPropertyRef_type.java @@ -0,0 +1,40 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.chunk; + +import org.antlr.v4.codegen.model.decl.StructDecl; + +/** */ +public class TokenPropertyRef_type extends TokenPropertyRef { + public TokenPropertyRef_type(StructDecl ctx, String label) { + super(ctx, label); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/chunk/TokenRef.java b/tool/src/org/antlr/v4/codegen/model/chunk/TokenRef.java new file mode 100644 index 0000000..ac2569b --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/chunk/TokenRef.java @@ -0,0 +1,43 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.chunk; + +import org.antlr.v4.codegen.model.decl.StructDecl; + +/** */ +public class TokenRef extends ActionChunk { + public String name; + + public TokenRef(StructDecl ctx, String name) { + super(ctx); + this.name = name; + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/dbg.java b/tool/src/org/antlr/v4/codegen/model/dbg.java new file mode 100644 index 0000000..4eb8386 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/dbg.java @@ -0,0 +1,35 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model; + +/** */ +public class dbg extends OutputModelObject { +} diff --git a/tool/src/org/antlr/v4/codegen/model/decl/AltLabelStructDecl.java b/tool/src/org/antlr/v4/codegen/model/decl/AltLabelStructDecl.java new file mode 100644 index 0000000..5d49625 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/decl/AltLabelStructDecl.java @@ -0,0 +1,78 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.decl; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.codegen.model.DispatchMethod; +import org.antlr.v4.codegen.model.ListenerDispatchMethod; +import org.antlr.v4.codegen.model.VisitorDispatchMethod; +import org.antlr.v4.tool.Rule; + +import java.util.ArrayList; + +/** A StructDecl to handle a -> label on alt */ +public class AltLabelStructDecl extends StructDecl { + public int altNum; + public AltLabelStructDecl(OutputModelFactory factory, Rule r, + int altNum, String label) + { + super(factory, r); + this.altNum = altNum; + this.name = // override name set in super to the label ctx + factory.getGenerator().getTarget().getAltLabelContextStructName(label); + derivedFromName = label; + } + + @Override + public void addDispatchMethods(Rule r) { + dispatchMethods = new ArrayList<DispatchMethod>(); + if ( factory.getGrammar().tool.gen_listener ) { + dispatchMethods.add(new ListenerDispatchMethod(factory, true)); + dispatchMethods.add(new ListenerDispatchMethod(factory, false)); + } + if ( factory.getGrammar().tool.gen_visitor ) { + dispatchMethods.add(new VisitorDispatchMethod(factory)); + } + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if ( obj == this ) return true; + if (!(obj instanceof AltLabelStructDecl)) return false; + + return name.equals(((AltLabelStructDecl)obj).name); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/decl/AttributeDecl.java b/tool/src/org/antlr/v4/codegen/model/decl/AttributeDecl.java new file mode 100644 index 0000000..7b77ecd --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/decl/AttributeDecl.java @@ -0,0 +1,45 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.decl; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.tool.Attribute; + +/** */ +public class AttributeDecl extends Decl { + public String type; + public String initValue; + public AttributeDecl(OutputModelFactory factory, Attribute a) { + super(factory, a.name, a.decl); + this.type = a.type; + this.initValue = a.initValue; + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/decl/CodeBlock.java b/tool/src/org/antlr/v4/codegen/model/decl/CodeBlock.java new file mode 100644 index 0000000..8a0890e --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/decl/CodeBlock.java @@ -0,0 +1,85 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.decl; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.codegen.model.ModelElement; +import org.antlr.v4.codegen.model.SrcOp; +import org.antlr.v4.runtime.misc.OrderedHashSet; + +import java.util.ArrayList; +import java.util.List; + +public class CodeBlock extends SrcOp { + public int codeBlockLevel; + public int treeLevel; + + @ModelElement public OrderedHashSet<Decl> locals; + @ModelElement public List<SrcOp> preamble; + @ModelElement public List<SrcOp> ops; + + public CodeBlock(OutputModelFactory factory) { + super(factory); + } + + public CodeBlock(OutputModelFactory factory, int treeLevel, int codeBlockLevel) { + super(factory); + this.treeLevel = treeLevel; + this.codeBlockLevel = codeBlockLevel; + } + + /** Add local var decl */ + public void addLocalDecl(Decl d) { + if ( locals==null ) locals = new OrderedHashSet<Decl>(); + locals.add(d); + d.isLocal = true; + } + + public void addPreambleOp(SrcOp op) { + if ( preamble==null ) preamble = new ArrayList<SrcOp>(); + preamble.add(op); + } + + public void addOp(SrcOp op) { + if ( ops==null ) ops = new ArrayList<SrcOp>(); + ops.add(op); + } + + public void insertOp(int i, SrcOp op) { + if ( ops==null ) ops = new ArrayList<SrcOp>(); + ops.add(i, op); + } + + public void addOps(List<SrcOp> ops) { + if ( this.ops==null ) this.ops = new ArrayList<SrcOp>(); + this.ops.addAll(ops); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/decl/ContextGetterDecl.java b/tool/src/org/antlr/v4/codegen/model/decl/ContextGetterDecl.java new file mode 100644 index 0000000..99ceef2 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/decl/ContextGetterDecl.java @@ -0,0 +1,68 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.decl; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.runtime.misc.MurmurHash; + +public abstract class ContextGetterDecl extends Decl { + public ContextGetterDecl(OutputModelFactory factory, String name) { + super(factory, name); + } + + /** Not used for output; just used to distinguish between decl types + * to avoid dups. + */ + public String getArgType() { return ""; }; // assume no args + + @Override + public int hashCode() { + int hash = MurmurHash.initialize(); + hash = MurmurHash.update(hash, name); + hash = MurmurHash.update(hash, getArgType()); + hash = MurmurHash.finish(hash, 2); + return hash; + } + + /** Make sure that a getter does not equal a label. X() and X are ok. + * OTOH, treat X() with two diff return values as the same. Treat + * two X() with diff args as different. + */ + @Override + public boolean equals(Object obj) { + if ( this==obj ) return true; + // A() and label A are different + if ( !(obj instanceof ContextGetterDecl) ) return false; + return + name.equals(((Decl) obj).name) && + getArgType().equals(((ContextGetterDecl) obj).getArgType()); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/decl/ContextRuleGetterDecl.java b/tool/src/org/antlr/v4/codegen/model/decl/ContextRuleGetterDecl.java new file mode 100644 index 0000000..2bb4f9b --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/decl/ContextRuleGetterDecl.java @@ -0,0 +1,42 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.decl; + +import org.antlr.v4.codegen.OutputModelFactory; + +/** {@code public XContext X() { }} */ +public class ContextRuleGetterDecl extends ContextGetterDecl { + public String ctxName; + public ContextRuleGetterDecl(OutputModelFactory factory, String name, String ctxName) { + super(factory, name); + this.ctxName = ctxName; + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/decl/ContextRuleListGetterDecl.java b/tool/src/org/antlr/v4/codegen/model/decl/ContextRuleListGetterDecl.java new file mode 100644 index 0000000..c3bfae9 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/decl/ContextRuleListGetterDecl.java @@ -0,0 +1,44 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.decl; + +import org.antlr.v4.codegen.OutputModelFactory; + +/** {@code public List<XContext> X() { } + * public XContext X(int i) { }} + */ +public class ContextRuleListGetterDecl extends ContextGetterDecl { + public String ctxName; + public ContextRuleListGetterDecl(OutputModelFactory factory, String name, String ctxName) { + super(factory, name); + this.ctxName = ctxName; + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/decl/ContextRuleListIndexedGetterDecl.java b/tool/src/org/antlr/v4/codegen/model/decl/ContextRuleListIndexedGetterDecl.java new file mode 100644 index 0000000..a2c3948 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/decl/ContextRuleListIndexedGetterDecl.java @@ -0,0 +1,44 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.decl; + +import org.antlr.v4.codegen.OutputModelFactory; + +public class ContextRuleListIndexedGetterDecl extends ContextRuleListGetterDecl { + public ContextRuleListIndexedGetterDecl(OutputModelFactory factory, String name, String ctxName) { + super(factory, name, ctxName); + } + + @Override + public String getArgType() { + return "int"; + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/decl/ContextTokenGetterDecl.java b/tool/src/org/antlr/v4/codegen/model/decl/ContextTokenGetterDecl.java new file mode 100644 index 0000000..431e817 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/decl/ContextTokenGetterDecl.java @@ -0,0 +1,40 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.decl; + +import org.antlr.v4.codegen.OutputModelFactory; + +/** {@code public Token X() { }} */ +public class ContextTokenGetterDecl extends ContextGetterDecl { + public ContextTokenGetterDecl(OutputModelFactory factory, String name) { + super(factory, name); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/decl/ContextTokenListGetterDecl.java b/tool/src/org/antlr/v4/codegen/model/decl/ContextTokenListGetterDecl.java new file mode 100644 index 0000000..d293720 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/decl/ContextTokenListGetterDecl.java @@ -0,0 +1,42 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.decl; + +import org.antlr.v4.codegen.OutputModelFactory; + +/** {@code public List<Token> X() { } + * public Token X(int i) { }} + */ +public class ContextTokenListGetterDecl extends ContextGetterDecl { + public ContextTokenListGetterDecl(OutputModelFactory factory, String name) { + super(factory, name); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/decl/ContextTokenListIndexedGetterDecl.java b/tool/src/org/antlr/v4/codegen/model/decl/ContextTokenListIndexedGetterDecl.java new file mode 100644 index 0000000..ff7e48e --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/decl/ContextTokenListIndexedGetterDecl.java @@ -0,0 +1,44 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.decl; + +import org.antlr.v4.codegen.OutputModelFactory; + +public class ContextTokenListIndexedGetterDecl extends ContextTokenListGetterDecl { + public ContextTokenListIndexedGetterDecl(OutputModelFactory factory, String name) { + super(factory, name); + } + + @Override + public String getArgType() { + return "int"; + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/decl/Decl.java b/tool/src/org/antlr/v4/codegen/model/decl/Decl.java new file mode 100644 index 0000000..c956555 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/decl/Decl.java @@ -0,0 +1,67 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.decl; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.codegen.model.SrcOp; + +/** */ +public class Decl extends SrcOp { + public String name; + public String decl; // whole thing if copied from action + public boolean isLocal; // if local var (not in RuleContext struct) + public StructDecl ctx; // which context contains us? set by addDecl + + public Decl(OutputModelFactory factory, String name, String decl) { + this(factory, name); + this.decl = decl; + } + + public Decl(OutputModelFactory factory, String name) { + super(factory); + this.name = name; + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + /** If same name, can't redefine, unless it's a getter */ + @Override + public boolean equals(Object obj) { + if ( this==obj ) return true; + if ( !(obj instanceof Decl) ) return false; + // A() and label A are different + if ( obj instanceof ContextGetterDecl ) return false; + return name.equals(((Decl) obj).name); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/decl/ElementListDecl.java b/tool/src/org/antlr/v4/codegen/model/decl/ElementListDecl.java new file mode 100644 index 0000000..cf99a1a --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/decl/ElementListDecl.java @@ -0,0 +1,39 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.decl; + +import org.antlr.v4.codegen.OutputModelFactory; + +public class ElementListDecl extends Decl { + public ElementListDecl(OutputModelFactory factory, String name) { + super(factory, name); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/decl/RuleContextDecl.java b/tool/src/org/antlr/v4/codegen/model/decl/RuleContextDecl.java new file mode 100644 index 0000000..6f97bfa --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/decl/RuleContextDecl.java @@ -0,0 +1,44 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.decl; + +import org.antlr.v4.codegen.OutputModelFactory; + +/** */ +public class RuleContextDecl extends Decl { + public String ctxName; + public boolean isImplicit; + + public RuleContextDecl(OutputModelFactory factory, String name, String ctxName) { + super(factory, name); + this.ctxName = ctxName; + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/decl/RuleContextListDecl.java b/tool/src/org/antlr/v4/codegen/model/decl/RuleContextListDecl.java new file mode 100644 index 0000000..fbd22f6 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/decl/RuleContextListDecl.java @@ -0,0 +1,40 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.decl; + +import org.antlr.v4.codegen.OutputModelFactory; + +public class RuleContextListDecl extends RuleContextDecl { + public RuleContextListDecl(OutputModelFactory factory, String name, String ctxName) { + super(factory, name, ctxName); + isImplicit = false; + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/decl/StructDecl.java b/tool/src/org/antlr/v4/codegen/model/decl/StructDecl.java new file mode 100644 index 0000000..309d6ca --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/decl/StructDecl.java @@ -0,0 +1,112 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.decl; + +import org.antlr.v4.codegen.OutputModelFactory; +import org.antlr.v4.codegen.model.DispatchMethod; +import org.antlr.v4.codegen.model.ListenerDispatchMethod; +import org.antlr.v4.codegen.model.ModelElement; +import org.antlr.v4.codegen.model.OutputModelObject; +import org.antlr.v4.codegen.model.VisitorDispatchMethod; +import org.antlr.v4.runtime.misc.OrderedHashSet; +import org.antlr.v4.tool.Attribute; +import org.antlr.v4.tool.Rule; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** This object models the structure holding all of the parameters, + * return values, local variables, and labels associated with a rule. + */ +public class StructDecl extends Decl { + public String derivedFromName; // rule name or label name + public boolean provideCopyFrom; + @ModelElement public OrderedHashSet<Decl> attrs = new OrderedHashSet<Decl>(); + @ModelElement public OrderedHashSet<Decl> getters = new OrderedHashSet<Decl>(); + @ModelElement public Collection<AttributeDecl> ctorAttrs; + @ModelElement public List<? super DispatchMethod> dispatchMethods; + @ModelElement public List<OutputModelObject> interfaces; + @ModelElement public List<OutputModelObject> extensionMembers; + + public StructDecl(OutputModelFactory factory, Rule r) { + super(factory, factory.getGenerator().getTarget().getRuleFunctionContextStructName(r)); + addDispatchMethods(r); + derivedFromName = r.name; + provideCopyFrom = r.hasAltSpecificContexts(); + } + + public void addDispatchMethods(Rule r) { + dispatchMethods = new ArrayList<DispatchMethod>(); + if ( !r.hasAltSpecificContexts() ) { + // no enter/exit for this ruleContext if rule has labels + if ( factory.getGrammar().tool.gen_listener ) { + dispatchMethods.add(new ListenerDispatchMethod(factory, true)); + dispatchMethods.add(new ListenerDispatchMethod(factory, false)); + } + if ( factory.getGrammar().tool.gen_visitor ) { + dispatchMethods.add(new VisitorDispatchMethod(factory)); + } + } + } + + public void addDecl(Decl d) { + d.ctx = this; + if ( d instanceof ContextGetterDecl ) getters.add(d); + else attrs.add(d); + } + + public void addDecl(Attribute a) { + addDecl(new AttributeDecl(factory, a)); + } + + public void addDecls(Collection<Attribute> attrList) { + for (Attribute a : attrList) addDecl(a); + } + + public void implementInterface(OutputModelObject value) { + if (interfaces == null) { + interfaces = new ArrayList<OutputModelObject>(); + } + + interfaces.add(value); + } + + public void addExtensionMember(OutputModelObject member) { + if (extensionMembers == null) { + extensionMembers = new ArrayList<OutputModelObject>(); + } + + extensionMembers.add(member); + } + + public boolean isEmpty() { return attrs.isEmpty(); } +} diff --git a/tool/src/org/antlr/v4/codegen/model/decl/TokenDecl.java b/tool/src/org/antlr/v4/codegen/model/decl/TokenDecl.java new file mode 100644 index 0000000..0971fb0 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/decl/TokenDecl.java @@ -0,0 +1,42 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.decl; + +import org.antlr.v4.codegen.OutputModelFactory; + +/** x=ID or implicit _tID label */ +public class TokenDecl extends Decl { + public boolean isImplicit; + + public TokenDecl(OutputModelFactory factory, String varName) { + super(factory, varName); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/decl/TokenListDecl.java b/tool/src/org/antlr/v4/codegen/model/decl/TokenListDecl.java new file mode 100644 index 0000000..37b7b0e --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/decl/TokenListDecl.java @@ -0,0 +1,40 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.decl; + +import org.antlr.v4.codegen.OutputModelFactory; + +/** */ +public class TokenListDecl extends TokenDecl { + public TokenListDecl(OutputModelFactory factory, String varName) { + super(factory, varName); + } +} diff --git a/tool/src/org/antlr/v4/codegen/model/decl/TokenTypeDecl.java b/tool/src/org/antlr/v4/codegen/model/decl/TokenTypeDecl.java new file mode 100644 index 0000000..2762ba3 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/model/decl/TokenTypeDecl.java @@ -0,0 +1,40 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.model.decl; + +import org.antlr.v4.codegen.OutputModelFactory; + +/** */ +public class TokenTypeDecl extends Decl { + public TokenTypeDecl(OutputModelFactory factory, String name) { + super(factory, name); + } +} diff --git a/tool/src/org/antlr/v4/codegen/target/CSharpTarget.java b/tool/src/org/antlr/v4/codegen/target/CSharpTarget.java new file mode 100644 index 0000000..09df962 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/target/CSharpTarget.java @@ -0,0 +1,177 @@ +/* + * [The "BSD license"] + * Copyright (c) 2013 Terence Parr + * Copyright (c) 2013 Sam Harwell + * 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.codegen.target; + +import org.antlr.v4.codegen.CodeGenerator; +import org.antlr.v4.codegen.Target; +import org.antlr.v4.tool.ErrorType; +import org.antlr.v4.tool.ast.GrammarAST; +import org.stringtemplate.v4.NumberRenderer; +import org.stringtemplate.v4.STErrorListener; +import org.stringtemplate.v4.STGroup; +import org.stringtemplate.v4.STGroupFile; +import org.stringtemplate.v4.StringRenderer; +import org.stringtemplate.v4.misc.STMessage; + +public class CSharpTarget extends Target { + + public CSharpTarget(CodeGenerator gen) { + super(gen, "CSharp"); + targetCharValueEscape[0] = "\\0"; + targetCharValueEscape[0x0007] = "\\a"; + targetCharValueEscape[0x000B] = "\\v"; + } + + @Override + public String getVersion() { + return "4.5.1"; // crossing fingers that it's close enough. + } + + @Override + public String encodeIntAsCharEscape(int v) { + if (v < Character.MIN_VALUE || v > Character.MAX_VALUE) { + throw new IllegalArgumentException(String.format("Cannot encode the specified value: %d", v)); + } + + if (v >= 0 && v < targetCharValueEscape.length && targetCharValueEscape[v] != null) { + return targetCharValueEscape[v]; + } + + if (v >= 0x20 && v < 127 && (v < '0' || v > '9') && (v < 'a' || v > 'f') && (v < 'A' || v > 'F')) { + return String.valueOf((char)v); + } + + return String.format("\\x%X", v & 0xFFFF); + } + + @Override + public String getTargetStringLiteralFromANTLRStringLiteral( + CodeGenerator generator, + String literal, boolean addQuotes) + { + StringBuilder sb = new StringBuilder(); + String is = literal; + + if ( addQuotes ) sb.append('"'); + + for (int i = 1; i < is.length() -1; i++) { + if (is.charAt(i) == '\\') { + // Anything escaped is what it is! We assume that + // people know how to escape characters correctly. However + // we catch anything that does not need an escape in Java (which + // is what the default implementation is dealing with and remove + // the escape. The C target does this for instance. + // + switch (is.charAt(i+1)) { + // Pass through any escapes that Java also needs + // + case '"': + case 'n': + case 'r': + case 't': + case 'b': + case 'f': + case '\\': + // Pass the escape through + sb.append('\\'); + break; + + case 'u': // Assume unnnn + // Pass the escape through as double \\ + // so that Java leaves as \u0000 string not char + sb.append('\\'); + sb.append('\\'); + break; + + default: + // Remove the escape by virtue of not adding it here + // Thus \' becomes ' and so on + break; + } + + // Go past the \ character + i++; + } else { + // Characters that don't need \ in ANTLR 'strings' but do in Java + if (is.charAt(i) == '"') { + // We need to escape " in Java + sb.append('\\'); + } + } + // Add in the next character, which may have been escaped + sb.append(is.charAt(i)); + } + + if ( addQuotes ) sb.append('"'); + + return sb.toString(); + } + + @Override + protected boolean visibleGrammarSymbolCausesIssueInGeneratedCode(GrammarAST idNode) { + return false; + } + + @Override + protected STGroup loadTemplates() { + // override the superclass behavior to put all C# templates in the same folder + STGroup result = new STGroupFile(CodeGenerator.TEMPLATE_ROOT+"/CSharp/"+getLanguage()+STGroup.GROUP_FILE_EXTENSION); + result.registerRenderer(Integer.class, new NumberRenderer()); + result.registerRenderer(String.class, new StringRenderer()); + result.setListener(new STErrorListener() { + @Override + public void compileTimeError(STMessage msg) { + reportError(msg); + } + + @Override + public void runTimeError(STMessage msg) { + reportError(msg); + } + + @Override + public void IOError(STMessage msg) { + reportError(msg); + } + + @Override + public void internalError(STMessage msg) { + reportError(msg); + } + + private void reportError(STMessage msg) { + getCodeGenerator().tool.errMgr.toolError(ErrorType.STRING_TEMPLATE_WARNING, msg.cause, msg.toString()); + } + }); + + return result; + } + +} diff --git a/tool/src/org/antlr/v4/codegen/target/JavaScriptTarget.java b/tool/src/org/antlr/v4/codegen/target/JavaScriptTarget.java new file mode 100644 index 0000000..26fa4fe --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/target/JavaScriptTarget.java @@ -0,0 +1,234 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.target; + +import org.antlr.v4.codegen.CodeGenerator; +import org.antlr.v4.codegen.Target; +import org.antlr.v4.tool.ast.GrammarAST; +import org.stringtemplate.v4.STGroup; +import org.stringtemplate.v4.StringRenderer; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Locale; +import java.util.Set; + +/** + * + * @author Eric Vergnaud + */ +public class JavaScriptTarget extends Target { + + /** Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar */ + protected static final String[] javaScriptKeywords = { + "break", "case", "class", "catch", "const", "continue", "debugger", + "default", "delete", "do", "else", "export", "extends", "finally", "for", + "function", "if", "import", "in", "instanceof", "let", "new", "return", + "super", "switch", "this", "throw", "try", "typeof", "var", "void", + "while", "with", "yield", + //future reserved + "enum", "await", "implements", "package", "protected", "static", + "interface", "private", "public", + //future reserved in older standards + "abstract", "boolean", "byte", "char", "double", "final", "float", + "goto", "int", "long", "native", "short", "synchronized", "transient", + "volatile", + //literals + "null", "true", "false" + }; + + /** Avoid grammar symbols in this set to prevent conflicts in gen'd code. */ + protected final Set<String> badWords = new HashSet<String>(); + + public JavaScriptTarget(CodeGenerator gen) { + super(gen, "JavaScript"); + } + + @Override + public String getVersion() { + return "4.5.1"; + } + + public Set<String> getBadWords() { + if (badWords.isEmpty()) { + addBadWords(); + } + + return badWords; + } + + protected void addBadWords() { + badWords.addAll(Arrays.asList(javaScriptKeywords)); + badWords.add("rule"); + badWords.add("parserRule"); + } + + /** + * {@inheritDoc} + * <p/> + * For Java, this is the translation {@code 'a\n"'} → {@code "a\n\""}. + * Expect single quotes around the incoming literal. Just flip the quotes + * and replace double quotes with {@code \"}. + * <p/> + * Note that we have decided to allow people to use '\"' without penalty, so + * we must build the target string in a loop as {@link String#replace} + * cannot handle both {@code \"} and {@code "} without a lot of messing + * around. + */ + @Override + public String getTargetStringLiteralFromANTLRStringLiteral( + CodeGenerator generator, + String literal, boolean addQuotes) + { + StringBuilder sb = new StringBuilder(); + String is = literal; + + if ( addQuotes ) sb.append('"'); + + for (int i = 1; i < is.length() -1; i++) { + if (is.charAt(i) == '\\') { + // Anything escaped is what it is! We assume that + // people know how to escape characters correctly. However + // we catch anything that does not need an escape in Java (which + // is what the default implementation is dealing with and remove + // the escape. The C target does this for instance. + // + switch (is.charAt(i+1)) { + // Pass through any escapes that Java also needs + // + case '"': + case 'n': + case 'r': + case 't': + case 'b': + case 'f': + case '\\': + // Pass the escape through + sb.append('\\'); + break; + + case 'u': // Assume unnnn + // Pass the escape through as double \\ + // so that Java leaves as \u0000 string not char + sb.append('\\'); + sb.append('\\'); + break; + + default: + // Remove the escape by virtue of not adding it here + // Thus \' becomes ' and so on + break; + } + + // Go past the \ character + i++; + } else { + // Characters that don't need \ in ANTLR 'strings' but do in Java + if (is.charAt(i) == '"') { + // We need to escape " in Java + sb.append('\\'); + } + } + // Add in the next character, which may have been escaped + sb.append(is.charAt(i)); + } + + if ( addQuotes ) sb.append('"'); + + return sb.toString(); + } + + @Override + public String encodeIntAsCharEscape(int v) { + if (v < Character.MIN_VALUE || v > Character.MAX_VALUE) { + throw new IllegalArgumentException(String.format("Cannot encode the specified value: %d", v)); + } + + if (v >= 0 && v < targetCharValueEscape.length && targetCharValueEscape[v] != null) { + return targetCharValueEscape[v]; + } + + if (v >= 0x20 && v < 127) { + return String.valueOf((char)v); + } + + String hex = Integer.toHexString(v|0x10000).substring(1,5); + return "\\u"+hex; + } + + @Override + public int getSerializedATNSegmentLimit() { + return 2 ^ 31; + } + + @Override + public int getInlineTestSetWordSize() { + return 32; + } + + @Override + protected boolean visibleGrammarSymbolCausesIssueInGeneratedCode(GrammarAST idNode) { + return getBadWords().contains(idNode.getText()); + } + + @Override + protected STGroup loadTemplates() { + STGroup result = super.loadTemplates(); + result.registerRenderer(String.class, new JavaStringRenderer(), true); + return result; + } + + protected static class JavaStringRenderer extends StringRenderer { + + @Override + public String toString(Object o, String formatString, Locale locale) { + if ("java-escape".equals(formatString)) { + // 5C is the hex code for the \ itself + return ((String)o).replace("\\u", "\\u005Cu"); + } + + return super.toString(o, formatString, locale); + } + + } + + public boolean wantsBaseListener() { + return false; + } + + public boolean wantsBaseVisitor() { + return false; + } + + public boolean supportsOverloadedMethods() { + return false; + } +} diff --git a/tool/src/org/antlr/v4/codegen/target/JavaTarget.java b/tool/src/org/antlr/v4/codegen/target/JavaTarget.java new file mode 100644 index 0000000..6ee18da --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/target/JavaTarget.java @@ -0,0 +1,126 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.target; + +import org.antlr.v4.Tool; +import org.antlr.v4.codegen.CodeGenerator; +import org.antlr.v4.codegen.Target; +import org.antlr.v4.tool.ast.GrammarAST; +import org.stringtemplate.v4.STGroup; +import org.stringtemplate.v4.StringRenderer; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Locale; +import java.util.Set; + +public class JavaTarget extends Target { + + /** + * The Java target can cache the code generation templates. + */ + private static final ThreadLocal<STGroup> targetTemplates = new ThreadLocal<STGroup>(); + + protected static final String[] javaKeywords = { + "abstract", "assert", "boolean", "break", "byte", "case", "catch", + "char", "class", "const", "continue", "default", "do", "double", "else", + "enum", "extends", "false", "final", "finally", "float", "for", "goto", + "if", "implements", "import", "instanceof", "int", "interface", + "long", "native", "new", "null", "package", "private", "protected", + "public", "return", "short", "static", "strictfp", "super", "switch", + "synchronized", "this", "throw", "throws", "transient", "true", "try", + "void", "volatile", "while" + }; + + /** Avoid grammar symbols in this set to prevent conflicts in gen'd code. */ + protected final Set<String> badWords = new HashSet<String>(); + + public JavaTarget(CodeGenerator gen) { + super(gen, "Java"); + } + + @Override + public String getVersion() { + return Tool.VERSION; // Java and tool versions move in lock step + } + + public Set<String> getBadWords() { + if (badWords.isEmpty()) { + addBadWords(); + } + + return badWords; + } + + protected void addBadWords() { + badWords.addAll(Arrays.asList(javaKeywords)); + badWords.add("rule"); + badWords.add("parserRule"); + } + + @Override + public int getSerializedATNSegmentLimit() { + // 65535 is the class file format byte limit for a UTF-8 encoded string literal + // 3 is the maximum number of bytes it takes to encode a value in the range 0-0xFFFF + return 65535 / 3; + } + + @Override + protected boolean visibleGrammarSymbolCausesIssueInGeneratedCode(GrammarAST idNode) { + return getBadWords().contains(idNode.getText()); + } + + @Override + protected STGroup loadTemplates() { + STGroup result = targetTemplates.get(); + if (result == null) { + result = super.loadTemplates(); + result.registerRenderer(String.class, new JavaStringRenderer(), true); + targetTemplates.set(result); + } + + return result; + } + + protected static class JavaStringRenderer extends StringRenderer { + + @Override + public String toString(Object o, String formatString, Locale locale) { + if ("java-escape".equals(formatString)) { + // 5C is the hex code for the \ itself + return ((String)o).replace("\\u", "\\u005Cu"); + } + + return super.toString(o, formatString, locale); + } + + } +} diff --git a/tool/src/org/antlr/v4/codegen/target/Python2Target.java b/tool/src/org/antlr/v4/codegen/target/Python2Target.java new file mode 100644 index 0000000..36249d5 --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/target/Python2Target.java @@ -0,0 +1,136 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.target; + +import org.antlr.v4.codegen.CodeGenerator; +import org.antlr.v4.codegen.Target; +import org.antlr.v4.tool.ast.GrammarAST; +import org.stringtemplate.v4.STGroup; +import org.stringtemplate.v4.StringRenderer; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Locale; +import java.util.Set; + +/** + * + * @author Eric Vergnaud + */ +public class Python2Target extends Target { + protected static final String[] python2Keywords = { + "abs", "all", "any", "apply", "as", + "bin", "bool", "buffer", "bytearray", + "callable", "chr", "classmethod", "coerce", "compile", "complex", + "delattr", "dict", "dir", "divmod", + "enumerate", "eval", "execfile", + "file", "filter", "float", "format", "frozenset", + "getattr", "globals", + "hasattr", "hash", "help", "hex", + "id", "input", "int", "intern", "isinstance", "issubclass", "iter", + "len", "list", "locals", + "map", "max", "min", "next", + "memoryview", + "object", "oct", "open", "ord", + "pow", "print", "property", + "range", "raw_input", "reduce", "reload", "repr", "reversed", "round", + "set", "setattr", "slice", "sorted", "staticmethod", "str", "sum", "super", + "tuple", "type", + "unichr", "unicode", + "vars", + "with", + "xrange", + "zip", + "__import__", + "True", "False", "None" + }; + + /** Avoid grammar symbols in this set to prevent conflicts in gen'd code. */ + protected final Set<String> badWords = new HashSet<String>(); + + public Python2Target(CodeGenerator gen) { + super(gen, "Python2"); + } + + @Override + protected boolean visibleGrammarSymbolCausesIssueInGeneratedCode(GrammarAST idNode) { + return getBadWords().contains(idNode.getText()); + } + + @Override + protected STGroup loadTemplates() { + STGroup result = super.loadTemplates(); + result.registerRenderer(String.class, new PythonStringRenderer(), true); + return result; + } + + protected static class PythonStringRenderer extends StringRenderer { + + @Override + public String toString(Object o, String formatString, Locale locale) { + return super.toString(o, formatString, locale); + } + } + + @Override + public boolean wantsBaseListener() { + return false; + } + + @Override + public boolean wantsBaseVisitor() { + return false; + } + + @Override + public boolean supportsOverloadedMethods() { + return false; + } + + @Override + public String getVersion() { + return "4.5.2"; + } + + public Set<String> getBadWords() { + if (badWords.isEmpty()) { + addBadWords(); + } + + return badWords; + } + + protected void addBadWords() { + badWords.addAll(Arrays.asList(python2Keywords)); + badWords.add("rule"); + badWords.add("parserRule"); + } +} diff --git a/tool/src/org/antlr/v4/codegen/target/Python3Target.java b/tool/src/org/antlr/v4/codegen/target/Python3Target.java new file mode 100644 index 0000000..d07247f --- /dev/null +++ b/tool/src/org/antlr/v4/codegen/target/Python3Target.java @@ -0,0 +1,143 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.codegen.target; + +import org.antlr.v4.codegen.CodeGenerator; +import org.antlr.v4.codegen.Target; +import org.antlr.v4.tool.ast.GrammarAST; +import org.stringtemplate.v4.STGroup; +import org.stringtemplate.v4.StringRenderer; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Locale; +import java.util.Set; + +/** + * + * @author Eric Vergnaud + */ +public class Python3Target extends Target { + protected static final String[] python3Keywords = { + "abs", "all", "any", "apply", "as", + "bin", "bool", "buffer", "bytearray", + "callable", "chr", "classmethod", "coerce", "compile", "complex", + "delattr", "dict", "dir", "divmod", + "enumerate", "eval", "execfile", + "file", "filter", "float", "format", "frozenset", + "getattr", "globals", + "hasattr", "hash", "help", "hex", + "id", "input", "int", "intern", "isinstance", "issubclass", "iter", + "len", "list", "locals", + "map", "max", "min", "next", + "memoryview", + "object", "oct", "open", "ord", + "pow", "print", "property", + "range", "raw_input", "reduce", "reload", "repr", "reversed", "round", + "set", "setattr", "slice", "sorted", "staticmethod", "str", "sum", "super", + "tuple", "type", + "unichr", "unicode", + "vars", + "with", + "zip", + "__import__", + "True", "False", "None" + }; + + public Python3Target(CodeGenerator gen) { + super(gen, "Python3"); + } + + @Override + public int getSerializedATNSegmentLimit() { + // set to something stupid to avoid segmentation + return 2 ^ 31; + } + + @Override + protected boolean visibleGrammarSymbolCausesIssueInGeneratedCode(GrammarAST idNode) { + return getBadWords().contains(idNode.getText()); + } + + @Override + protected STGroup loadTemplates() { + STGroup result = super.loadTemplates(); + result.registerRenderer(String.class, new PythonStringRenderer(), true); + return result; + } + + protected static class PythonStringRenderer extends StringRenderer { + + @Override + public String toString(Object o, String formatString, Locale locale) { + return super.toString(o, formatString, locale); + } + } + + @Override + public boolean wantsBaseListener() { + return false; + } + + @Override + public boolean wantsBaseVisitor() { + return false; + } + + @Override + public boolean supportsOverloadedMethods() { + return false; + } + + @Override + public String getVersion() { + return "4.5.2"; + } + + /** Avoid grammar symbols in this set to prevent conflicts in gen'd code. */ + protected final Set<String> badWords = new HashSet<String>(); + + public Set<String> getBadWords() { + if (badWords.isEmpty()) { + addBadWords(); + } + + return badWords; + } + + protected void addBadWords() { + badWords.addAll(Arrays.asList(python3Keywords)); + badWords.add("rule"); + badWords.add("parserRule"); + } + + +} diff --git a/tool/src/org/antlr/v4/gui/BasicFontMetrics.java b/tool/src/org/antlr/v4/gui/BasicFontMetrics.java new file mode 100644 index 0000000..b424a00 --- /dev/null +++ b/tool/src/org/antlr/v4/gui/BasicFontMetrics.java @@ -0,0 +1,95 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.gui; + +/** Font metrics. The only way to generate accurate images + * in any format that contain text is to know the font metrics. + * Specifically, we need to know the width of every character and the + * maximum height (since we want all characters to fit within same line height). + * I used ttf2tfm to dump the font metrics from Mac TrueType fonts and + * then converted that to a Java class for use in a PostScript generator + * for trees. Commands: + * + * <pre> + * $ ttf2tfm /Library/Fonts/Arial\ Black.ttf > metrics + * </pre> + * + * Then run metrics into python code after stripping header/footer: + * + * <pre> + * # + * # Process lines from ttf2tfm that look like this: + * # Glyph Code Glyph Name Width llx lly urx ury + * # ------------------------------------------------------------------------ + * # 3 00020 space 333 0, 0 -- 0, 0 + * # + * lines = open("metrics").read().split('\n') + * print "public class FontName {" + * print " {" + * maxh = 0; + * for line in lines[4:]: # skip header 0..3 + * all = line.split(' ') + * words = [x for x in all if len(x)>0] + * ascii = int(words[1], 16) + * height = int(words[8]) + * if height>maxh: maxh = height + * if ascii>=128: break + * print " widths[%d] = %s; // %s" % (ascii, words[3], words[2]) + * + * print " maxCharHeight = "+str(maxh)+";" + * print " }" + * print "}" + * </pre> + * + * Units are 1000th of an 'em'. + */ +public abstract class BasicFontMetrics { + public static final int MAX_CHAR = '\u00FF'; + protected int maxCharHeight; + protected int[] widths = new int[MAX_CHAR+1]; + + public double getWidth(String s, int fontSize) { + double w = 0; + for (char c : s.toCharArray()) { + w += getWidth(c, fontSize); + } + return w; + } + + public double getWidth(char c, int fontSize) { + if ( c > MAX_CHAR || widths[c]==0 ) return widths['m']/1000.0; // return width('m') + return widths[c]/1000.0 * fontSize; + } + + public double getLineHeight(int fontSize) { + return maxCharHeight / 1000.0 * fontSize; + } +} diff --git a/tool/src/org/antlr/v4/gui/GraphicsSupport.java b/tool/src/org/antlr/v4/gui/GraphicsSupport.java new file mode 100644 index 0000000..eb9546d --- /dev/null +++ b/tool/src/org/antlr/v4/gui/GraphicsSupport.java @@ -0,0 +1,138 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.gui; + +import javax.imageio.ImageIO; +import javax.print.DocFlavor; +import javax.print.DocPrintJob; +import javax.print.PrintException; +import javax.print.PrintService; +import javax.print.SimpleDoc; +import javax.print.StreamPrintServiceFactory; +import javax.print.attribute.HashPrintRequestAttributeSet; +import javax.print.attribute.PrintRequestAttributeSet; +import javax.swing.*; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.awt.print.PageFormat; +import java.awt.print.Printable; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Arrays; + +public class GraphicsSupport { + /** + [The "BSD license"] + Copyright (c) 2011 Cay Horstmann + 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. + */ + public static void saveImage(final JComponent comp, String fileName) + throws IOException, PrintException + { + if ( fileName.endsWith(".ps") || fileName.endsWith(".eps") ) { + DocFlavor flavor = DocFlavor.SERVICE_FORMATTED.PRINTABLE; + String mimeType = "application/postscript"; + StreamPrintServiceFactory[] factories = + StreamPrintServiceFactory.lookupStreamPrintServiceFactories(flavor, mimeType); + System.out.println(Arrays.toString(factories)); + if (factories.length > 0) { + FileOutputStream out = new FileOutputStream(fileName); + PrintService service = factories[0].getPrintService(out); + SimpleDoc doc = new SimpleDoc(new Printable() { + @Override + public int print(Graphics g, PageFormat pf, int page) { + if (page >= 1) return Printable.NO_SUCH_PAGE; + else { + Graphics2D g2 = (Graphics2D) g; + g2.translate((pf.getWidth() - pf.getImageableWidth()) / 2, + (pf.getHeight() - pf.getImageableHeight()) / 2); + if ( comp.getWidth() > pf.getImageableWidth() || + comp.getHeight() > pf.getImageableHeight() ) + { + double sf1 = pf.getImageableWidth() / (comp.getWidth() + 1); + double sf2 = pf.getImageableHeight() / (comp.getHeight() + 1); + double s = Math.min(sf1, sf2); + g2.scale(s, s); + } + + comp.paint(g); + return Printable.PAGE_EXISTS; + } + } + }, flavor, null); + DocPrintJob job = service.createPrintJob(); + PrintRequestAttributeSet attributes = new HashPrintRequestAttributeSet(); + job.print(doc, attributes); + out.close(); + } + } else { + // parrt: works with [image/jpeg, image/png, image/x-png, image/vnd.wap.wbmp, image/bmp, image/gif] + Rectangle rect = comp.getBounds(); + BufferedImage image = new BufferedImage(rect.width, rect.height, + BufferedImage.TYPE_INT_RGB); + Graphics2D g = (Graphics2D) image.getGraphics(); + g.setColor(Color.WHITE); + g.fill(rect); +// g.setColor(Color.BLACK); + comp.paint(g); + String extension = fileName.substring(fileName.lastIndexOf('.') + 1); + boolean result = ImageIO.write(image, extension, new File(fileName)); + if ( !result ) { + System.err.println("Now imager for " + extension); + } + g.dispose(); + } + } +} diff --git a/tool/src/org/antlr/v4/gui/JFileChooserConfirmOverwrite.java b/tool/src/org/antlr/v4/gui/JFileChooserConfirmOverwrite.java new file mode 100644 index 0000000..59cbb69 --- /dev/null +++ b/tool/src/org/antlr/v4/gui/JFileChooserConfirmOverwrite.java @@ -0,0 +1,63 @@ +/* + * [The "BSD license"] + * Copyright (c) 2013 Terence Parr + * Copyright (c) 2013 Sam Harwell + * 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.gui; + +import javax.swing.*; +import java.io.File; + +/** + * + * @author Sam Harwell + */ +public class JFileChooserConfirmOverwrite extends JFileChooser { + + public JFileChooserConfirmOverwrite() { + setMultiSelectionEnabled(false); + } + + @Override + public void approveSelection() { + File selectedFile = getSelectedFile(); + + if (selectedFile.exists()) { + int answer = JOptionPane.showConfirmDialog(this, + "Overwrite existing file?", + "Overwrite?", + JOptionPane.YES_NO_OPTION); + if (answer != JOptionPane.YES_OPTION) { + // do not call super.approveSelection + return; + } + } + + super.approveSelection(); + } + +} diff --git a/tool/src/org/antlr/v4/gui/PostScriptDocument.java b/tool/src/org/antlr/v4/gui/PostScriptDocument.java new file mode 100644 index 0000000..7faf2db --- /dev/null +++ b/tool/src/org/antlr/v4/gui/PostScriptDocument.java @@ -0,0 +1,212 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.gui; + +import java.awt.*; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +public class PostScriptDocument { + public static final String DEFAULT_FONT = "CourierNew"; + + public static final Map<String, String> POSTSCRIPT_FONT_NAMES; + static { + POSTSCRIPT_FONT_NAMES = new HashMap<String, String>(); + POSTSCRIPT_FONT_NAMES.put(Font.SANS_SERIF + ".plain", "ArialMT"); + POSTSCRIPT_FONT_NAMES.put(Font.SANS_SERIF + ".bold", "Arial-BoldMT"); + POSTSCRIPT_FONT_NAMES.put(Font.SANS_SERIF + ".italic", "Arial-ItalicMT"); + POSTSCRIPT_FONT_NAMES.put(Font.SANS_SERIF + ".bolditalic", "Arial-BoldItalicMT"); + POSTSCRIPT_FONT_NAMES.put(Font.SERIF + ".plain", "TimesNewRomanPSMT"); + POSTSCRIPT_FONT_NAMES.put(Font.SERIF + ".bold", "TimesNewRomanPS-BoldMT"); + POSTSCRIPT_FONT_NAMES.put(Font.SERIF + ".italic", "TimesNewRomanPS-ItalicMT"); + POSTSCRIPT_FONT_NAMES.put(Font.SERIF + ".bolditalic", "TimesNewRomanPS-BoldItalicMT"); + POSTSCRIPT_FONT_NAMES.put(Font.MONOSPACED + ".plain", "CourierNewPSMT"); + POSTSCRIPT_FONT_NAMES.put(Font.MONOSPACED + ".bold", "CourierNewPS-BoldMT"); + POSTSCRIPT_FONT_NAMES.put(Font.MONOSPACED + ".italic", "CourierNewPS-ItalicMT"); + POSTSCRIPT_FONT_NAMES.put(Font.MONOSPACED + ".bolditalic", "CourierNewPS-BoldItalicMT"); + } + + protected int boundingBoxWidth; + protected int boundingBoxHeight; + + protected SystemFontMetrics fontMetrics; + protected String fontName; + protected int fontSize = 12; + protected double lineWidth = 0.3; + protected String boundingBox; + + protected StringBuilder ps = new StringBuilder(); + protected boolean closed = false; + + public PostScriptDocument() { + this(DEFAULT_FONT, 12); + } + + public PostScriptDocument(String fontName, int fontSize) { + header(); + setFont(fontName, fontSize); + } + + public String getPS() { + close(); + return header()+ps.toString(); + } + + public void boundingBox(int w, int h) { + boundingBoxWidth = w; + boundingBoxHeight = h; + boundingBox = String.format(Locale.US, "%%%%BoundingBox: %d %d %d %d\n", 0,0, + boundingBoxWidth,boundingBoxHeight); + } + + public void close() { + if ( closed ) return; +// ps.append("showpage\n"); + ps.append("%%Trailer\n"); + closed = true; + } + + /** Compute the header separately because we need to wait for the bounding box */ + protected StringBuilder header() { + StringBuilder b = new StringBuilder(); + b.append("%!PS-Adobe-3.0 EPSF-3.0\n"); + b.append(boundingBox).append("\n"); + b.append("0.3 setlinewidth\n"); + b.append("%% x y w h highlight\n" + + "/highlight {\n" + + " 4 dict begin\n" + + " /h exch def\n" + + " /w exch def\n" + + " /y exch def\n" + + " /x exch def\n" + + " gsave\n" + + " newpath\n" + + " x y moveto\n" + + " 0 h rlineto % up to left corner\n" + + " w 0 rlineto % to upper right corner\n" + + " 0 h neg rlineto % to lower right corner\n" + + " w neg 0 rlineto % back home to lower left corner\n" + + " closepath\n" + + " .95 .83 .82 setrgbcolor\n" + + " fill\n" + + " grestore\n" + + " end\n" + + "} def\n"); + + return b; + } + + public void setFont(String fontName, int fontSize) { + this.fontMetrics = new SystemFontMetrics(fontName); + this.fontName = fontMetrics.getFont().getPSName(); + this.fontSize = fontSize; + + String psname = POSTSCRIPT_FONT_NAMES.get(this.fontName); + if (psname == null) { + psname = this.fontName; + } + + ps.append(String.format(Locale.US, "/%s findfont %d scalefont setfont\n", psname, fontSize)); + } + + public void lineWidth(double w) { + lineWidth = w; + ps.append(w).append(" setlinewidth\n"); + } + + public void move(double x, double y) { + ps.append(String.format(Locale.US, "%1.3f %1.3f moveto\n", x, y)); + } + + public void lineto(double x, double y) { + ps.append(String.format(Locale.US, "%1.3f %1.3f lineto\n", x, y)); + } + + public void line(double x1, double y1, double x2, double y2) { + move(x1, y1); + lineto(x2, y2); + } + + public void rect(double x, double y, double width, double height) { + line(x, y, x, y + height); + line(x, y + height, x + width, y + height); + line(x + width, y + height, x + width, y); + line(x + width, y, x, y); + } + + /** Make red box */ + public void highlight(double x, double y, double width, double height) { + ps.append(String.format(Locale.US, "%1.3f %1.3f %1.3f %1.3f highlight\n", x, y, width, height)); + } + + public void stroke() { + ps.append("stroke\n"); + } + +// public void rarrow(double x, double y) { +// ps.append(String.format(Locale.US, "%1.3f %1.3f rarrow\n", x,y)); +// } +// +// public void darrow(double x, double y) { +// ps.append(String.format(Locale.US, "%1.3f %1.3f darrow\n", x,y)); +// } + + public void text(String s, double x, double y) { + StringBuilder buf = new StringBuilder(); + // escape \, (, ): \\, \(, \) + for (char c : s.toCharArray()) { + switch ( c ) { + case '\\' : + case '(' : + case ')' : + buf.append('\\'); + buf.append(c); + break; + default : + buf.append(c); + break; + } + } + s = buf.toString(); + move(x,y); + ps.append(String.format(Locale.US, "(%s) show\n", s)); + stroke(); + } + + // courier new: wid/hei 7.611979 10.0625 + /** All chars are 600 thousands of an 'em' wide if courier */ + public double getWidth(char c) { return fontMetrics.getWidth(c, fontSize); } + public double getWidth(String s) { return fontMetrics.getWidth(s, fontSize); } + public double getLineHeight() { return fontMetrics.getLineHeight(fontSize); } + + public int getFontSize() { return fontSize; } +} diff --git a/tool/src/org/antlr/v4/gui/SystemFontMetrics.java b/tool/src/org/antlr/v4/gui/SystemFontMetrics.java new file mode 100644 index 0000000..fb2b17a --- /dev/null +++ b/tool/src/org/antlr/v4/gui/SystemFontMetrics.java @@ -0,0 +1,63 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.gui; + +import java.awt.*; +import java.awt.font.FontRenderContext; +import java.awt.font.TextLayout; +import java.awt.image.BufferedImage; + +/** + * + * @author Sam Harwell + */ +public class SystemFontMetrics extends BasicFontMetrics { + protected final Font font; + + public SystemFontMetrics(String fontName) { + BufferedImage img = new BufferedImage(40, 40, BufferedImage.TYPE_4BYTE_ABGR); + Graphics2D graphics = GraphicsEnvironment.getLocalGraphicsEnvironment().createGraphics(img); + FontRenderContext fontRenderContext = graphics.getFontRenderContext(); + this.font = new Font(fontName, Font.PLAIN, 1000); + double maxHeight = 0; + for (int i = 0; i < 255; i++) { + TextLayout layout = new TextLayout(Character.toString((char)i), font, fontRenderContext); + maxHeight = Math.max(maxHeight, layout.getBounds().getHeight()); + super.widths[i] = (int)layout.getAdvance(); + } + + super.maxCharHeight = (int)Math.round(maxHeight); + } + + public Font getFont() { + return font; + } +} diff --git a/tool/src/org/antlr/v4/gui/TestRig.java b/tool/src/org/antlr/v4/gui/TestRig.java new file mode 100644 index 0000000..4812e5c --- /dev/null +++ b/tool/src/org/antlr/v4/gui/TestRig.java @@ -0,0 +1,270 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.gui; + +import org.antlr.v4.runtime.ANTLRInputStream; +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.DiagnosticErrorListener; +import org.antlr.v4.runtime.Lexer; +import org.antlr.v4.runtime.Parser; +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.TokenStream; +import org.antlr.v4.runtime.atn.PredictionMode; + +import javax.print.PrintException; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +/** Run a lexer/parser combo, optionally printing tree string or generating + * postscript file. Optionally taking input file. + * + * $ java org.antlr.v4.runtime.misc.TestRig GrammarName startRuleName + * [-tree] + * [-tokens] [-gui] [-ps file.ps] + * [-trace] + * [-diagnostics] + * [-SLL] + * [input-filename(s)] + */ +public class TestRig { + public static final String LEXER_START_RULE_NAME = "tokens"; + + protected String grammarName; + protected String startRuleName; + protected final List<String> inputFiles = new ArrayList<String>(); + protected boolean printTree = false; + protected boolean gui = false; + protected String psFile = null; + protected boolean showTokens = false; + protected boolean trace = false; + protected boolean diagnostics = false; + protected String encoding = null; + protected boolean SLL = false; + + public TestRig(String[] args) throws Exception { + if ( args.length < 2 ) { + System.err.println("java org.antlr.v4.runtime.misc.TestRig GrammarName startRuleName\n" + + " [-tokens] [-tree] [-gui] [-ps file.ps] [-encoding encodingname]\n" + + " [-trace] [-diagnostics] [-SLL]\n"+ + " [input-filename(s)]"); + System.err.println("Use startRuleName='tokens' if GrammarName is a lexer grammar."); + System.err.println("Omitting input-filename makes rig read from stdin."); + return; + } + int i=0; + grammarName = args[i]; + i++; + startRuleName = args[i]; + i++; + while ( i<args.length ) { + String arg = args[i]; + i++; + if ( arg.charAt(0)!='-' ) { // input file name + inputFiles.add(arg); + continue; + } + if ( arg.equals("-tree") ) { + printTree = true; + } + if ( arg.equals("-gui") ) { + gui = true; + } + if ( arg.equals("-tokens") ) { + showTokens = true; + } + else if ( arg.equals("-trace") ) { + trace = true; + } + else if ( arg.equals("-SLL") ) { + SLL = true; + } + else if ( arg.equals("-diagnostics") ) { + diagnostics = true; + } + else if ( arg.equals("-encoding") ) { + if ( i>=args.length ) { + System.err.println("missing encoding on -encoding"); + return; + } + encoding = args[i]; + i++; + } + else if ( arg.equals("-ps") ) { + if ( i>=args.length ) { + System.err.println("missing filename on -ps"); + return; + } + psFile = args[i]; + i++; + } + } + } + + public static void main(String[] args) throws Exception { + TestRig testRig = new TestRig(args); + if(args.length >= 2) { + testRig.process(); + } + } + + public void process() throws Exception { +// System.out.println("exec "+grammarName+"."+startRuleName); + String lexerName = grammarName+"Lexer"; + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + Class<? extends Lexer> lexerClass = null; + try { + lexerClass = cl.loadClass(lexerName).asSubclass(Lexer.class); + } + catch (java.lang.ClassNotFoundException cnfe) { + // might be pure lexer grammar; no Lexer suffix then + lexerName = grammarName; + try { + lexerClass = cl.loadClass(lexerName).asSubclass(Lexer.class); + } + catch (ClassNotFoundException cnfe2) { + System.err.println("Can't load "+lexerName+" as lexer or parser"); + return; + } + } + + Constructor<? extends Lexer> lexerCtor = lexerClass.getConstructor(CharStream.class); + Lexer lexer = lexerCtor.newInstance((CharStream)null); + + Class<? extends Parser> parserClass = null; + Parser parser = null; + if ( !startRuleName.equals(LEXER_START_RULE_NAME) ) { + String parserName = grammarName+"Parser"; + parserClass = cl.loadClass(parserName).asSubclass(Parser.class); + if ( parserClass==null ) { + System.err.println("Can't load "+parserName); + } + Constructor<? extends Parser> parserCtor = parserClass.getConstructor(TokenStream.class); + parser = parserCtor.newInstance((TokenStream)null); + } + + if ( inputFiles.size()==0 ) { + InputStream is = System.in; + Reader r; + if ( encoding!=null ) { + r = new InputStreamReader(is, encoding); + } + else { + r = new InputStreamReader(is); + } + + process(lexer, parserClass, parser, is, r); + return; + } + for (String inputFile : inputFiles) { + InputStream is = System.in; + if ( inputFile!=null ) { + is = new FileInputStream(inputFile); + } + Reader r; + if ( encoding!=null ) { + r = new InputStreamReader(is, encoding); + } + else { + r = new InputStreamReader(is); + } + + if ( inputFiles.size()>1 ) { + System.err.println(inputFile); + } + process(lexer, parserClass, parser, is, r); + } + } + + protected void process(Lexer lexer, Class<? extends Parser> parserClass, Parser parser, InputStream is, Reader r) throws IOException, IllegalAccessException, InvocationTargetException, PrintException { + try { + ANTLRInputStream input = new ANTLRInputStream(r); + lexer.setInputStream(input); + CommonTokenStream tokens = new CommonTokenStream(lexer); + + tokens.fill(); + + if ( showTokens ) { + for (Object tok : tokens.getTokens()) { + System.out.println(tok); + } + } + + if ( startRuleName.equals(LEXER_START_RULE_NAME) ) return; + + if ( diagnostics ) { + parser.addErrorListener(new DiagnosticErrorListener()); + parser.getInterpreter().setPredictionMode(PredictionMode.LL_EXACT_AMBIG_DETECTION); + } + + if ( printTree || gui || psFile!=null ) { + parser.setBuildParseTree(true); + } + + if ( SLL ) { // overrides diagnostics + parser.getInterpreter().setPredictionMode(PredictionMode.SLL); + } + + parser.setTokenStream(tokens); + parser.setTrace(trace); + + try { + Method startRule = parserClass.getMethod(startRuleName); + ParserRuleContext tree = (ParserRuleContext)startRule.invoke(parser, (Object[])null); + + if ( printTree ) { + System.out.println(tree.toStringTree(parser)); + } + if ( gui ) { + Trees.inspect(tree, parser); + } + if ( psFile!=null ) { + Trees.save(tree, parser, psFile); // Generate postscript + } + } + catch (NoSuchMethodException nsme) { + System.err.println("No method for rule "+startRuleName+" or it has arguments"); + } + } + finally { + if ( r!=null ) r.close(); + if ( is!=null ) is.close(); + } + } +} diff --git a/tool/src/org/antlr/v4/gui/TreeLayoutAdaptor.java b/tool/src/org/antlr/v4/gui/TreeLayoutAdaptor.java new file mode 100644 index 0000000..6024e3b --- /dev/null +++ b/tool/src/org/antlr/v4/gui/TreeLayoutAdaptor.java @@ -0,0 +1,149 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.gui; + +import org.abego.treelayout.TreeForTreeLayout; +import org.antlr.v4.runtime.tree.Tree; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** Adaptor ANTLR trees to {@link TreeForTreeLayout}. */ +public class TreeLayoutAdaptor implements TreeForTreeLayout<Tree> { + private static class AntlrTreeChildrenIterable implements Iterable<Tree> { + private final Tree tree; + + public AntlrTreeChildrenIterable(Tree tree) { + this.tree = tree; + } + + @Override + public Iterator<Tree> iterator() { + return new Iterator<Tree>() { + private int i = 0; + + @Override + public boolean hasNext() { + return tree.getChildCount() > i; + } + + @Override + public Tree next() { + if (!hasNext()) + throw new NoSuchElementException(); + + return tree.getChild(i++); + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + } + + private static class AntlrTreeChildrenReverseIterable implements + Iterable<Tree> + { + private final Tree tree; + + public AntlrTreeChildrenReverseIterable(Tree tree) { + this.tree = tree; + } + + @Override + public Iterator<Tree> iterator() { + return new Iterator<Tree>() { + private int i = tree.getChildCount(); + + @Override + public boolean hasNext() { + return i > 0; + } + + @Override + public Tree next() { + if (!hasNext()) + throw new NoSuchElementException(); + + return tree.getChild(--i); + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + } + + private Tree root; + + public TreeLayoutAdaptor(Tree root) { + this.root = root; + } + + @Override + public boolean isLeaf(Tree node) { + return node.getChildCount() == 0; + } + + @Override + public boolean isChildOfParent(Tree node, Tree parentNode) { + return node.getParent() == parentNode; + } + + @Override + public Tree getRoot() { + return root; + } + + @Override + public Tree getLastChild(Tree parentNode) { + return parentNode.getChild(parentNode.getChildCount() - 1); + } + + @Override + public Tree getFirstChild(Tree parentNode) { + return parentNode.getChild(0); + } + + @Override + public Iterable<Tree> getChildrenReverse(Tree node) { + return new AntlrTreeChildrenReverseIterable(node); + } + + @Override + public Iterable<Tree> getChildren(Tree node) { + return new AntlrTreeChildrenIterable(node); + } +} diff --git a/tool/src/org/antlr/v4/gui/TreePostScriptGenerator.java b/tool/src/org/antlr/v4/gui/TreePostScriptGenerator.java new file mode 100644 index 0000000..d78a1c8 --- /dev/null +++ b/tool/src/org/antlr/v4/gui/TreePostScriptGenerator.java @@ -0,0 +1,176 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.gui; + +import org.abego.treelayout.Configuration; +import org.abego.treelayout.NodeExtentProvider; +import org.abego.treelayout.TreeForTreeLayout; +import org.abego.treelayout.TreeLayout; +import org.abego.treelayout.util.DefaultConfiguration; +import org.antlr.v4.runtime.misc.Utils; +import org.antlr.v4.runtime.tree.ErrorNode; +import org.antlr.v4.runtime.tree.Tree; + +import java.awt.*; +import java.awt.geom.Rectangle2D; +import java.util.List; + +public class TreePostScriptGenerator { + public class VariableExtentProvide implements NodeExtentProvider<Tree> { + @Override + public double getWidth(Tree tree) { + String s = getText(tree); + return doc.getWidth(s) + nodeWidthPadding*2; + } + + @Override + public double getHeight(Tree tree) { + String s = getText(tree); + double h = + doc.getLineHeight() + nodeHeightPaddingAbove + nodeHeightPaddingBelow; + String[] lines = s.split("\n"); + return h * lines.length; + } + } + + protected double gapBetweenLevels = 17; + protected double gapBetweenNodes = 7; + protected int nodeWidthPadding = 1; // added to left/right + protected int nodeHeightPaddingAbove = 0; + protected int nodeHeightPaddingBelow = 5; + + protected Tree root; + protected TreeTextProvider treeTextProvider; + protected TreeLayout<Tree> treeLayout; + + protected PostScriptDocument doc; + + public TreePostScriptGenerator(List<String> ruleNames, Tree root) { + this(ruleNames, root, PostScriptDocument.DEFAULT_FONT, 11); + } + + public TreePostScriptGenerator(List<String> ruleNames, Tree root, + String fontName, int fontSize) + { + this.root = root; + setTreeTextProvider(new TreeViewer.DefaultTreeTextProvider(ruleNames)); + doc = new PostScriptDocument(fontName, fontSize); + boolean compareNodeIdentities = true; + this.treeLayout = + new TreeLayout<Tree>(getTreeLayoutAdaptor(root), + new VariableExtentProvide(), + new DefaultConfiguration<Tree>(gapBetweenLevels, + gapBetweenNodes, + Configuration.Location.Bottom), + compareNodeIdentities); + } + + /** Get an adaptor for root that indicates how to walk ANTLR trees. + * Override to change the adapter from the default of {@link TreeLayoutAdaptor} */ + public TreeForTreeLayout<Tree> getTreeLayoutAdaptor(Tree root) { + return new TreeLayoutAdaptor(root); + } + + public String getPS() { + // generate the edges and boxes (with text) + generateEdges(getTree().getRoot()); + for (Tree node : treeLayout.getNodeBounds().keySet()) { + generateNode(node); + } + + Dimension size = treeLayout.getBounds().getBounds().getSize(); + doc.boundingBox(size.width, size.height); + doc.close(); + return doc.getPS(); + } + + protected void generateEdges(Tree parent) { + if (!getTree().isLeaf(parent)) { + Rectangle2D.Double parentBounds = getBoundsOfNode(parent); +// System.out.println("%% parent("+getText(parent)+")="+parentBounds); + double x1 = parentBounds.getCenterX(); + double y1 = parentBounds.y; + for (Tree child : getChildren(parent)) { + Rectangle2D.Double childBounds = getBoundsOfNode(child); +// System.out.println("%% child("+getText(child)+")="+childBounds); + double x2 = childBounds.getCenterX(); + double y2 = childBounds.getMaxY(); + doc.line(x1, y1, x2, y2); + generateEdges(child); + } + } + } + + protected void generateNode(Tree t) { + // draw the text on top of the box (possibly multiple lines) + String[] lines = getText(t).split("\n"); + Rectangle2D.Double box = getBoundsOfNode(t); + // for debugging, turn this on to see boundingbox of nodes + //doc.rect(box.x, box.y, box.width, box.height); + // make error nodes from parse tree red by default + if ( t instanceof ErrorNode ) { + doc.highlight(box.x, box.y, box.width, box.height); + } + double x = box.x+nodeWidthPadding; + double y = box.y+nodeHeightPaddingBelow; + for (int i = 0; i < lines.length; i++) { + doc.text(lines[i], x, y); + y += doc.getLineHeight(); + } + } + + protected TreeForTreeLayout<Tree> getTree() { + return treeLayout.getTree(); + } + + protected Iterable<Tree> getChildren(Tree parent) { + return getTree().getChildren(parent); + } + + protected Rectangle2D.Double getBoundsOfNode(Tree node) { + return treeLayout.getNodeBounds().get(node); + } + + protected String getText(Tree tree) { + String s = treeTextProvider.getText(tree); + s = Utils.escapeWhitespace(s, false); + return s; + } + + public TreeTextProvider getTreeTextProvider() { + return treeTextProvider; + } + + public void setTreeTextProvider(TreeTextProvider treeTextProvider) { + this.treeTextProvider = treeTextProvider; + } + +} diff --git a/tool/src/org/antlr/v4/gui/TreeTextProvider.java b/tool/src/org/antlr/v4/gui/TreeTextProvider.java new file mode 100644 index 0000000..9a88fc0 --- /dev/null +++ b/tool/src/org/antlr/v4/gui/TreeTextProvider.java @@ -0,0 +1,37 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.gui; + +import org.antlr.v4.runtime.tree.Tree; + +public interface TreeTextProvider { + String getText(Tree node); +} diff --git a/tool/src/org/antlr/v4/gui/TreeViewer.java b/tool/src/org/antlr/v4/gui/TreeViewer.java new file mode 100644 index 0000000..0ca15f3 --- /dev/null +++ b/tool/src/org/antlr/v4/gui/TreeViewer.java @@ -0,0 +1,764 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.gui; + +import org.abego.treelayout.NodeExtentProvider; +import org.abego.treelayout.TreeForTreeLayout; +import org.abego.treelayout.TreeLayout; +import org.abego.treelayout.util.DefaultConfiguration; +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.misc.Utils; +import org.antlr.v4.runtime.tree.ErrorNode; +import org.antlr.v4.runtime.tree.Tree; +import org.antlr.v4.runtime.tree.Trees; + +import javax.imageio.ImageIO; +import javax.print.PrintException; +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.TreeSelectionListener; +import javax.swing.filechooser.FileFilter; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.TreePath; +import javax.swing.tree.TreeSelectionModel; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; +import java.awt.geom.CubicCurve2D; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.prefs.Preferences; + +public class TreeViewer extends JComponent { + public static final Color LIGHT_RED = new Color(244, 213, 211); + + public static class DefaultTreeTextProvider implements TreeTextProvider { + private final List<String> ruleNames; + + public DefaultTreeTextProvider(List<String> ruleNames) { + this.ruleNames = ruleNames; + } + + @Override + public String getText(Tree node) { + return String.valueOf(Trees.getNodeText(node, ruleNames)); + } + } + + public static class VariableExtentProvide implements NodeExtentProvider<Tree> { + TreeViewer viewer; + public VariableExtentProvide(TreeViewer viewer) { + this.viewer = viewer; + } + @Override + public double getWidth(Tree tree) { + FontMetrics fontMetrics = viewer.getFontMetrics(viewer.font); + String s = viewer.getText(tree); + int w = fontMetrics.stringWidth(s) + viewer.nodeWidthPadding*2; + return w; + } + + @Override + public double getHeight(Tree tree) { + FontMetrics fontMetrics = viewer.getFontMetrics(viewer.font); + int h = fontMetrics.getHeight() + viewer.nodeHeightPadding*2; + String s = viewer.getText(tree); + String[] lines = s.split("\n"); + return h * lines.length; + } + } + + protected TreeTextProvider treeTextProvider; + protected TreeLayout<Tree> treeLayout; + protected java.util.List<Tree> highlightedNodes; + + protected String fontName = "Helvetica"; //Font.SANS_SERIF; + protected int fontStyle = Font.PLAIN; + protected int fontSize = 11; + protected Font font = new Font(fontName, fontStyle, fontSize); + + protected double gapBetweenLevels = 17; + protected double gapBetweenNodes = 7; + protected int nodeWidthPadding = 2; // added to left/right + protected int nodeHeightPadding = 0; // added above/below + protected int arcSize = 0; // make an arc in node outline? + + protected double scale = 1.0; + + protected Color boxColor = null; // set to a color to make it draw background + + protected Color highlightedBoxColor = Color.lightGray; + protected Color borderColor = null; + protected Color textColor = Color.black; + + public TreeViewer(List<String> ruleNames, Tree tree) { + setRuleNames(ruleNames); + if ( tree!=null ) { + setTree(tree); + } + setFont(font); + } + + private void updatePreferredSize() { + setPreferredSize(getScaledTreeSize()); + invalidate(); + if (getParent() != null) { + getParent().validate(); + } + repaint(); + } + + // ---------------- PAINT ----------------------------------------------- + + private boolean useCurvedEdges = false; + + public boolean getUseCurvedEdges() { + return useCurvedEdges; + } + + public void setUseCurvedEdges(boolean useCurvedEdges) { + this.useCurvedEdges = useCurvedEdges; + } + + protected void paintEdges(Graphics g, Tree parent) { + if (!getTree().isLeaf(parent)) { + BasicStroke stroke = new BasicStroke(1.0f, BasicStroke.CAP_ROUND, + BasicStroke.JOIN_ROUND); + ((Graphics2D)g).setStroke(stroke); + + Rectangle2D.Double parentBounds = getBoundsOfNode(parent); + double x1 = parentBounds.getCenterX(); + double y1 = parentBounds.getMaxY(); + for (Tree child : getTree().getChildren(parent)) { + Rectangle2D.Double childBounds = getBoundsOfNode(child); + double x2 = childBounds.getCenterX(); + double y2 = childBounds.getMinY(); + if (getUseCurvedEdges()) { + CubicCurve2D c = new CubicCurve2D.Double(); + double ctrlx1 = x1; + double ctrly1 = (y1+y2)/2; + double ctrlx2 = x2; + double ctrly2 = y1; + c.setCurve(x1, y1, ctrlx1, ctrly1, ctrlx2, ctrly2, x2, y2); + ((Graphics2D) g).draw(c); + } else { + g.drawLine((int) x1, (int) y1, + (int) x2, (int) y2); + } + paintEdges(g, child); + } + } + } + + protected void paintBox(Graphics g, Tree tree) { + Rectangle2D.Double box = getBoundsOfNode(tree); + // draw the box in the background + boolean ruleFailedAndMatchedNothing = false; + if ( tree instanceof ParserRuleContext ) { + ParserRuleContext ctx = (ParserRuleContext) tree; + ruleFailedAndMatchedNothing = ctx.exception != null && + ctx.stop != null && ctx.stop.getTokenIndex() < ctx.start.getTokenIndex(); + } + if ( isHighlighted(tree) || boxColor!=null || + tree instanceof ErrorNode || + ruleFailedAndMatchedNothing) + { + if ( isHighlighted(tree) ) g.setColor(highlightedBoxColor); + else if ( tree instanceof ErrorNode || ruleFailedAndMatchedNothing ) g.setColor(LIGHT_RED); + else g.setColor(boxColor); + g.fillRoundRect((int) box.x, (int) box.y, (int) box.width - 1, + (int) box.height - 1, arcSize, arcSize); + } + if ( borderColor!=null ) { + g.setColor(borderColor); + g.drawRoundRect((int) box.x, (int) box.y, (int) box.width - 1, + (int) box.height - 1, arcSize, arcSize); + } + + // draw the text on top of the box (possibly multiple lines) + g.setColor(textColor); + String s = getText(tree); + String[] lines = s.split("\n"); + FontMetrics m = getFontMetrics(font); + int x = (int) box.x + arcSize / 2 + nodeWidthPadding; + int y = (int) box.y + m.getAscent() + m.getLeading() + 1 + nodeHeightPadding; + for (int i = 0; i < lines.length; i++) { + text(g, lines[i], x, y); + y += m.getHeight(); + } + } + + public void text(Graphics g, String s, int x, int y) { +// System.out.println("drawing '"+s+"' @ "+x+","+y); + s = Utils.escapeWhitespace(s, true); + g.drawString(s, x, y); + } + + @Override + public void paint(Graphics g) { + super.paint(g); + + if ( treeLayout==null ) { + return; + } + + Graphics2D g2 = (Graphics2D)g; + // anti-alias the lines + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + + // Anti-alias the text + g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + +// AffineTransform at = g2.getTransform(); +// g2.scale( +// (double) this.getWidth() / 400, +// (double) this.getHeight() / 400); +// +// g2.setTransform(at); + + paintEdges(g, getTree().getRoot()); + + // paint the boxes + for (Tree Tree : treeLayout.getNodeBounds().keySet()) { + paintBox(g, Tree); + } + } + + @Override + protected Graphics getComponentGraphics(Graphics g) { + Graphics2D g2d=(Graphics2D)g; + g2d.scale(scale, scale); + return super.getComponentGraphics(g2d); + } + + // ---------------------------------------------------------------------- + + + private static final String DIALOG_WIDTH_PREFS_KEY = "dialog_width"; + private static final String DIALOG_HEIGHT_PREFS_KEY = "dialog_height"; + private static final String DIALOG_X_PREFS_KEY = "dialog_x"; + private static final String DIALOG_Y_PREFS_KEY = "dialog_y"; + private static final String DIALOG_DIVIDER_LOC_PREFS_KEY = "dialog_divider_location"; + private static final String DIALOG_VIEWER_SCALE_PREFS_KEY = "dialog_viewer_scale"; + + protected static JDialog showInDialog(final TreeViewer viewer) { + final JDialog dialog = new JDialog(); + dialog.setTitle("Parse Tree Inspector"); + + final Preferences prefs = Preferences.userNodeForPackage(TreeViewer.class); + + // Make new content panes + final Container mainPane = new JPanel(new BorderLayout(5,5)); + final Container contentPane = new JPanel(new BorderLayout(0,0)); + contentPane.setBackground(Color.white); + + // Wrap viewer in scroll pane + JScrollPane scrollPane = new JScrollPane(viewer); + // Make the scrollpane (containing the viewer) the center component + contentPane.add(scrollPane, BorderLayout.CENTER); + + JPanel wrapper = new JPanel(new FlowLayout()); + + // Add button to bottom + JPanel bottomPanel = new JPanel(new BorderLayout(0,0)); + contentPane.add(bottomPanel, BorderLayout.SOUTH); + + JButton ok = new JButton("OK"); + ok.addActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + dialog.dispatchEvent(new WindowEvent(dialog, WindowEvent.WINDOW_CLOSING)); + } + } + ); + wrapper.add(ok); + + // Add an export-to-png button right of the "OK" button + JButton png = new JButton("Export as PNG"); + png.addActionListener( + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + generatePNGFile(viewer, dialog); + } + } + ); + wrapper.add(png); + + bottomPanel.add(wrapper, BorderLayout.SOUTH); + + // Add scale slider + double lastKnownViewerScale = prefs.getDouble(DIALOG_VIEWER_SCALE_PREFS_KEY, viewer.getScale()); + viewer.setScale(lastKnownViewerScale); + + int sliderValue = (int) ((lastKnownViewerScale - 1.0) * 1000); + final JSlider scaleSlider = new JSlider(JSlider.HORIZONTAL, -999, 1000, sliderValue); + + scaleSlider.addChangeListener( + new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + int v = scaleSlider.getValue(); + viewer.setScale(v / 1000.0 + 1.0); + } + } + ); + bottomPanel.add(scaleSlider, BorderLayout.CENTER); + + // Add a JTree representing the parser tree of the input. + JPanel treePanel = new JPanel(new BorderLayout(5, 5)); + + // An "empty" icon that will be used for the JTree's nodes. + Icon empty = new EmptyIcon(); + + UIManager.put("Tree.closedIcon", empty); + UIManager.put("Tree.openIcon", empty); + UIManager.put("Tree.leafIcon", empty); + + Tree parseTreeRoot = viewer.getTree().getRoot(); + TreeNodeWrapper nodeRoot = new TreeNodeWrapper(parseTreeRoot, viewer); + fillTree(nodeRoot, parseTreeRoot, viewer); + final JTree tree = new JTree(nodeRoot); + tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); + + tree.addTreeSelectionListener(new TreeSelectionListener() { + @Override + public void valueChanged(TreeSelectionEvent e) { + + JTree selectedTree = (JTree) e.getSource(); + TreePath path = selectedTree.getSelectionPath(); + TreeNodeWrapper treeNode = (TreeNodeWrapper) path.getLastPathComponent(); + + // Set the clicked AST. + viewer.setTree((Tree) treeNode.getUserObject()); + } + }); + + treePanel.add(new JScrollPane(tree)); + + // Create the pane for both the JTree and the AST + final JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, + treePanel, contentPane); + + mainPane.add(splitPane, BorderLayout.CENTER); + + dialog.setContentPane(mainPane); + + // make viz + WindowListener exitListener = new WindowAdapter() { + public void windowClosing(WindowEvent e) { + prefs.putInt(DIALOG_WIDTH_PREFS_KEY, (int) dialog.getSize().getWidth()); + prefs.putInt(DIALOG_HEIGHT_PREFS_KEY, (int) dialog.getSize().getHeight()); + prefs.putDouble(DIALOG_X_PREFS_KEY, dialog.getLocationOnScreen().getX()); + prefs.putDouble(DIALOG_Y_PREFS_KEY, dialog.getLocationOnScreen().getY()); + prefs.putInt(DIALOG_DIVIDER_LOC_PREFS_KEY, splitPane.getDividerLocation()); + prefs.putDouble(DIALOG_VIEWER_SCALE_PREFS_KEY, viewer.getScale()); + + dialog.setVisible(false); + dialog.dispose(); + } + }; + dialog.addWindowListener(exitListener); + dialog.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + + int width = prefs.getInt(DIALOG_WIDTH_PREFS_KEY, 600); + int height = prefs.getInt(DIALOG_HEIGHT_PREFS_KEY, 500); + dialog.setPreferredSize(new Dimension(width, height)); + dialog.pack(); + + // After pack(): set the divider at 1/3 (200/600) of the frame. + int dividerLocation = prefs.getInt(DIALOG_DIVIDER_LOC_PREFS_KEY, 200); + splitPane.setDividerLocation(dividerLocation); + + if (prefs.getDouble(DIALOG_X_PREFS_KEY, -1) != -1) { + dialog.setLocation( + (int)prefs.getDouble(DIALOG_X_PREFS_KEY, 100), + (int)prefs.getDouble(DIALOG_Y_PREFS_KEY, 100) + ); + } + else { + dialog.setLocationRelativeTo(null); + } + + dialog.setVisible(true); + return dialog; + } + + private static void generatePNGFile(TreeViewer viewer, JDialog dialog) { + BufferedImage bi = new BufferedImage(viewer.getSize().width, + viewer.getSize().height, + BufferedImage.TYPE_INT_ARGB); + Graphics g = bi.createGraphics(); + viewer.paint(g); + g.dispose(); + + try { + File suggestedFile = generateNonExistingPngFile(); + JFileChooser fileChooser = new JFileChooserConfirmOverwrite(); + fileChooser.setCurrentDirectory(suggestedFile.getParentFile()); + fileChooser.setSelectedFile(suggestedFile); + FileFilter pngFilter = new FileFilter() { + + @Override + public boolean accept(File pathname) { + if (pathname.isFile()) { + return pathname.getName().toLowerCase().endsWith(".png"); + } + + return true; + } + + @Override + public String getDescription() { + return "PNG Files (*.png)"; + } + }; + + fileChooser.addChoosableFileFilter(pngFilter); + fileChooser.setFileFilter(pngFilter); + + int returnValue = fileChooser.showSaveDialog(dialog); + if (returnValue == JFileChooser.APPROVE_OPTION) { + File pngFile = fileChooser.getSelectedFile(); + ImageIO.write(bi, "png", pngFile); + + try { + // Try to open the parent folder using the OS' native file manager. + Desktop.getDesktop().open(pngFile.getParentFile()); + } + catch (Exception ex) { + // We could not launch the file manager: just show a popup that we + // succeeded in saving the PNG file. + JOptionPane.showMessageDialog(dialog, "Saved PNG to: " + + pngFile.getAbsolutePath()); + ex.printStackTrace(); + } + } + } + catch (Exception ex) { + JOptionPane.showMessageDialog(dialog, + "Could not export to PNG: " + ex.getMessage(), + "Error", + JOptionPane.ERROR_MESSAGE); + ex.printStackTrace(); + } + } + + private static File generateNonExistingPngFile() { + + final String parent = "."; + final String name = "antlr4_parse_tree"; + final String extension = ".png"; + + File pngFile = new File(parent, name + extension); + + int counter = 1; + + // Keep looping until we create a File that does not yet exist. + while (pngFile.exists()) { + pngFile = new File(parent, name + "_"+ counter + extension); + counter++; + } + + return pngFile; + } + + private static void fillTree(TreeNodeWrapper node, Tree tree, TreeViewer viewer) { + + if (tree == null) { + return; + } + + for (int i = 0; i < tree.getChildCount(); i++) { + + Tree childTree = tree.getChild(i); + TreeNodeWrapper childNode = new TreeNodeWrapper(childTree, viewer); + + node.add(childNode); + + fillTree(childNode, childTree, viewer); + } + } + + private Dimension getScaledTreeSize() { + Dimension scaledTreeSize = + treeLayout.getBounds().getBounds().getSize(); + scaledTreeSize = new Dimension((int)(scaledTreeSize.width*scale), + (int)(scaledTreeSize.height*scale)); + return scaledTreeSize; + } + + + public Future<JDialog> open() { + final TreeViewer viewer = this; + viewer.setScale(1.5); + Callable<JDialog> callable = new Callable<JDialog>() { + JDialog result; + + @Override + public JDialog call() throws Exception { + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + result = showInDialog(viewer); + } + }); + + return result; + } + }; + + ExecutorService executor = Executors.newSingleThreadExecutor(); + + try { + return executor.submit(callable); + } + finally { + executor.shutdown(); + } + } + + public void save(String fileName) throws IOException, PrintException { + JDialog dialog = new JDialog(); + Container contentPane = dialog.getContentPane(); + ((JComponent) contentPane).setBorder(BorderFactory.createEmptyBorder( + 10, 10, 10, 10)); + contentPane.add(this); + contentPane.setBackground(Color.white); + dialog.pack(); + dialog.setLocationRelativeTo(null); + dialog.dispose(); + GraphicsSupport.saveImage(this, fileName); + } + + // --------------------------------------------------- + + protected Rectangle2D.Double getBoundsOfNode(Tree node) { + return treeLayout.getNodeBounds().get(node); + } + + protected String getText(Tree tree) { + String s = treeTextProvider.getText(tree); + s = Utils.escapeWhitespace(s, true); + return s; + } + + public TreeTextProvider getTreeTextProvider() { + return treeTextProvider; + } + + public void setTreeTextProvider(TreeTextProvider treeTextProvider) { + this.treeTextProvider = treeTextProvider; + } + + public void setFontSize(int sz) { + fontSize = sz; + font = new Font(fontName, fontStyle, fontSize); + } + + public void setFontName(String name) { + fontName = name; + font = new Font(fontName, fontStyle, fontSize); + } + + /** Slow for big lists of highlighted nodes */ + public void addHighlightedNodes(Collection<Tree> nodes) { + highlightedNodes = new ArrayList<Tree>(); + highlightedNodes.addAll(nodes); + } + + public void removeHighlightedNodes(Collection<Tree> nodes) { + if ( highlightedNodes!=null ) { + // only remove exact objects defined by ==, not equals() + for (Tree t : nodes) { + int i = getHighlightedNodeIndex(t); + if ( i>=0 ) highlightedNodes.remove(i); + } + } + } + + protected boolean isHighlighted(Tree node) { + return getHighlightedNodeIndex(node) >= 0; + } + + protected int getHighlightedNodeIndex(Tree node) { + if ( highlightedNodes==null ) return -1; + for (int i = 0; i < highlightedNodes.size(); i++) { + Tree t = highlightedNodes.get(i); + if ( t == node ) return i; + } + return -1; + } + + @Override + public Font getFont() { + return font; + } + + @Override + public void setFont(Font font) { + this.font = font; + } + + public int getArcSize() { + return arcSize; + } + + public void setArcSize(int arcSize) { + this.arcSize = arcSize; + } + + public Color getBoxColor() { + return boxColor; + } + + public void setBoxColor(Color boxColor) { + this.boxColor = boxColor; + } + + public Color getHighlightedBoxColor() { + return highlightedBoxColor; + } + + public void setHighlightedBoxColor(Color highlightedBoxColor) { + this.highlightedBoxColor = highlightedBoxColor; + } + + public Color getBorderColor() { + return borderColor; + } + + public void setBorderColor(Color borderColor) { + this.borderColor = borderColor; + } + + public Color getTextColor() { + return textColor; + } + + public void setTextColor(Color textColor) { + this.textColor = textColor; + } + + protected TreeForTreeLayout<Tree> getTree() { + return treeLayout.getTree(); + } + + public void setTree(Tree root) { + if ( root!=null ) { + boolean useIdentity = true; // compare node identity + this.treeLayout = + new TreeLayout<Tree>(getTreeLayoutAdaptor(root), + new TreeViewer.VariableExtentProvide(this), + new DefaultConfiguration<Tree>(gapBetweenLevels, + gapBetweenNodes), + useIdentity); + // Let the UI display this new AST. + updatePreferredSize(); + } + else { + this.treeLayout = null; + repaint(); + } + } + + /** Get an adaptor for root that indicates how to walk ANTLR trees. + * Override to change the adapter from the default of {@link TreeLayoutAdaptor} */ + public TreeForTreeLayout<Tree> getTreeLayoutAdaptor(Tree root) { + return new TreeLayoutAdaptor(root); + } + + public double getScale() { + return scale; + } + + public void setScale(double scale) { + if(scale <= 0) { + scale = 1; + } + this.scale = scale; + updatePreferredSize(); + } + + public void setRuleNames(List<String> ruleNames) { + setTreeTextProvider(new DefaultTreeTextProvider(ruleNames)); + } + + private static class TreeNodeWrapper extends DefaultMutableTreeNode { + + final TreeViewer viewer; + + TreeNodeWrapper(Tree tree, TreeViewer viewer) { + super(tree); + this.viewer = viewer; + } + + @Override + public String toString() { + return viewer.getText((Tree) this.getUserObject()); + } + } + + private static class EmptyIcon implements Icon { + + @Override + public int getIconWidth() { + return 0; + } + + @Override + public int getIconHeight() { + return 0; + } + + @Override + public void paintIcon(Component c, Graphics g, int x, int y) { + /* Do nothing. */ + } + } +} diff --git a/tool/src/org/antlr/v4/gui/Trees.java b/tool/src/org/antlr/v4/gui/Trees.java new file mode 100644 index 0000000..53796ab --- /dev/null +++ b/tool/src/org/antlr/v4/gui/Trees.java @@ -0,0 +1,120 @@ +package org.antlr.v4.gui; + +import org.antlr.v4.runtime.Parser; +import org.antlr.v4.runtime.misc.Utils; +import org.antlr.v4.runtime.tree.Tree; + +import javax.print.PrintException; +import javax.swing.*; +import java.io.BufferedWriter; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.Future; + +public class Trees { + /** Call this method to view a parse tree in a dialog box visually. */ + public static Future<JDialog> inspect(Tree t, List<String> ruleNames) { + TreeViewer viewer = new TreeViewer(ruleNames, t); + return viewer.open(); + } + + /** Call this method to view a parse tree in a dialog box visually. */ + public static Future<JDialog> inspect(Tree t, Parser parser) { + List<String> ruleNames = parser != null ? Arrays.asList(parser.getRuleNames()) : null; + return inspect(t, ruleNames); + } + + /** Save this tree in a postscript file */ + public static void save(Tree t, Parser parser, String fileName) + throws IOException, PrintException + { + List<String> ruleNames = parser != null ? Arrays.asList(parser.getRuleNames()) : null; + save(t, ruleNames, fileName); + } + + /** Save this tree in a postscript file using a particular font name and size */ + public static void save(Tree t, Parser parser, String fileName, + String fontName, int fontSize) + throws IOException + { + List<String> ruleNames = parser != null ? Arrays.asList(parser.getRuleNames()) : null; + save(t, ruleNames, fileName, fontName, fontSize); + } + + /** Save this tree in a postscript file */ + public static void save(Tree t, List<String> ruleNames, String fileName) + throws IOException, PrintException + { + writePS(t, ruleNames, fileName); + } + + /** Save this tree in a postscript file using a particular font name and size */ + public static void save(Tree t, + List<String> ruleNames, String fileName, + String fontName, int fontSize) + throws IOException + { + writePS(t, ruleNames, fileName, fontName, fontSize); + } + + public static String getPS(Tree t, List<String> ruleNames, + String fontName, int fontSize) + { + TreePostScriptGenerator psgen = + new TreePostScriptGenerator(ruleNames, t, fontName, fontSize); + return psgen.getPS(); + } + + public static String getPS(Tree t, List<String> ruleNames) { + return getPS(t, ruleNames, "Helvetica", 11); + } + + public static void writePS(Tree t, List<String> ruleNames, + String fileName, + String fontName, int fontSize) + throws IOException + { + String ps = getPS(t, ruleNames, fontName, fontSize); + FileWriter f = new FileWriter(fileName); + BufferedWriter bw = new BufferedWriter(f); + try { + bw.write(ps); + } + finally { + bw.close(); + } + } + + public static void writePS(Tree t, List<String> ruleNames, String fileName) + throws IOException + { + writePS(t, ruleNames, fileName, "Helvetica", 11); + } + + /** Print out a whole tree in LISP form. Arg nodeTextProvider is used on the + * node payloads to get the text for the nodes. + * + * @since 4.5.1 + */ + public static String toStringTree(Tree t, TreeTextProvider nodeTextProvider) { + if ( t==null ) return "null"; + String s = Utils.escapeWhitespace(nodeTextProvider.getText(t), false); + if ( t.getChildCount()==0 ) return s; + StringBuilder buf = new StringBuilder(); + buf.append("("); + s = Utils.escapeWhitespace(nodeTextProvider.getText(t), false); + buf.append(s); + buf.append(' '); + for (int i = 0; i<t.getChildCount(); i++) { + if ( i>0 ) buf.append(' '); + buf.append(toStringTree(t.getChild(i), nodeTextProvider)); + } + buf.append(")"); + return buf.toString(); + } + + private Trees() { + } +} diff --git a/tool/src/org/antlr/v4/misc/CharSupport.java b/tool/src/org/antlr/v4/misc/CharSupport.java new file mode 100644 index 0000000..22e9648 --- /dev/null +++ b/tool/src/org/antlr/v4/misc/CharSupport.java @@ -0,0 +1,153 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.misc; + +import org.antlr.v4.runtime.Lexer; + +/** */ +public class CharSupport { + /** When converting ANTLR char and string literals, here is the + * value set of escape chars. + */ + public static int ANTLRLiteralEscapedCharValue[] = new int[255]; + + /** Given a char, we need to be able to show as an ANTLR literal. + */ + public static String ANTLRLiteralCharValueEscape[] = new String[255]; + + static { + ANTLRLiteralEscapedCharValue['n'] = '\n'; + ANTLRLiteralEscapedCharValue['r'] = '\r'; + ANTLRLiteralEscapedCharValue['t'] = '\t'; + ANTLRLiteralEscapedCharValue['b'] = '\b'; + ANTLRLiteralEscapedCharValue['f'] = '\f'; + ANTLRLiteralEscapedCharValue['\\'] = '\\'; + ANTLRLiteralEscapedCharValue['\''] = '\''; + ANTLRLiteralEscapedCharValue['"'] = '"'; + ANTLRLiteralCharValueEscape['\n'] = "\\n"; + ANTLRLiteralCharValueEscape['\r'] = "\\r"; + ANTLRLiteralCharValueEscape['\t'] = "\\t"; + ANTLRLiteralCharValueEscape['\b'] = "\\b"; + ANTLRLiteralCharValueEscape['\f'] = "\\f"; + ANTLRLiteralCharValueEscape['\\'] = "\\\\"; + ANTLRLiteralCharValueEscape['\''] = "\\'"; + } + + /** Return a string representing the escaped char for code c. E.g., If c + * has value 0x100, you will get "\u0100". ASCII gets the usual + * char (non-hex) representation. Control characters are spit out + * as unicode. While this is specially set up for returning Java strings, + * it can be used by any language target that has the same syntax. :) + */ + public static String getANTLRCharLiteralForChar(int c) { + if ( c< Lexer.MIN_CHAR_VALUE ) { + return "'<INVALID>'"; + } + if ( c<ANTLRLiteralCharValueEscape.length && ANTLRLiteralCharValueEscape[c]!=null ) { + return '\''+ANTLRLiteralCharValueEscape[c]+'\''; + } + if ( Character.UnicodeBlock.of((char)c)==Character.UnicodeBlock.BASIC_LATIN && + !Character.isISOControl((char)c) ) { + if ( c=='\\' ) { + return "'\\\\'"; + } + if ( c=='\'') { + return "'\\''"; + } + return '\''+Character.toString((char)c)+'\''; + } + // turn on the bit above max "\uFFFF" value so that we pad with zeros + // then only take last 4 digits + String hex = Integer.toHexString(c|0x10000).toUpperCase().substring(1,5); + String unicodeStr = "'\\u"+hex+"'"; + return unicodeStr; + } + + /** Given a literal like (the 3 char sequence with single quotes) 'a', + * return the int value of 'a'. Convert escape sequences here also. + * Return -1 if not single char. + */ + public static int getCharValueFromGrammarCharLiteral(String literal) { + if ( literal==null || literal.length()<3 ) return -1; + return getCharValueFromCharInGrammarLiteral(literal.substring(1,literal.length()-1)); + } + + /** Given char x or \t or \u1234 return the char value; + * Unnecessary escapes like '\{' yield -1. + */ + public static int getCharValueFromCharInGrammarLiteral(String cstr) { + switch ( cstr.length() ) { + case 1 : + // 'x' + return cstr.charAt(0); // no escape char + case 2 : + if ( cstr.charAt(0)!='\\' ) return -1; + // '\x' (antlr lexer will catch invalid char) + if ( Character.isDigit(cstr.charAt(1)) ) return -1; + int escChar = cstr.charAt(1); + int charVal = ANTLRLiteralEscapedCharValue[escChar]; + if ( charVal==0 ) return -1; + return charVal; + case 6 : + // '\u1234' + if ( !cstr.startsWith("\\u") ) return -1; + String unicodeChars = cstr.substring(2, cstr.length()); + return Integer.parseInt(unicodeChars, 16); + default : + return -1; + } + } + + public static String getStringFromGrammarStringLiteral(String literal) { + StringBuilder buf = new StringBuilder(); + int i = 1; // skip first quote + int n = literal.length()-1; // skip last quote + while ( i < n ) { // scan all but last quote + int end = i+1; + if ( literal.charAt(i) == '\\' ) { + end = i+2; + if ( (i+1)>=n ) break; // ignore spurious \ on end + if ( literal.charAt(i+1) == 'u' ) end = i+6; + } + if ( end>n ) break; + String esc = literal.substring(i, end); + int c = getCharValueFromCharInGrammarLiteral(esc); + if ( c==-1 ) { buf.append(esc); } + else buf.append((char)c); + i = end; + } + return buf.toString(); + } + + public static String capitalize(String s) { + return Character.toUpperCase(s.charAt(0)) + s.substring(1); + } +} diff --git a/tool/src/org/antlr/v4/misc/FrequencySet.java b/tool/src/org/antlr/v4/misc/FrequencySet.java new file mode 100644 index 0000000..94e467c --- /dev/null +++ b/tool/src/org/antlr/v4/misc/FrequencySet.java @@ -0,0 +1,52 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.misc; + +import java.util.HashMap; + +/** Count how many of each key we have; not thread safe */ +public class FrequencySet<T> extends HashMap<T, MutableInt> { + public int count(T key) { + MutableInt value = get(key); + if (value == null) return 0; + return value.v; + } + public void add(T key) { + MutableInt value = get(key); + if (value == null) { + value = new MutableInt(1); + put(key, value); + } + else { + value.v++; + } + } +} diff --git a/tool/src/org/antlr/v4/misc/Graph.java b/tool/src/org/antlr/v4/misc/Graph.java new file mode 100644 index 0000000..2509c81 --- /dev/null +++ b/tool/src/org/antlr/v4/misc/Graph.java @@ -0,0 +1,117 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.misc; + +import org.antlr.v4.runtime.misc.OrderedHashSet; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** A generic graph with edges; Each node as a single Object payload. + * This is only used to topologically sort a list of file dependencies + * at the moment. + */ +public class Graph<T> { + + public static class Node<T> { + T payload; + List<Node<T>> edges; // points at which nodes? + + public Node(T payload) { this.payload = payload; } + + public void addEdge(Node<T> n) { + if ( edges==null ) edges = new ArrayList<Node<T>>(); + if ( !edges.contains(n) ) edges.add(n); + } + + @Override + public String toString() { return payload.toString(); } + } + + /** Map from node payload to node containing it */ + protected Map<T,Node<T>> nodes = new LinkedHashMap<T,Node<T>>(); + + public void addEdge(T a, T b) { + //System.out.println("add edge "+a+" to "+b); + Node<T> a_node = getNode(a); + Node<T> b_node = getNode(b); + a_node.addEdge(b_node); + } + + protected Node<T> getNode(T a) { + Node<T> existing = nodes.get(a); + if ( existing!=null ) return existing; + Node<T> n = new Node<T>(a); + nodes.put(a, n); + return n; + } + + /** DFS-based topological sort. A valid sort is the reverse of + * the post-order DFA traversal. Amazingly simple but true. + * For sorting, I'm not following convention here since ANTLR + * needs the opposite. Here's what I assume for sorting: + * + * If there exists an edge u -> v then u depends on v and v + * must happen before u. + * + * So if this gives nonreversed postorder traversal, I get the order + * I want. + */ + public List<T> sort() { + Set<Node<T>> visited = new OrderedHashSet<Node<T>>(); + ArrayList<T> sorted = new ArrayList<T>(); + while ( visited.size() < nodes.size() ) { + // pick any unvisited node, n + Node<T> n = null; + for (Node<T> tNode : nodes.values()) { + n = tNode; + if ( !visited.contains(n) ) break; + } + if (n!=null) { // if at least one unvisited + DFS(n, visited, sorted); + } + } + return sorted; + } + + public void DFS(Node<T> n, Set<Node<T>> visited, ArrayList<T> sorted) { + if ( visited.contains(n) ) return; + visited.add(n); + if ( n.edges!=null ) { + for (Node<T> target : n.edges) { + DFS(target, visited, sorted); + } + } + sorted.add(n.payload); + } +}
\ No newline at end of file diff --git a/tool/src/org/antlr/v4/misc/MutableInt.java b/tool/src/org/antlr/v4/misc/MutableInt.java new file mode 100644 index 0000000..816a4ba --- /dev/null +++ b/tool/src/org/antlr/v4/misc/MutableInt.java @@ -0,0 +1,56 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.misc; + +public class MutableInt extends Number implements Comparable<Number> { + public int v; + + public MutableInt(int v) { this.v = v; } + + @Override + public boolean equals(Object o) { + if ( o instanceof Number ) return v == ((Number)o).intValue(); + return false; + } + + @Override public int hashCode() { return v; } + + @Override public int compareTo(Number o) { return v-o.intValue(); } + @Override public int intValue() { return v; } + @Override public long longValue() { return v; } + @Override public float floatValue() { return v; } + @Override public double doubleValue() { return v; } + + @Override + public String toString() { + return String.valueOf(v); + } +} diff --git a/tool/src/org/antlr/v4/misc/OrderedHashMap.java b/tool/src/org/antlr/v4/misc/OrderedHashMap.java new file mode 100644 index 0000000..ce37b86 --- /dev/null +++ b/tool/src/org/antlr/v4/misc/OrderedHashMap.java @@ -0,0 +1,73 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.misc; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** I need the get-element-i functionality so I'm subclassing + * LinkedHashMap. + */ +public class OrderedHashMap<K,V> extends LinkedHashMap<K,V> { + /** Track the elements as they are added to the set */ + protected List<K> elements = new ArrayList<K>(); + + public K getKey(int i) { return elements.get(i); } + + public V getElement(int i) { return get(elements.get(i)); } + + @Override + public V put(K key, V value) { + elements.add(key); + return super.put(key, value); + } + + @Override + public void putAll(Map<? extends K, ? extends V> m) { + for (Map.Entry<? extends K, ? extends V> entry : m.entrySet()) { + put(entry.getKey(), entry.getValue()); + } + } + + @Override + public V remove(Object key) { + elements.remove(key); + return super.remove(key); + } + + @Override + public void clear() { + elements.clear(); + super.clear(); + } +} diff --git a/tool/src/org/antlr/v4/misc/Utils.java b/tool/src/org/antlr/v4/misc/Utils.java new file mode 100644 index 0000000..c62003c --- /dev/null +++ b/tool/src/org/antlr/v4/misc/Utils.java @@ -0,0 +1,171 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.misc; + +import org.antlr.v4.tool.ast.GrammarAST; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** */ +public class Utils { + public static final int INTEGER_POOL_MAX_VALUE = 1000; + + public interface Filter<T> { + boolean select(T t); + } + + public interface Func0<TResult> { + TResult exec(); + } + + public interface Func1<T1, TResult> { + TResult exec(T1 arg1); + } + + static Integer[] ints = new Integer[INTEGER_POOL_MAX_VALUE+1]; + + /** Integer objects are immutable so share all Integers with the + * same value up to some max size. Use an array as a perfect hash. + * Return shared object for 0..INTEGER_POOL_MAX_VALUE or a new + * Integer object with x in it. Java's autoboxing only caches up to 127. + public static Integer integer(int x) { + if ( x<0 || x>INTEGER_POOL_MAX_VALUE ) { + return new Integer(x); + } + if ( ints[x]==null ) { + ints[x] = new Integer(x); + } + return ints[x]; + } + */ + + public static String stripFileExtension(String name) { + if ( name==null ) return null; + int lastDot = name.lastIndexOf('.'); + if ( lastDot<0 ) return name; + return name.substring(0, lastDot); + } + + public static String join(Object[] a, String separator) { + StringBuilder buf = new StringBuilder(); + for (int i=0; i<a.length; i++) { + Object o = a[i]; + buf.append(o.toString()); + if ( (i+1)<a.length ) { + buf.append(separator); + } + } + return buf.toString(); + } + + public static String sortLinesInString(String s) { + String lines[] = s.split("\n"); + Arrays.sort(lines); + List<String> linesL = Arrays.asList(lines); + StringBuilder buf = new StringBuilder(); + for (String l : linesL) { + buf.append(l); + buf.append('\n'); + } + return buf.toString(); + } + + public static <T extends GrammarAST> List<String> nodesToStrings(List<T> nodes) { + if ( nodes == null ) return null; + List<String> a = new ArrayList<String>(); + for (T t : nodes) a.add(t.getText()); + return a; + } + +// public static <T> List<T> list(T... values) { +// List<T> x = new ArrayList<T>(values.length); +// for (T v : values) { +// if ( v!=null ) x.add(v); +// } +// return x; +// } + + public static String capitalize(String s) { + return Character.toUpperCase(s.charAt(0)) + s.substring(1); + } + + public static String decapitalize(String s) { + return Character.toLowerCase(s.charAt(0)) + s.substring(1); + } + + /** apply methodName to list and return list of results. method has + * no args. This pulls data out of a list essentially. + */ + public static <From,To> List<To> select(List<From> list, Func1<From, To> selector) { + if ( list==null ) return null; + List<To> b = new ArrayList<To>(); + for (From f : list) { + b.add(selector.exec(f)); + } + return b; + } + + /** Find exact object type or sublass of cl in list */ + public static <T> T find(List<?> ops, Class<T> cl) { + for (Object o : ops) { + if ( cl.isInstance(o) ) return cl.cast(o); +// if ( o.getClass() == cl ) return o; + } + return null; + } + + public static <T> int indexOf(List<? extends T> elems, Filter<T> filter) { + for (int i=0; i<elems.size(); i++) { + if ( filter.select(elems.get(i)) ) return i; + } + return -1; + } + + public static <T> int lastIndexOf(List<? extends T> elems, Filter<T> filter) { + for (int i=elems.size()-1; i>=0; i--) { + if ( filter.select(elems.get(i)) ) return i; + } + return -1; + } + + public static void setSize(List<?> list, int size) { + if (size < list.size()) { + list.subList(size, list.size()).clear(); + } else { + while (size > list.size()) { + list.add(null); + } + } + } + +} diff --git a/tool/src/org/antlr/v4/parse/ANTLRLexer.g b/tool/src/org/antlr/v4/parse/ANTLRLexer.g new file mode 100644 index 0000000..4768d2e --- /dev/null +++ b/tool/src/org/antlr/v4/parse/ANTLRLexer.g @@ -0,0 +1,798 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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. + */ + +// File : A3Lexer.g +// Author : Jim Idle (jimi@temporal-wave.com) +// Copyright : Free BSD - See @header clause below +// Version : First implemented as part of ANTLR 3.2 this is the self +// hosting ANTLR 3 Lexer. +// +// Description +// ----------- +// This is the definitive lexer grammar for parsing ANTLR V3.x.x grammars. All other +// gramnmars are derived from this grammar via source code control integration (perforce) +// or by the gdiff tool. +// +// This grammar and its associated grmmmars A3Parser.g and A3Walker.g exhibit the following +// traits, which are recommended for all production quality grammars: +// +// 1) They are separate grammars, not composite grammars; +// 2) They implement all supporting methods in a superclass (at least this is recommended +// for language targets that support inheritence; +// 3) All errors are pushed as far down the parsing chain as possible, which means +// that the lexer tries to defer error reporting to the parser, and the parser +// tries to defer error reporting to a semantic phase consisting of a single +// walk of the AST. The reason for this is that the error messages produced +// from later phases of the parse will generally have better context and so +// be more useful to the end user. Consider the message: "Syntax error at 'options'" +// vs: "You cannot specify two options{} sections in a single grammar file". +// 4) The lexer is 'programmed' to catch common mistakes such as unterminated literals +// and report them specifically and not just issue confusing lexer mismatch errors. +// + +/** Read in an ANTLR grammar and build an AST. Try not to do + * any actions, just build the tree. + * + * The phases are: + * + * A3Lexer.g (this file) + * A3Parser.g + * A3Verify.g (derived from A3Walker.g) + * assign.types.g + * define.g + * buildnfa.g + * antlr.print.g (optional) + * codegen.g + * + * Terence Parr + * University of San Francisco + * 2005 + * Jim Idle (this v3 grammar) + * Temporal Wave LLC + * 2009 + */ +lexer grammar ANTLRLexer; + +// ============================================================================== +// Note that while this grammar does not care about order of constructs +// that don't really matter, such as options before @header etc, it must first +// be parsed by the original v2 parser, before it replaces it. That parser does +// care about order of structures. Hence we are constrained by the v2 parser +// for at least the first bootstrap release that causes this parser to replace +// the v2 version. +// ============================================================================== + +// ------- +// Options +// +// V3 option directives to tell the tool what we are asking of it for this +// grammar. +// +options { + + // Target language is Java, which is the default but being specific + // here as this grammar is also meant as a good example grammar for + // for users. + // + language = Java; + + // The super class that this lexer should expect to inherit from, and + // which contains any and all support routines for the lexer. This is + // commented out in this baseline (definitive or normative grammar) + // - see the ANTLR tool implementation for hints on how to use the super + // class + // + //superclass = AbstractA3Lexer; +} + +tokens { SEMPRED; TOKEN_REF; RULE_REF; LEXER_CHAR_SET; ARG_ACTION; } + +// Include the copyright in this source and also the generated source +// +@lexer::header { +/* + [The "BSD licence"] + Copyright (c) 2005-2009 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.parse; +import org.antlr.v4.tool.*; +} + + +@members { + public static final int COMMENTS_CHANNEL = 2; + + public CommonTokenStream tokens; // track stream we push to; need for context info + public boolean isLexerRule = false; + + public void grammarError(ErrorType etype, org.antlr.runtime.Token token, Object... args) { } + + /** scan backwards from current point in this.tokens list + * looking for the start of the rule or subrule. + * Return token or null if for some reason we can't find the start. + */ + public Token getRuleOrSubruleStartToken() { + if ( tokens==null ) return null; + int i = tokens.index(); + int n = tokens.size(); + if ( i>=n ) i = n-1; // seems index == n as we lex + while ( i>=0 && i<n) { + int ttype = tokens.get(i).getType(); + if ( ttype == LPAREN || ttype == TOKEN_REF || ttype == RULE_REF ) { + return tokens.get(i); + } + i--; + } + return null; + } +} + +// -------- +// Comments +// +// ANTLR comments can be multi or single line and we don't care +// which particularly. However we also accept Javadoc style comments +// of the form: /** ... */ and we do take care to distinguish those +// from ordinary multi-line comments +// Note how we guide the lexical PATH because we want to issue a decriptive +// error message in case of a standalone '/' character, which makes no +// sense in ANTLR source code. We alo trap unterminated multi-line comments +// +fragment DOC_COMMENT : ; +COMMENT +@init { + + // Record the start line and offsets as if we need to report an + // unterminated comment, then we want to show the start of the comment + // we think is broken, not the end, where people will have to try and work + // it out themselves. + // + int startLine = $line; + int offset = getCharPositionInLine(); +} + : // Eat the first character only, then see if we have a comment + // or something silly. + // + '/' // Comment introducer + + ( + // Single line comment, possibly with embedded src/line directives + // in a similar style to the C pre-processor, allowing generated + // code to refer the programmer back to the original source code + // in case of error. + // + '/' + ( + (' $ANTLR')=> ' $ANTLR' SRC + | ~(NLCHARS)* + ) + + | // Multi-line comment, which may be a documentation comment + // if it starts /** (note that we protect against accidentaly + // recognizing a comment /**/ as a documentation comment + // + '*' ( + { input.LA(2) != '/'}?=> '*' { $type = DOC_COMMENT; } + | { true }?=> // Required to cover all alts with predicates + ) + + // Should we support embedded multiline comments here? + // + ( + // Pick out end of multiline comment and exit the loop + // if we find it. + // + { !(input.LA(1) == '*' && input.LA(2) == '/') }? + + // Anything else other than the non-greedy match of + // the comment close sequence + // + . + )* + ( + // Look for the comment terminator, but if it is accidentally + // unterminated, then we will hit EOF, which will trigger the + // epsilon alt and hence we can issue an error message relative + // to the start of the unterminated multi-line comment + // + '*/' + + | // Unterminated comment! + // + { + // ErrorManager.msg(Msg.UNTERMINATED_DOC_COMMENT, startLine, offset, $pos, startLine, offset, $pos, (Object)null); + } + ) + + | // There was nothing that made sense following the opening '/' and so + // we issue an error regarding the malformed comment + // + { + // TODO: Insert error message relative to comment start + // + } + ) + { + // We do not wish to pass the comments in to the parser. If you are + // writing a formatter then you will want to preserve the comments off + // channel, but could just skip and save token space if not. + // + $channel=COMMENTS_CHANNEL; + } + ; + +ARG_OR_CHARSET +options {k=1;} + : {isLexerRule}?=> LEXER_CHAR_SET {$type=LEXER_CHAR_SET;} + | {!isLexerRule}?=> ARG_ACTION + { + $type=ARG_ACTION; + // Set the token text to our gathered string minus outer [ ] + String t = $text; + t = t.substring(1,t.length()-1); + setText(t); + } + ; + +fragment +LEXER_CHAR_SET + : '[' + ( '\\' ~('\r'|'\n') + | ~('\r'|'\n'|'\\'|']') + )* + ']' + ; + +// -------------- +// Argument specs +// +// Certain argument lists, such as those specifying call parameters +// to a rule invocation, or input parameters to a rule specification +// are contained within square brackets. In the lexer we consume them +// all at once and sort them out later in the grammar analysis. +// +fragment +ARG_ACTION + : '[' + ( + ARG_ACTION + + | ('"')=>ACTION_STRING_LITERAL + + | ('\'')=>ACTION_CHAR_LITERAL + + | ~('['|']') + )* + + ']' + ; + +// ------- +// Actions +// +// Other than making sure to distinguish between { and } embedded +// within what we have assumed to be literals in the action code, the +// job of the lexer is merely to gather the code within the action +// (delimited by {}) and pass it to the parser as a single token. +// We know that this token will be asked for its text somewhere +// in the upcoming parse, so setting the text here to exclude +// the delimiting {} is no additional overhead. +// +ACTION + : NESTED_ACTION + ( '?' {$type = SEMPRED;} + ( (WSNLCHARS* '=>') => WSNLCHARS* '=>' // v3 gated sempred + { + Token t = new CommonToken(input, state.type, state.channel, state.tokenStartCharIndex, getCharIndex()-1); + t.setLine(state.tokenStartLine); + t.setText(state.text); + t.setCharPositionInLine(state.tokenStartCharPositionInLine); + grammarError(ErrorType.V3_GATED_SEMPRED, t); + } + )? + )? + ; + +// ---------------- +// Action structure +// +// Many language targets use {} as block delimiters and so we +// must recursively match {} delimited blocks to balance the +// braces. Additionally, we must make some assumptions about +// literal string representation in the target language. We assume +// that they are delimited by ' or " and so consume these +// in their own alts so as not to inadvertantly match {}. +// This rule calls itself on matching a { +// +fragment +NESTED_ACTION +@init { + + // Record the start line and offsets as if we need to report an + // unterminated block, then we want to show the start of the comment + // we think is broken, not the end, where people will have to try and work + // it out themselves. + // + int startLine = getLine(); + int offset = getCharPositionInLine(); +} + + : // Action and other blocks start with opening { + // + '{' + ( + // And now we can match one of a number of embedded + // elements within the action until we find a + // } that balances the opening {. If we do not find + // the balanced } then we will hit EOF and can issue + // an error message about the brace that we belive to + // be mismatched. This won't be foolproof but we will + // be able to at least report an error against the + // opening brace that we feel is in error and this will + // guide the user to the correction as best we can. + // + + + // An embedded {} block + // + NESTED_ACTION + + | // What appears to be a literal + // + ACTION_CHAR_LITERAL + + | // We have assumed that the target language has C/Java + // type comments. + // + COMMENT + + | // What appears to be a literal + // + ACTION_STRING_LITERAL + + | // What appears to be an escape sequence + // + ACTION_ESC + + | // Some other single character that is not + // handled above + // + ~('\\'|'"'|'\''|'/'|'{'|'}') + + )* + + ( + // Correctly balanced closing brace + // + '}' + + | // Looks like have an imblanced {} block, report + // with respect to the opening brace. + // + { + // TODO: Report imbalanced {} + System.out.println("Block starting at line " + startLine + " offset " + (offset+1) + " contains imbalanced {} or is missing a }"); + } + ) + ; + + +// Keywords +// -------- +// keywords used to specify ANTLR v3 grammars. Keywords may not be used as +// labels for rules or in any other context where they would be ambiguous +// with the keyword vs some other identifier +// OPTIONS, TOKENS, and CHANNELS must also consume the opening brace that captures +// their option block, as this is the easiest way to parse it separate +// to an ACTION block, despite it using the same {} delimiters. +// +OPTIONS : 'options' WSNLCHARS* '{' ; +TOKENS_SPEC : 'tokens' WSNLCHARS* '{' ; +CHANNELS : 'channels' WSNLCHARS* '{' ; + +IMPORT : 'import' ; +FRAGMENT : 'fragment' ; +LEXER : 'lexer' ; +PARSER : 'parser' ; +GRAMMAR : 'grammar' ; +TREE_GRAMMAR : 'tree' WSNLCHARS* 'grammar' ; +PROTECTED : 'protected' ; +PUBLIC : 'public' ; +PRIVATE : 'private' ; +RETURNS : 'returns' ; +LOCALS : 'locals' ; +THROWS : 'throws' ; +CATCH : 'catch' ; +FINALLY : 'finally' ; +MODE : 'mode' ; + +// ----------- +// Punctuation +// +// Character sequences used as separators, delimters, operators, etc +// +COLON : ':' + { + // scan backwards, looking for a RULE_REF or TOKEN_REF. + // which would indicate the start of a rule definition. + // If we see a LPAREN, then it's the start of the subrule. + // this.tokens is the token string we are pushing into, so + // just loop backwards looking for a rule definition. Then + // we set isLexerRule. + Token t = getRuleOrSubruleStartToken(); + if ( t!=null ) { + if ( t.getType()==RULE_REF ) isLexerRule = false; + else if ( t.getType()==TOKEN_REF ) isLexerRule = true; + // else must be subrule; don't alter context + } + } + ; +COLONCOLON : '::' ; +COMMA : ',' ; +SEMI : ';' ; +LPAREN : '(' ; +RPAREN : ')' ; +RARROW : '->' ; +LT : '<' ; +GT : '>' ; +ASSIGN : '=' ; +QUESTION : '?' ; +SYNPRED : '=>' + { + Token t = new CommonToken(input, state.type, state.channel, + state.tokenStartCharIndex, getCharIndex()-1); + t.setLine(state.tokenStartLine); + t.setText(state.text); + t.setCharPositionInLine(state.tokenStartCharPositionInLine); + grammarError(ErrorType.V3_SYNPRED, t); + $channel=HIDDEN; + } + ; +STAR : '*' ; +PLUS : '+' ; +PLUS_ASSIGN : '+=' ; +OR : '|' ; +DOLLAR : '$' ; +DOT : '.' ; // can be WILDCARD or DOT in qid or imported rule ref +RANGE : '..' ; +AT : '@' ; +POUND : '#' ; +NOT : '~' ; +RBRACE : '}' ; + +/** Allow unicode rule/token names */ +ID : a=NameStartChar NameChar* + { + if ( Grammar.isTokenName($a.text) ) $type = TOKEN_REF; + else $type = RULE_REF; + } + ; + +fragment +NameChar : NameStartChar + | '0'..'9' + | '_' + | '\u00B7' + | '\u0300'..'\u036F' + | '\u203F'..'\u2040' + ; + +fragment +NameStartChar + : 'A'..'Z' | 'a'..'z' + | '\u00C0'..'\u00D6' + | '\u00D8'..'\u00F6' + | '\u00F8'..'\u02FF' + | '\u0370'..'\u037D' + | '\u037F'..'\u1FFF' + | '\u200C'..'\u200D' + | '\u2070'..'\u218F' + | '\u2C00'..'\u2FEF' + | '\u3001'..'\uD7FF' + | '\uF900'..'\uFDCF' + | '\uFDF0'..'\uFEFE' + | '\uFF00'..'\uFFFD' + ; // ignores | ['\u10000-'\uEFFFF] ; + +// ---------------------------- +// Literals embedded in actions +// +// Note that we have made the assumption that the language used within +// actions uses the fairly standard " and ' delimiters for literals and +// that within these literals, characters are escaped using the \ character. +// There are some languages which do not conform to this in all cases, such +// as by using /string/ and so on. We will have to deal with such cases if +// if they come up in targets. +// + +// Within actions, or other structures that are not part of the ANTLR +// syntax, we may encounter literal characters. Within these, we do +// not want to inadvertantly match things like '}' and so we eat them +// specifically. While this rule is called CHAR it allows for the fact that +// some languages may use/allow ' as the string delimiter. +// +fragment +ACTION_CHAR_LITERAL + : '\'' (('\\')=>ACTION_ESC | ~'\'' )* '\'' + ; + +// Within actions, or other structures that are not part of the ANTLR +// syntax, we may encounter literal strings. Within these, we do +// not want to inadvertantly match things like '}' and so we eat them +// specifically. +// +fragment +ACTION_STRING_LITERAL + : '"' (('\\')=>ACTION_ESC | ~'"')* '"' + ; + +// Within literal strings and characters that are not part of the ANTLR +// syntax, we must allow for escaped character sequences so that we do not +// inadvertantly recognize the end of a string or character when the terminating +// delimiter has been esacped. +// +fragment +ACTION_ESC + : '\\' . + ; + +// ------- +// Integer +// +// Obviously (I hope) match an aribtrary long sequence of digits. +// +INT : ('0'..'9')+ + ; + +// ----------- +// Source spec +// +// A fragment rule for picking up information about an origrinating +// file from which the grammar we are parsing has been generated. This allows +// ANTLR to report errors against the originating file and not the generated +// file. +// +fragment +SRC : 'src' WSCHARS+ file=ACTION_STRING_LITERAL WSCHARS+ line=INT + { + // TODO: Add target specific code to change the source file name and current line number + // + } + ; + +// -------------- +// Literal string +// +// ANTLR makes no disticintion between a single character literal and a +// multi-character string. All literals are single quote delimited and +// may contain unicode escape sequences of the form \uxxxx, where x +// is a valid hexadecimal number (as per Java basically). +STRING_LITERAL + : '\'' ( ( ESC_SEQ | ~('\\'|'\''|'\r'|'\n') ) )* + ( '\'' + | // Unterminated string literal + { + Token t = new CommonToken(input, state.type, state.channel, state.tokenStartCharIndex, getCharIndex()-1); + t.setLine(state.tokenStartLine); + t.setText(state.text); + t.setCharPositionInLine(state.tokenStartCharPositionInLine); + grammarError(ErrorType.UNTERMINATED_STRING_LITERAL, t); + } + ) + ; + +// A valid hex digit specification +// +fragment +HEX_DIGIT : ('0'..'9'|'a'..'f'|'A'..'F') ; + +// Any kind of escaped character that we can embed within ANTLR +// literal strings. +// +fragment +ESC_SEQ + : '\\' + ( + // The standard escaped character set such as tab, newline, + // etc. + // + 'b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\' + + | // A Java style Unicode escape sequence + // + UNICODE_ESC + + | // An illegal escape seqeunce + // + { + Token t = new CommonToken(input, state.type, state.channel, getCharIndex()-1, getCharIndex()); + t.setText(t.getText()); + t.setLine(input.getLine()); + t.setCharPositionInLine(input.getCharPositionInLine()-1); + grammarError(ErrorType.INVALID_ESCAPE_SEQUENCE, t); + } + ) + ; + +fragment +UNICODE_ESC + +@init { + + // Flag to tell us whether we have a valid number of + // hex digits in the escape sequence + // + int hCount = 0; +} + : 'u' // Leadin for unicode escape sequence + + // We now require 4 hex digits. Note though + // that we accept any number of characters + // and issue an error if we do not get 4. We cannot + // use an inifinite count such as + because this + // might consume too many, so we lay out the lexical + // options and issue an error at the invalid paths. + // + ( + ( + HEX_DIGIT { hCount++; } + ( + HEX_DIGIT { hCount++; } + ( + HEX_DIGIT { hCount++; } + ( + // Four valid hex digits, we are good + // + HEX_DIGIT { hCount++; } + + | // Three valid digits + ) + + | // Two valid digits + ) + + | // One valid digit + ) + ) + | // No valid hex digits at all + ) + + // Now check the digit count and issue an error if we need to + // + { + if (hCount != 4) { + Token t = new CommonToken(input, state.type, state.channel, getCharIndex()-3-hCount, getCharIndex()-1); + t.setText(t.getText()); + t.setLine(input.getLine()); + t.setCharPositionInLine(input.getCharPositionInLine()-hCount-2); + grammarError(ErrorType.INVALID_ESCAPE_SEQUENCE, t); + } + } + ; + +// ---------- +// Whitespace +// +// Characters and character constructs that are of no import +// to the parser and are used to make the grammar easier to read +// for humans. +// +WS + : ( + ' ' + | '\t' + | '\r' + | '\n' + | '\f' + )+ + {$channel=HIDDEN;} + ; + +// A fragment rule for use in recognizing end of line in +// rules like COMMENT. +// +fragment +NLCHARS + : '\n' | '\r' + ; + +// A fragment rule for recognizing traditional whitespace +// characters within lexer rules. +// +fragment +WSCHARS + : ' ' | '\t' | '\f' + ; + +// A fragment rule for recognizing both traditional whitespace and +// end of line markers, when we don't care to distinguish but don't +// want any action code going on. +// +fragment +WSNLCHARS + : ' ' | '\t' | '\f' | '\n' | '\r' + ; + +// This rule allows ANTLR 4 to parse grammars using the UTF-8 encoding with a +// byte order mark. Since this Unicode character doesn't appear as a token +// anywhere else in the grammar, we can simply skip all instances of it without +// problem. This rule will not break usage of \uFEFF inside a LEXER_CHAR_SET or +// STRING_LITERAL. +UnicodeBOM + : '\uFEFF' {skip();} + ; + +// ----------------- +// Illegal Character +// +// This is an illegal character trap which is always the last rule in the +// lexer specification. It matches a single character of any value and being +// the last rule in the file will match when no other rule knows what to do +// about the character. It is reported as an error but is not passed on to the +// parser. This means that the parser to deal with the gramamr file anyway +// but we will not try to analyse or code generate from a file with lexical +// errors. +// +ERRCHAR + : . + { + Token t = new CommonToken(input, state.type, state.channel, state.tokenStartCharIndex, getCharIndex()-1); + t.setLine(state.tokenStartLine); + t.setText(state.text); + t.setCharPositionInLine(state.tokenStartCharPositionInLine); + String msg = getTokenErrorDisplay(t) + " came as a complete surprise to me"; + grammarError(ErrorType.SYNTAX_ERROR, t, msg); + state.syntaxErrors++; + skip(); + } + ; diff --git a/tool/src/org/antlr/v4/parse/ANTLRParser.g b/tool/src/org/antlr/v4/parse/ANTLRParser.g new file mode 100644 index 0000000..42946fa --- /dev/null +++ b/tool/src/org/antlr/v4/parse/ANTLRParser.g @@ -0,0 +1,922 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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. + */ + +/** The definitive ANTLR v3 grammar to parse ANTLR v4 grammars. + * The grammar builds ASTs that are sniffed by subsequent stages. + */ +parser grammar ANTLRParser; + +options { + // Target language is Java, which is the default but being specific + // here as this grammar is also meant as a good example grammar for + // for users. + language = Java; + + // The output of this grammar is going to be an AST upon which + // we run a semantic checking phase, then the rest of the analysis + // including final code generation. + output = AST; + + // The vocabulary (tokens and their int token types) we are using + // for the parser. This is generated by the lexer. The vocab will be extended + // to include the imaginary tokens below. + tokenVocab = ANTLRLexer; + + ASTLabelType = GrammarAST; +} + +// Imaginary Tokens +// +// Imaginary tokens do not exist as far as the lexer is concerned, and it cannot +// generate them. However we sometimes need additional 'tokens' to use as root +// nodes for the AST we are generating. The tokens section is where we +// specify any such tokens +tokens { + RULE; + PREC_RULE; // flip to this if we find that it's left-recursive + RULES; + RULEMODIFIERS; + RULEACTIONS; + BLOCK; + OPTIONAL; + CLOSURE; + POSITIVE_CLOSURE; + RANGE; + SET; + CHAR_RANGE; + EPSILON; + ALT; + ALTLIST; + ID; + ARG; + ARGLIST; + RET; + COMBINED; + INITACTION; + LABEL; // $x used in rewrite rules + TEMPLATE; + WILDCARD; + // A generic node indicating a list of something when we don't + // really need to distinguish what we have a list of as the AST + // will 'kinow' by context. + // + LIST; + ELEMENT_OPTIONS; // TOKEN<options> + RESULT; + + // lexer action stuff + LEXER_ALT_ACTION; + LEXER_ACTION_CALL; // ID(foo) +} + +// Include the copyright in this source and also the generated source +// +@header { +/* + [The "BSD licence"] + Copyright (c) 2005-20012 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.parse; + +import org.antlr.v4.tool.*; +import org.antlr.v4.tool.ast.*; + +import java.util.ArrayDeque; +import java.util.Deque; +} + +@members { +Deque<String> paraphrases = new ArrayDeque<String>(); +public void grammarError(ErrorType etype, org.antlr.runtime.Token token, Object... args) { } +} + +// The main entry point for parsing a V3 grammar from top to toe. This is +// the method call from whence to obtain the AST for the parse. +// +grammarSpec +@after { +GrammarAST options = (GrammarAST)$tree.getFirstChildWithType(ANTLRParser.OPTIONS); +if ( options!=null ) { + Grammar.setNodeOptions($tree, options); +} +} + : // First we should see the type and name of the grammar file that + // we are about to parse. + // + grammarType id SEMI + + // There now follows zero or more declaration sections that should + // be given to us before the rules are declared + // +// A number of things can be declared/stated before the grammar rules +// 'proper' are parsed. These include grammar imports (delegate), grammar +// options, imaginary token declarations, global scope declarations, +// and actions such as @header. In this rule we allow any number of +// these constructs in any order so that the grammar author is not +// constrained by some arbitrary order of declarations that nobody +// can remember. In the next phase of the parse, we verify that these +// constructs are valid, not repeated and so on. + sync ( prequelConstruct sync )* + + // We should now see at least one ANTLR EBNF style rule + // declaration. If the rules are missing we will let the + // semantic verification phase tell the user about it. + // + rules + + modeSpec* + + // And we force ANTLR to process everything it finds in the input + // stream by specifying hte need to match End Of File before the + // parse is complete. + // + EOF + + // Having parsed everything in the file and accumulated the relevant + // subtrees, we can now rewrite everything into the main AST form + // that our tree walkers are expecting. + // + + -> ^(grammarType // The grammar type is our root AST node + id // We need to identify the grammar of course + prequelConstruct* // The set of declarations we accumulated + rules // And of course, we need the set of rules we discovered + modeSpec* + ) + ; + +grammarType +@after { + if ( $tg!=null ) throw new v3TreeGrammarException(tg); + if ( $t!=null ) ((GrammarRootAST)$tree).grammarType = $t.type; + else ((GrammarRootAST)$tree).grammarType=COMBINED; +} + : ( t=LEXER g=GRAMMAR -> GRAMMAR<GrammarRootAST>[$g, "LEXER_GRAMMAR", getTokenStream()] + | // A standalone parser specification + t=PARSER g=GRAMMAR -> GRAMMAR<GrammarRootAST>[$g, "PARSER_GRAMMAR", getTokenStream()] + + // A combined lexer and parser specification + | g=GRAMMAR -> GRAMMAR<GrammarRootAST>[$g, "COMBINED_GRAMMAR", getTokenStream()] + | tg=TREE_GRAMMAR + + ) + ; + +// This is the list of all constructs that can be declared before +// the set of rules that compose the grammar, and is invoked 0..n +// times by the grammarPrequel rule. +prequelConstruct + : // A list of options that affect analysis and/or code generation + optionsSpec + + | // A list of grammars to which this grammar will delegate certain + // parts of the parsing sequence - a set of imported grammars + delegateGrammars + + | // The declaration of any token types we need that are not already + // specified by a preceeding grammar, such as when a parser declares + // imaginary tokens with which to construct the AST, or a rewriting + // tree parser adds further imaginary tokens to ones defined in a prior + // {tree} parser. + tokensSpec + + | // A list of custom channels used by the grammar + channelsSpec + + | // A declaration of language target implemented constructs. All such + // action sections start with '@' and are given to the language target's + // StringTemplate group. For instance @parser::header and @lexer::header + // are gathered here. + action + ; + +// A list of options that affect analysis and/or code generation +optionsSpec + : OPTIONS (option SEMI)* RBRACE -> ^(OPTIONS[$OPTIONS, "OPTIONS"] option*) + ; + +option + : id ASSIGN^ optionValue + ; + +// ------------ +// Option Value +// +// The actual value of an option - Doh! +// +optionValue + : // If the option value is a single word that conforms to the + // lexical rules of token or rule names, then the user may skip quotes + // and so on. Many option values meet this description + qid + | STRING_LITERAL + | ACTION<ActionAST> + | INT + ; + +// A list of grammars to which this grammar will delegate certain +// parts of the parsing sequence - a set of imported grammars +delegateGrammars + : IMPORT delegateGrammar (COMMA delegateGrammar)* SEMI -> ^(IMPORT delegateGrammar+) + ; + +// A possibly named grammar file that should be imported to this gramamr +// and delgated to for the rules it specifies +delegateGrammar + : id ASSIGN^ id + | id + ; + +tokensSpec + : TOKENS_SPEC id (COMMA id)* RBRACE -> ^(TOKENS_SPEC id+) + | TOKENS_SPEC RBRACE -> + | TOKENS_SPEC^ v3tokenSpec+ RBRACE! + {grammarError(ErrorType.V3_TOKENS_SYNTAX, $TOKENS_SPEC);} + ; + +v3tokenSpec + : id + ( ASSIGN lit=STRING_LITERAL + { + grammarError(ErrorType.V3_ASSIGN_IN_TOKENS, $id.start, + $id.text, $lit.getText()); + } + -> id // ignore assignment + | -> id + ) + SEMI + ; + +channelsSpec + : CHANNELS^ id (COMMA! id)* RBRACE! + ; + +// A declaration of a language target specifc section, +// such as @header, @includes and so on. We do not verify these +// sections, they are just passed on to the language target. +/** Match stuff like @parser::members {int i;} */ +action + : AT (actionScopeName COLONCOLON)? id ACTION -> ^(AT actionScopeName? id ACTION<ActionAST>) + ; + +/** Sometimes the scope names will collide with keywords; allow them as + * ids for action scopes. + */ +actionScopeName + : id + | LEXER -> ID[$LEXER] + | PARSER -> ID[$PARSER] + ; + +modeSpec + : MODE id SEMI sync (lexerRule sync)* -> ^(MODE id lexerRule*) + ; + +rules + : sync (rule sync)* + // Rewrite with an enclosing node as this is good for counting + // the number of rules and an easy marker for the walker to detect + // that there are no rules. + ->^(RULES rule*) + ; + +sync +@init { + BitSet followSet = computeErrorRecoverySet(); + if ( input.LA(1)!=Token.EOF && !followSet.member(input.LA(1)) ) { + reportError(new NoViableAltException("",0,0,input)); + beginResync(); + consumeUntil(input, followSet); + endResync(); + } +} : + ; + +rule: parserRule + | lexerRule + ; + +// The specification of an EBNF rule in ANTLR style, with all the +// rule level parameters, declarations, actions, rewrite specs and so +// on. +// +// Note that here we allow any number of rule declaration sections (such +// as scope, returns, etc) in any order and we let the upcoming semantic +// verification of the AST determine if things are repeated or if a +// particular functional element is not valid in the context of the +// grammar type, such as using returns in lexer rules and so on. +parserRule +@init { paraphrases.push("matching a rule"); } +@after { + paraphrases.pop(); + GrammarAST options = (GrammarAST)$tree.getFirstChildWithType(ANTLRParser.OPTIONS); + if ( options!=null ) { + Grammar.setNodeOptions($tree, options); + } +} + : // Start with the rule name. Here we do not distinguish between + // parser or lexer rules, the semantic verification phase will + // reject any rules that make no sense, such as lexer rules in + // a pure parser or tree parser. + RULE_REF + + // Immediately following the rulename, there may be a specification + // of input parameters for the rule. We do not do anything with the + // parameters here except gather them for future phases such as + // semantic verifcation, type assignment etc. We require that + // the input parameters are the next syntactically significant element + // following the rule id. + ARG_ACTION? + + ruleReturns? + + throwsSpec? + + localsSpec? + + // Now, before the rule specification itself, which is introduced + // with a COLON, we may have zero or more configuration sections. + // As usual we just accept anything that is syntactically valid for + // one form of the rule or another and let the semantic verification + // phase throw out anything that is invalid. +// At the rule level, a programmer may specify a number of sections, such +// as scope declarations, rule return elements, @ sections (which may be +// language target specific) and so on. We allow any number of these in any +// order here and as usual rely onthe semantic verification phase to reject +// anything invalid using its addinotal context information. Here we are +// context free and just accept anything that is a syntactically correct +// construct. +// + rulePrequels + + COLON + + // The rule is, at the top level, just a list of alts, with + // finer grained structure defined within the alts. + ruleBlock + + SEMI + + exceptionGroup + + -> ^( RULE<RuleAST> RULE_REF ARG_ACTION<ActionAST>? + ruleReturns? throwsSpec? localsSpec? rulePrequels? ruleBlock exceptionGroup* + ) + ; + +// Many language targets support exceptions and the rule will +// generally be able to throw the language target equivalent +// of a recognition exception. The grammar programmar can +// specify a list of exceptions to catch or a generic catch all +// and the target language code generation template is +// responsible for generating code that makes sense. +exceptionGroup + : exceptionHandler* finallyClause? + ; + +// Specifies a handler for a particular type of exception +// thrown by a rule +exceptionHandler + : CATCH ARG_ACTION ACTION -> ^(CATCH ARG_ACTION<ActionAST> ACTION<ActionAST>) + ; + +finallyClause + : FINALLY ACTION -> ^(FINALLY ACTION<ActionAST>) + ; + +rulePrequels +@init { paraphrases.push("matching rule preamble"); } +@after { paraphrases.pop(); } + : sync (rulePrequel sync)* -> rulePrequel* + ; + +// An individual rule level configuration as referenced by the ruleActions +// rule above. +// +rulePrequel + : optionsSpec + | ruleAction + ; + +// A rule can return elements that it constructs as it executes. +// The return values are specified in a 'returns' prequel element, +// which contains COMMA separated declarations, where the declaration +// is target language specific. Here we see the returns declaration +// as a single lexical action element, to be processed later. +// +ruleReturns + : RETURNS^ ARG_ACTION<ActionAST> + ; + +// -------------- +// Exception spec +// +// Some target languages, such as Java and C# support exceptions +// and they are specified as a prequel element for each rule that +// wishes to throw its own exception type. Note that the name of the +// exception is just a single word, so the header section of the grammar +// must specify the correct import statements (or language equivalent). +// Target languages that do not support exceptions just safely ignore +// them. +// +throwsSpec + : THROWS qid (COMMA qid)* -> ^(THROWS qid+) + ; + +// locals [Cat x, float g] +localsSpec : LOCALS^ ARG_ACTION<ActionAST> ; + +// @ Sections are generally target language specific things +// such as local variable declarations, code to run before the +// rule starts and so on. Fir instance most targets support the +// @init {} section where declarations and code can be placed +// to run before the rule is entered. The C target also has +// an @declarations {} section, where local variables are declared +// in order that the generated code is C89 copmliant. +// +/** Match stuff like @init {int i;} */ +ruleAction + : AT id ACTION -> ^(AT id ACTION<ActionAST>) + ; + +// A set of alts, rewritten as a BLOCK for generic processing +// in tree walkers. Used by the rule 'rule' so that the list of +// alts for a rule appears as a BLOCK containing the alts and +// can be processed by the generic BLOCK rule. Note that we +// use a separate rule so that the BLOCK node has start and stop +// boundaries set correctly by rule post processing of rewrites. +ruleBlock +@init {Token colon = input.LT(-1);} + : ruleAltList -> ^(BLOCK<BlockAST>[colon,"BLOCK"] ruleAltList) + ; + catch [ResyncToEndOfRuleBlock e] { + // just resyncing; ignore error + retval.tree = (GrammarAST)adaptor.errorNode(input, retval.start, input.LT(-1), null); + } + +ruleAltList + : labeledAlt (OR labeledAlt)* -> labeledAlt+ + ; + +labeledAlt + : alternative + ( POUND! id! {((AltAST)$alternative.tree).altLabel=$id.tree;} + )? + ; + +lexerRule +@init { paraphrases.push("matching a lexer rule"); } +@after { + paraphrases.pop(); +} + : FRAGMENT? + TOKEN_REF COLON lexerRuleBlock SEMI + -> ^( RULE<RuleAST> TOKEN_REF + ^(RULEMODIFIERS FRAGMENT)? lexerRuleBlock + ) + ; + +lexerRuleBlock +@init {Token colon = input.LT(-1);} + : lexerAltList -> ^(BLOCK<BlockAST>[colon,"BLOCK"] lexerAltList) + ; + catch [ResyncToEndOfRuleBlock e] { + // just resyncing; ignore error + retval.tree = (GrammarAST)adaptor.errorNode(input, retval.start, input.LT(-1), null); + } + +lexerAltList + : lexerAlt (OR lexerAlt)* -> lexerAlt+ + ; + +lexerAlt + : lexerElements + ( lexerCommands -> ^(LEXER_ALT_ACTION<AltAST> lexerElements lexerCommands) + | -> lexerElements + ) + ; + +lexerElements + : lexerElement+ -> ^(ALT<AltAST> lexerElement+) + | -> ^(ALT<AltAST> EPSILON) // empty alt + ; + +lexerElement +@init { + paraphrases.push("looking for lexer rule element"); + int m = input.mark(); +} +@after { paraphrases.pop(); } + : labeledLexerElement + ( ebnfSuffix -> ^( ebnfSuffix ^(BLOCK<BlockAST>[$labeledLexerElement.start,"BLOCK"] ^(ALT<AltAST> labeledLexerElement) ) ) + | -> labeledLexerElement + ) + | lexerAtom + ( ebnfSuffix -> ^( ebnfSuffix ^(BLOCK<BlockAST>[$lexerAtom.start,"BLOCK"] ^(ALT<AltAST> lexerAtom) ) ) + | -> lexerAtom + ) + | lexerBlock + ( ebnfSuffix -> ^(ebnfSuffix lexerBlock) + | -> lexerBlock + ) + | actionElement // actions only allowed at end of outer alt actually, + // but preds can be anywhere + ; + catch [RecognitionException re] { + retval.tree = (GrammarAST)adaptor.errorNode(input, retval.start, input.LT(-1), re); + int ttype = input.get(input.range()).getType(); // seems to be next token + // look for anything that really belongs at the start of the rule minus the initial ID + if ( ttype==COLON || ttype==RETURNS || ttype==CATCH || ttype==FINALLY || ttype==AT || ttype==EOF ) { + RecognitionException missingSemi = + new v4ParserException("unterminated rule (missing ';') detected at '"+ + input.LT(1).getText()+" "+input.LT(2).getText()+"'", input); + reportError(missingSemi); + if ( ttype==EOF ) { + input.seek(input.index()+1); + } + else if ( ttype==CATCH || ttype==FINALLY ) { + input.seek(input.range()); // ignore what's before rule trailer stuff + } + else if ( ttype==RETURNS || ttype==AT ) { // scan back looking for ID of rule header + int p = input.index(); + Token t = input.get(p); + while ( t.getType()!=RULE_REF && t.getType()!=TOKEN_REF ) { + p--; + t = input.get(p); + } + input.seek(p); + } + throw new ResyncToEndOfRuleBlock(); // make sure it goes back to rule block level to recover + } + reportError(re); + recover(input,re); + } + +labeledLexerElement + : id (ass=ASSIGN|ass=PLUS_ASSIGN) + ( lexerAtom -> ^($ass id lexerAtom) + | lexerBlock -> ^($ass id lexerBlock) + ) + ; + + +lexerBlock +@after { +GrammarAST options = (GrammarAST)$tree.getFirstChildWithType(ANTLRParser.OPTIONS); +if ( options!=null ) { + Grammar.setNodeOptions($tree, options); +} +} + : LPAREN + ( optionsSpec COLON )? + lexerAltList + RPAREN + -> ^(BLOCK<BlockAST>[$LPAREN,"BLOCK"] optionsSpec? lexerAltList ) + ; + +// channel=HIDDEN, skip, more, mode(INSIDE), push(INSIDE), pop +lexerCommands + : RARROW lexerCommand (COMMA lexerCommand)* -> lexerCommand+ + ; + +lexerCommand + : lexerCommandName LPAREN lexerCommandExpr RPAREN -> ^(LEXER_ACTION_CALL lexerCommandName lexerCommandExpr) + | lexerCommandName + ; + +lexerCommandExpr + : id + | INT + ; + +lexerCommandName + : id + | MODE ->ID[$MODE] + ; + +altList + : alternative (OR alternative)* -> alternative+ + ; + +// An individual alt with an optional alt option like <assoc=right> +alternative +@init { paraphrases.push("matching alternative"); } +@after { + paraphrases.pop(); + Grammar.setNodeOptions($tree, $o.tree); +} + : o=elementOptions? + ( e+=element+ -> ^(ALT<AltAST> elementOptions? $e+) + | -> ^(ALT<AltAST> elementOptions? EPSILON) // empty alt + ) + ; + +element +@init { + paraphrases.push("looking for rule element"); + int m = input.mark(); +} +@after { paraphrases.pop(); } + : labeledElement + ( ebnfSuffix -> ^( ebnfSuffix ^(BLOCK<BlockAST>[$labeledElement.start,"BLOCK"] ^(ALT<AltAST> labeledElement ) )) + | -> labeledElement + ) + | atom + ( ebnfSuffix -> ^( ebnfSuffix ^(BLOCK<BlockAST>[$atom.start,"BLOCK"] ^(ALT<AltAST> atom) ) ) + | -> atom + ) + | ebnf + | actionElement + ; + catch [RecognitionException re] { + retval.tree = (GrammarAST)adaptor.errorNode(input, retval.start, input.LT(-1), re); + int ttype = input.get(input.range()).getType(); + // look for anything that really belongs at the start of the rule minus the initial ID + if ( ttype==COLON || ttype==RETURNS || ttype==CATCH || ttype==FINALLY || ttype==AT ) { + RecognitionException missingSemi = + new v4ParserException("unterminated rule (missing ';') detected at '"+ + input.LT(1).getText()+" "+input.LT(2).getText()+"'", input); + reportError(missingSemi); + if ( ttype==CATCH || ttype==FINALLY ) { + input.seek(input.range()); // ignore what's before rule trailer stuff + } + if ( ttype==RETURNS || ttype==AT ) { // scan back looking for ID of rule header + int p = input.index(); + Token t = input.get(p); + while ( t.getType()!=RULE_REF && t.getType()!=TOKEN_REF ) { + p--; + t = input.get(p); + } + input.seek(p); + } + throw new ResyncToEndOfRuleBlock(); // make sure it goes back to rule block level to recover + } + reportError(re); + recover(input,re); + } + +actionElement +@after { + GrammarAST options = (GrammarAST)$tree.getFirstChildWithType(ANTLRParser.ELEMENT_OPTIONS); + if ( options!=null ) { + Grammar.setNodeOptions($tree, options); + } +} + : ACTION<ActionAST> + | ACTION elementOptions -> ^(ACTION<ActionAST> elementOptions) + | SEMPRED<PredAST> + | SEMPRED elementOptions -> ^(SEMPRED<PredAST> elementOptions) + ; + +labeledElement + : id (ass=ASSIGN|ass=PLUS_ASSIGN) + ( atom -> ^($ass id atom) + | block -> ^($ass id block) + ) + ; + +// A block of gramamr structure optionally followed by standard EBNF +// notation, or ANTLR specific notation. I.E. ? + ^ and so on +ebnf + : block + // And now we see if we have any of the optional suffixs and rewrite + // the AST for this rule accordingly + ( blockSuffix -> ^(blockSuffix block) + | -> block + ) + ; + +// The standard EBNF suffixes with additional components that make +// sense only to ANTLR, in the context of a grammar block. +blockSuffix + : ebnfSuffix // Standard EBNF + ; + +ebnfSuffix + : QUESTION nongreedy=QUESTION? -> OPTIONAL<OptionalBlockAST>[$start, $nongreedy] + | STAR nongreedy=QUESTION? -> CLOSURE<StarBlockAST>[$start, $nongreedy] + | PLUS nongreedy=QUESTION? -> POSITIVE_CLOSURE<PlusBlockAST>[$start, $nongreedy] + ; + +lexerAtom + : range + | terminal + | RULE_REF<RuleRefAST> + | notSet + | wildcard + | LEXER_CHAR_SET + ; + +atom + : // Qualified reference delegate.rule. This must be + // lexically contiguous (no spaces either side of the DOT) + // otherwise it is two references with a wildcard in between + // and not a qualified reference. + /* + { + input.LT(1).getCharPositionInLine()+input.LT(1).getText().length()== + input.LT(2).getCharPositionInLine() && + input.LT(2).getCharPositionInLine()+1==input.LT(3).getCharPositionInLine() + }? + id DOT ruleref -> ^(DOT id ruleref) + + | + */ + range // Range x..y - only valid in lexers + | terminal + | ruleref + | notSet + | wildcard + ; + catch [RecognitionException re] { throw re; } // pass upwards to element + +wildcard +@after { + GrammarAST options = (GrammarAST)$tree.getFirstChildWithType(ANTLRParser.ELEMENT_OPTIONS); + if ( options!=null ) { + Grammar.setNodeOptions($tree, options); + } +} + : // Wildcard '.' means any character in a lexer, any + // token in parser and any node or subtree in a tree parser + // Because the terminal rule is allowed to be the node + // specification for the start of a tree rule, we must + // later check that wildcard was not used for that. + DOT elementOptions? + -> ^(WILDCARD<TerminalAST>[$DOT] elementOptions?) + ; + +// -------------------- +// Inverted element set +// +// A set of characters (in a lexer) or terminal tokens, if a parser, +// that are then used to create the inverse set of them. +notSet + : NOT setElement -> ^(NOT<NotAST>[$NOT] ^(SET<SetAST>[$setElement.start,"SET"] setElement)) + | NOT blockSet -> ^(NOT<NotAST>[$NOT] blockSet) + ; + +blockSet +@init { + Token t; + boolean ebnf = false; +} + : LPAREN setElement (OR setElement)* RPAREN + -> ^(SET<SetAST>[$LPAREN,"SET"] setElement+ ) + ; + +setElement + : TOKEN_REF<TerminalAST>^ elementOptions? + | STRING_LITERAL<TerminalAST>^ elementOptions? + | range + | LEXER_CHAR_SET + ; + +// ------------- +// Grammar Block +// +// Anywhere where an element is valid, the grammar may start a new block +// of alts by surrounding that block with ( ). A new block may also have a set +// of options, which apply only to that block. +// +block +@after { +GrammarAST options = (GrammarAST)$tree.getFirstChildWithType(ANTLRParser.OPTIONS); +if ( options!=null ) { + Grammar.setNodeOptions($tree, options); +} +} + : LPAREN + ( optionsSpec? ra+=ruleAction* COLON )? + altList + RPAREN + -> ^(BLOCK<BlockAST>[$LPAREN,"BLOCK"] optionsSpec? $ra* altList ) + ; + +// ---------------- +// Parser rule ref +// +// Reference to a parser rule with optional arguments and optional +// directive to become the root node or ignore the tree produced +// +ruleref +@after { +GrammarAST options = (GrammarAST)$tree.getFirstChildWithType(ANTLRParser.ELEMENT_OPTIONS); +if ( options!=null ) { + Grammar.setNodeOptions($tree, options); +} +} + : RULE_REF ARG_ACTION? elementOptions? -> ^(RULE_REF<RuleRefAST> ARG_ACTION<ActionAST>? elementOptions?) + ; + catch [RecognitionException re] { throw re; } // pass upwards to element + +// --------------- +// Character Range +// +// Specifies a range of characters. Valid for lexer rules only, but +// we do not check that here, the tree walkers shoudl do that. +// Note also that the parser also allows through more than just +// character literals so that we can produce a much nicer semantic +// error about any abuse of the .. operator. +// +range + : STRING_LITERAL<TerminalAST> RANGE<RangeAST>^ STRING_LITERAL<TerminalAST> + ; + +terminal +@after { +GrammarAST options = (GrammarAST)$tree.getFirstChildWithType(ANTLRParser.ELEMENT_OPTIONS); +if ( options!=null ) { + Grammar.setNodeOptions($tree, options); +} +} + : TOKEN_REF elementOptions? -> ^(TOKEN_REF<TerminalAST> elementOptions?) + | STRING_LITERAL elementOptions? -> ^(STRING_LITERAL<TerminalAST> elementOptions?) + ; + +// Terminals may be adorned with certain options when +// reference in the grammar: TOK<,,,> +elementOptions + : LT (elementOption (COMMA elementOption)*)? GT + -> ^(ELEMENT_OPTIONS[$LT,"ELEMENT_OPTIONS"] elementOption*) + ; + +// When used with elements we can specify what the tree node type can +// be and also assign settings of various options (which we do not check here) +elementOption + : // This format indicates the default element option + qid + | id ASSIGN^ optionValue + ; + +// The name of the grammar, and indeed some other grammar elements may +// come through to the parser looking like a rule reference or a token +// reference, hence this rule is used to pick up whichever it is and rewrite +// it as a generic ID token. +id +@init { paraphrases.push("looking for an identifier"); } +@after { paraphrases.pop(); } + : RULE_REF ->ID[$RULE_REF] + | TOKEN_REF ->ID[$TOKEN_REF] + ; + +qid +@init { paraphrases.push("looking for a qualified identifier"); } +@after { paraphrases.pop(); } + : id (DOT id)* -> ID[$qid.start, $text] + ; + +alternativeEntry : alternative EOF ; // allow gunit to call alternative and see EOF afterwards +elementEntry : element EOF ; +ruleEntry : rule EOF ; +blockEntry : block EOF ; diff --git a/tool/src/org/antlr/v4/parse/ATNBuilder.g b/tool/src/org/antlr/v4/parse/ATNBuilder.g new file mode 100644 index 0000000..019f246 --- /dev/null +++ b/tool/src/org/antlr/v4/parse/ATNBuilder.g @@ -0,0 +1,214 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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. + */ + +tree grammar ATNBuilder; +options { + language = Java; + tokenVocab = ANTLRParser; + ASTLabelType = GrammarAST; +// filter = true; +} + +// Include the copyright in this source and also the generated source +@header { +/* + [The "BSD license"] + Copyright (c) 2010 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.parse; +import org.antlr.v4.tool.*; +import org.antlr.v4.tool.ast.*; +import org.antlr.v4.automata.ATNFactory; +} + +@members { + ATNFactory factory; + public ATNBuilder(TreeNodeStream input, ATNFactory factory) { + this(input); + this.factory = factory; + } +} + +dummy : block[null] ; // avoid error about no start rule + +ruleBlock[GrammarAST ebnfRoot] returns [ATNFactory.Handle p] +@init { + List<ATNFactory.Handle> alts = new ArrayList<ATNFactory.Handle>(); + int alt = 1; + factory.setCurrentOuterAlt(alt); +} + : ^(BLOCK + (^(OPTIONS .*))? + ( a=alternative + {alts.add($a.p); factory.setCurrentOuterAlt(++alt);} + )+ + ) + {$p = factory.block((BlockAST)$BLOCK, ebnfRoot, alts);} + ; + +block[GrammarAST ebnfRoot] returns [ATNFactory.Handle p] +@init {List<ATNFactory.Handle> alts = new ArrayList<ATNFactory.Handle>();} + : ^(BLOCK (^(OPTIONS .*))? (a=alternative {alts.add($a.p);})+) + {$p = factory.block((BlockAST)$BLOCK, ebnfRoot, alts);} + ; + +alternative returns [ATNFactory.Handle p] +@init {List<ATNFactory.Handle> els = new ArrayList<ATNFactory.Handle>();} + : ^(LEXER_ALT_ACTION a=alternative lexerCommands) + {$p = factory.lexerAltCommands($a.p,$lexerCommands.p);} + | ^(ALT elementOptions? EPSILON) {$p = factory.epsilon($EPSILON);} + | ^(ALT elementOptions? (e=element {els.add($e.p);})+) {$p = factory.alt(els);} + ; + +lexerCommands returns [ATNFactory.Handle p] +@init {List<ATNFactory.Handle> cmds = new ArrayList<ATNFactory.Handle>();} + : (c=lexerCommand {if ($c.cmd != null) cmds.add($c.cmd);})+ + { + $p = factory.alt(cmds); + } + ; + +lexerCommand returns [ATNFactory.Handle cmd] + : ^(LEXER_ACTION_CALL ID lexerCommandExpr) + {$cmd = factory.lexerCallCommand($ID, $lexerCommandExpr.start);} + | ID + {$cmd = factory.lexerCommand($ID);} + ; + +lexerCommandExpr + : ID + | INT + ; + +element returns [ATNFactory.Handle p] + : labeledElement {$p = $labeledElement.p;} + | atom {$p = $atom.p;} + | subrule {$p = $subrule.p;} + | ACTION {$p = factory.action((ActionAST)$ACTION);} + | SEMPRED {$p = factory.sempred((PredAST)$SEMPRED);} + | ^(ACTION .) {$p = factory.action((ActionAST)$ACTION);} + | ^(SEMPRED .) {$p = factory.sempred((PredAST)$SEMPRED);} + | ^(NOT b=blockSet[true]) {$p = $b.p;} + | LEXER_CHAR_SET {$p = factory.charSetLiteral($start);} + ; + +astOperand returns [ATNFactory.Handle p] + : atom {$p = $atom.p;} + | ^(NOT blockSet[true]) {$p = $blockSet.p;} + ; + +labeledElement returns [ATNFactory.Handle p] + : ^(ASSIGN ID element) {$p = factory.label($element.p);} + | ^(PLUS_ASSIGN ID element) {$p = factory.listLabel($element.p);} + ; + +subrule returns [ATNFactory.Handle p] + : ^(OPTIONAL block[$start]) {$p = $block.p;} + | ^(CLOSURE block[$start]) {$p = $block.p;} + | ^(POSITIVE_CLOSURE block[$start]) {$p = $block.p;} + | block[null] {$p = $block.p;} + ; + +blockSet[boolean invert] returns [ATNFactory.Handle p] +@init {List<GrammarAST> alts = new ArrayList<GrammarAST>();} + : ^(SET (setElement {alts.add($setElement.start);})+) {$p = factory.set($start, alts, $invert);} + ; + +/** Don't combine with atom otherwise it will build spurious ATN nodes */ +setElement + : ^(STRING_LITERAL .) + | ^(TOKEN_REF .) + | STRING_LITERAL + | TOKEN_REF + | ^(RANGE a=STRING_LITERAL b=STRING_LITERAL) + | LEXER_CHAR_SET + ; + +atom returns [ATNFactory.Handle p] + : range {$p = $range.p;} + | ^(DOT ID terminal) {$p = $terminal.p;} + | ^(DOT ID ruleref) {$p = $ruleref.p;} + | ^(WILDCARD .) {$p = factory.wildcard($start);} + | WILDCARD {$p = factory.wildcard($start);} + | blockSet[false] {$p = $blockSet.p;} + | terminal {$p = $terminal.p;} + | ruleref {$p = $ruleref.p;} + ; + +ruleref returns [ATNFactory.Handle p] + : ^(RULE_REF ARG_ACTION? ^(ELEMENT_OPTIONS .*)) {$p = factory.ruleRef($RULE_REF);} + | ^(RULE_REF ARG_ACTION?) {$p = factory.ruleRef($RULE_REF);} + | RULE_REF {$p = factory.ruleRef($RULE_REF);} + ; + +range returns [ATNFactory.Handle p] + : ^(RANGE a=STRING_LITERAL b=STRING_LITERAL) {$p = factory.range($a,$b);} + ; + +terminal returns [ATNFactory.Handle p] + : ^(STRING_LITERAL .) {$p = factory.stringLiteral((TerminalAST)$start);} + | STRING_LITERAL {$p = factory.stringLiteral((TerminalAST)$start);} + | ^(TOKEN_REF ARG_ACTION .) {$p = factory.tokenRef((TerminalAST)$start);} + | ^(TOKEN_REF .) {$p = factory.tokenRef((TerminalAST)$start);} + | TOKEN_REF {$p = factory.tokenRef((TerminalAST)$start);} + ; + +elementOptions + : ^(ELEMENT_OPTIONS elementOption*) + ; + +elementOption + : ID + | ^(ASSIGN ID ID) + | ^(ASSIGN ID STRING_LITERAL) + | ^(ASSIGN ID ACTION) + | ^(ASSIGN ID INT) + ; diff --git a/tool/src/org/antlr/v4/parse/ActionSplitter.g b/tool/src/org/antlr/v4/parse/ActionSplitter.g new file mode 100644 index 0000000..5feeb95 --- /dev/null +++ b/tool/src/org/antlr/v4/parse/ActionSplitter.g @@ -0,0 +1,125 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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. + */ + +lexer grammar ActionSplitter; + +options { filter=true; } + +@header { +package org.antlr.v4.parse; +import org.antlr.v4.tool.*; +import org.antlr.v4.tool.ast.*; +} + +@members { +ActionSplitterListener delegate; + +public ActionSplitter(CharStream input, ActionSplitterListener delegate) { + this(input, new RecognizerSharedState()); + this.delegate = delegate; +} + +/** force filtering (and return tokens). triggers all above actions. */ +public List<Token> getActionTokens() { + List<Token> chunks = new ArrayList<Token>(); + Token t = nextToken(); + while ( t.getType()!=Token.EOF ) { + chunks.add(t); + t = nextToken(); + } + return chunks; +} + +private boolean isIDStartChar(int c) { + return c == '_' || Character.isLetter(c); +} +} + +// ignore comments right away + +COMMENT + : '/*' ( options {greedy=false;} : . )* '*/' {delegate.text($text);} + ; + +LINE_COMMENT + : '//' ~('\n'|'\r')* '\r'? '\n' {delegate.text($text);} + ; + +SET_NONLOCAL_ATTR + : '$' x=ID '::' y=ID WS? '=' expr=ATTR_VALUE_EXPR ';' + { + delegate.setNonLocalAttr($text, $x, $y, $expr); + } + ; + +NONLOCAL_ATTR + : '$' x=ID '::' y=ID {delegate.nonLocalAttr($text, $x, $y);} + ; + +QUALIFIED_ATTR + : '$' x=ID '.' y=ID {input.LA(1)!='('}? {delegate.qualifiedAttr($text, $x, $y);} + ; + +SET_ATTR + : '$' x=ID WS? '=' expr=ATTR_VALUE_EXPR ';' + { + delegate.setAttr($text, $x, $expr); + } + ; + +ATTR + : '$' x=ID {delegate.attr($text, $x);} + ; + +// Anything else is just random text +TEXT +@init {StringBuilder buf = new StringBuilder();} +@after {delegate.text(buf.toString());} + : ( c=~('\\'| '$') {buf.append((char)$c);} + | '\\$' {buf.append('$');} + | '\\' c=~('$') {buf.append('\\').append((char)$c);} + | {!isIDStartChar(input.LA(2))}? => '$' {buf.append('$');} + )+ + ; + +fragment +ID : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')* + ; + +/** Don't allow an = as first char to prevent $x == 3; kind of stuff. */ +fragment +ATTR_VALUE_EXPR + : ~'=' (~';')* + ; + +fragment +WS : (' '|'\t'|'\n'|'\r')+ + ; + diff --git a/tool/src/org/antlr/v4/parse/ActionSplitterListener.java b/tool/src/org/antlr/v4/parse/ActionSplitterListener.java new file mode 100644 index 0000000..0e793b7 --- /dev/null +++ b/tool/src/org/antlr/v4/parse/ActionSplitterListener.java @@ -0,0 +1,45 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.parse; + +import org.antlr.runtime.Token; + +/** */ +public interface ActionSplitterListener { + void qualifiedAttr(String expr, Token x, Token y); + void setAttr(String expr, Token x, Token rhs); + void attr(String expr, Token x); + + void setNonLocalAttr(String expr, Token x, Token y, Token rhs); + void nonLocalAttr(String expr, Token x, Token y); + + void text(String text); +} diff --git a/tool/src/org/antlr/v4/parse/BlockSetTransformer.g b/tool/src/org/antlr/v4/parse/BlockSetTransformer.g new file mode 100644 index 0000000..2233b30 --- /dev/null +++ b/tool/src/org/antlr/v4/parse/BlockSetTransformer.g @@ -0,0 +1,129 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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. + */ + +tree grammar BlockSetTransformer; +options { + language = Java; + tokenVocab = ANTLRParser; + ASTLabelType = GrammarAST; + output = AST; + filter = true; +} + +@header { +package org.antlr.v4.parse; +import org.antlr.v4.misc.Utils; +import org.antlr.v4.misc.*; +import org.antlr.v4.tool.*; +import org.antlr.v4.tool.ast.*; +import java.util.List; +import java.util.Set; +import java.util.HashSet; +import java.util.ArrayList; +import org.antlr.v4.runtime.misc.IntervalSet; +} + +@members { +public String currentRuleName; +public GrammarAST currentAlt; +public Grammar g; +public BlockSetTransformer(TreeNodeStream input, Grammar g) { + this(input, new RecognizerSharedState()); + this.g = g; +} +} + +topdown + : ^(RULE (id=TOKEN_REF|id=RULE_REF) {currentRuleName=$id.text;} .+) + | setAlt + | ebnfBlockSet + | blockSet + ; + +setAlt + : {inContext("RULE BLOCK")}? + ALT {currentAlt = $start;} + ; + +// (BLOCK (ALT (+ (BLOCK (ALT INT) (ALT ID))))) +ebnfBlockSet +@after { + GrammarTransformPipeline.setGrammarPtr(g, $tree); +} + : ^(ebnfSuffix blockSet) -> ^(ebnfSuffix ^(BLOCK<BlockAST> ^(ALT<AltAST> blockSet))) + ; + +ebnfSuffix +@after {$tree = (GrammarAST)adaptor.dupNode($start);} + : OPTIONAL + | CLOSURE + | POSITIVE_CLOSURE + ; + +blockSet +@init { +boolean inLexer = Grammar.isTokenName(currentRuleName); +} +@after { + GrammarTransformPipeline.setGrammarPtr(g, $tree); +} + : {inContext("RULE")}? // top-level: rule block and > 1 alt + ^(BLOCK ^(alt=ALT elementOptions? {((AltAST)$alt).altLabel==null}? setElement[inLexer]) ( ^(ALT elementOptions? setElement[inLexer]) )+) + -> ^(BLOCK<BlockAST>[$BLOCK.token] ^(ALT<AltAST>[$BLOCK.token,"ALT"] ^(SET[$BLOCK.token, "SET"] setElement+))) + | {!inContext("RULE")}? // if not rule block and > 1 alt + ^(BLOCK ^(ALT elementOptions? setElement[inLexer]) ( ^(ALT elementOptions? setElement[inLexer]) )+) + -> ^(SET[$BLOCK.token, "SET"] setElement+) + ; + +setElement[boolean inLexer] +@after { + GrammarTransformPipeline.setGrammarPtr(g, $tree); +} + : ( ^(a=STRING_LITERAL elementOptions) {!inLexer || CharSupport.getCharValueFromGrammarCharLiteral($a.getText())!=-1}? + | a=STRING_LITERAL {!inLexer || CharSupport.getCharValueFromGrammarCharLiteral($a.getText())!=-1}? + | {!inLexer}?=> ^(TOKEN_REF elementOptions) + | {!inLexer}?=> TOKEN_REF + | {inLexer}?=> ^(RANGE a=STRING_LITERAL b=STRING_LITERAL) + {CharSupport.getCharValueFromGrammarCharLiteral($a.getText())!=-1 && + CharSupport.getCharValueFromGrammarCharLiteral($b.getText())!=-1}? + ) + ; + +elementOptions + : ^(ELEMENT_OPTIONS elementOption*) + ; + +elementOption + : ID + | ^(ASSIGN id=ID v=ID) + | ^(ASSIGN ID v=STRING_LITERAL) + | ^(ASSIGN ID v=ACTION) + | ^(ASSIGN ID v=INT) + ; diff --git a/tool/src/org/antlr/v4/parse/GrammarASTAdaptor.java b/tool/src/org/antlr/v4/parse/GrammarASTAdaptor.java new file mode 100644 index 0000000..e6a0ec3 --- /dev/null +++ b/tool/src/org/antlr/v4/parse/GrammarASTAdaptor.java @@ -0,0 +1,83 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.parse; + +import org.antlr.runtime.CommonToken; +import org.antlr.runtime.Token; +import org.antlr.runtime.tree.CommonTreeAdaptor; +import org.antlr.v4.tool.ast.GrammarAST; +import org.antlr.v4.tool.ast.GrammarASTErrorNode; +import org.antlr.v4.tool.ast.RuleAST; +import org.antlr.v4.tool.ast.TerminalAST; + +public class GrammarASTAdaptor extends CommonTreeAdaptor { + org.antlr.runtime.CharStream input; // where we can find chars ref'd by tokens in tree + public GrammarASTAdaptor() { } + public GrammarASTAdaptor(org.antlr.runtime.CharStream input) { this.input = input; } + + @Override + public Object create(Token token) { + return new GrammarAST(token); + } + + @Override + /** Make sure even imaginary nodes know the input stream */ + public Object create(int tokenType, String text) { + GrammarAST t; + if ( tokenType==ANTLRParser.RULE ) { + // needed by TreeWizard to make RULE tree + t = new RuleAST(new CommonToken(tokenType, text)); + } + else if ( tokenType==ANTLRParser.STRING_LITERAL ) { + // implicit lexer construction done with wizard; needs this node type + // whereas grammar ANTLRParser.g can use token option to spec node type + t = new TerminalAST(new CommonToken(tokenType, text)); + } + else { + t = (GrammarAST)super.create(tokenType, text); + } + t.token.setInputStream(input); + return t; + } + + @Override + public Object dupNode(Object t) { + if ( t==null ) return null; + return ((GrammarAST)t).dupNode(); //create(((GrammarAST)t).token); + } + + @Override + public Object errorNode(org.antlr.runtime.TokenStream input, org.antlr.runtime.Token start, org.antlr.runtime.Token stop, + org.antlr.runtime.RecognitionException e) + { + return new GrammarASTErrorNode(input, start, stop, e); + } +} diff --git a/tool/src/org/antlr/v4/parse/GrammarToken.java b/tool/src/org/antlr/v4/parse/GrammarToken.java new file mode 100644 index 0000000..4d849bf --- /dev/null +++ b/tool/src/org/antlr/v4/parse/GrammarToken.java @@ -0,0 +1,98 @@ +/* + * [The "BSD license"] + * Copyright (c) 2014 Terence Parr + * Copyright (c) 2014 Sam Harwell + * 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.parse; + +import org.antlr.runtime.CommonToken; +import org.antlr.runtime.Token; +import org.antlr.v4.tool.Grammar; + +/** A CommonToken that can also track it's original location, + * derived from options on the element ref like BEGIN<line=34,...>. + */ +public class GrammarToken extends CommonToken { + public Grammar g; + public int originalTokenIndex = -1; + + public GrammarToken(Grammar g, Token oldToken) { + super(oldToken); + this.g = g; + } + + @Override + public int getCharPositionInLine() { + if ( originalTokenIndex>=0 ) return g.originalTokenStream.get(originalTokenIndex).getCharPositionInLine(); + return super.getCharPositionInLine(); + } + + @Override + public int getLine() { + if ( originalTokenIndex>=0 ) return g.originalTokenStream.get(originalTokenIndex).getLine(); + return super.getLine(); + } + + @Override + public int getTokenIndex() { + return originalTokenIndex; + } + + @Override + public int getStartIndex() { + if ( originalTokenIndex>=0 ) { + return ((CommonToken)g.originalTokenStream.get(originalTokenIndex)).getStartIndex(); + } + return super.getStartIndex(); + } + + @Override + public int getStopIndex() { + int n = super.getStopIndex() - super.getStartIndex() + 1; + return getStartIndex() + n - 1; + } + + @Override + public String toString() { + String channelStr = ""; + if ( channel>0 ) { + channelStr=",channel="+channel; + } + String txt = getText(); + if ( txt!=null ) { + txt = txt.replaceAll("\n","\\\\n"); + txt = txt.replaceAll("\r","\\\\r"); + txt = txt.replaceAll("\t","\\\\t"); + } + else { + txt = "<no text>"; + } + return "[@"+getTokenIndex()+","+getStartIndex()+":"+getStopIndex()+ + "='"+txt+"',<"+getType()+">"+channelStr+","+getLine()+":"+getCharPositionInLine()+"]"; + } +} diff --git a/tool/src/org/antlr/v4/parse/GrammarTreeVisitor.g b/tool/src/org/antlr/v4/parse/GrammarTreeVisitor.g new file mode 100644 index 0000000..9c8b1be --- /dev/null +++ b/tool/src/org/antlr/v4/parse/GrammarTreeVisitor.g @@ -0,0 +1,1033 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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. + */ + +/** The definitive ANTLR v3 tree grammar to walk/visit ANTLR v4 grammars. + * Parses trees created by ANTLRParser.g. + * + * Rather than have multiple tree grammars, one for each visit, I'm + * creating this generic visitor that knows about context. All of the + * boilerplate pattern recognition is done here. Then, subclasses can + * override the methods they care about. This prevents a lot of the same + * context tracking stuff like "set current alternative for current + * rule node" that is repeated in lots of tree filters. + */ +tree grammar GrammarTreeVisitor; +options { + language = Java; + tokenVocab = ANTLRParser; + ASTLabelType = GrammarAST; +} + +// Include the copyright in this source and also the generated source +@header { +/* + [The "BSD license"] + Copyright (c) 2011 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.parse; +import org.antlr.v4.Tool; +import org.antlr.v4.tool.*; +import org.antlr.v4.tool.ast.*; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +} + +@members { +public String grammarName; +public GrammarAST currentRuleAST; +public String currentModeName = LexerGrammar.DEFAULT_MODE_NAME; +public String currentRuleName; +public GrammarAST currentOuterAltRoot; +public int currentOuterAltNumber = 1; // 1..n +public int rewriteEBNFLevel = 0; + +public GrammarTreeVisitor() { this(null); } + +// Should be abstract but can't make gen'd parser abstract; +// subclasses should implement else everything goes to stderr! +public ErrorManager getErrorManager() { return null; } + +public void visitGrammar(GrammarAST t) { visit(t, "grammarSpec"); } +public void visit(GrammarAST t, String ruleName) { + CommonTreeNodeStream nodes = new CommonTreeNodeStream(new GrammarASTAdaptor(), t); + setTreeNodeStream(nodes); + try { + Method m = getClass().getMethod(ruleName); + m.invoke(this); + } + catch (Throwable e) { + ErrorManager errMgr = getErrorManager(); + if ( e instanceof InvocationTargetException ) { + e = e.getCause(); + } + //e.printStackTrace(System.err); + if ( errMgr==null ) { + System.err.println("can't find rule "+ruleName+ + " or tree structure error: "+t.toStringTree() + ); + e.printStackTrace(System.err); + } + else errMgr.toolError(ErrorType.INTERNAL_ERROR, e); + } +} + +public void discoverGrammar(GrammarRootAST root, GrammarAST ID) { } +public void finishPrequels(GrammarAST firstPrequel) { } +public void finishGrammar(GrammarRootAST root, GrammarAST ID) { } + +public void grammarOption(GrammarAST ID, GrammarAST valueAST) { } +public void ruleOption(GrammarAST ID, GrammarAST valueAST) { } +public void blockOption(GrammarAST ID, GrammarAST valueAST) { } +public void defineToken(GrammarAST ID) { } +public void defineChannel(GrammarAST ID) { } +public void globalNamedAction(GrammarAST scope, GrammarAST ID, ActionAST action) { } +public void importGrammar(GrammarAST label, GrammarAST ID) { } + +public void modeDef(GrammarAST m, GrammarAST ID) { } + +public void discoverRules(GrammarAST rules) { } +public void finishRules(GrammarAST rule) { } +public void discoverRule(RuleAST rule, GrammarAST ID, List<GrammarAST> modifiers, + ActionAST arg, ActionAST returns, GrammarAST thrws, + GrammarAST options, ActionAST locals, + List<GrammarAST> actions, + GrammarAST block) { } +public void finishRule(RuleAST rule, GrammarAST ID, GrammarAST block) { } +public void discoverLexerRule(RuleAST rule, GrammarAST ID, List<GrammarAST> modifiers, + GrammarAST block) { } +public void finishLexerRule(RuleAST rule, GrammarAST ID, GrammarAST block) { } +public void ruleCatch(GrammarAST arg, ActionAST action) { } +public void finallyAction(ActionAST action) { } +public void discoverOuterAlt(AltAST alt) { } +public void finishOuterAlt(AltAST alt) { } +public void discoverAlt(AltAST alt) { } +public void finishAlt(AltAST alt) { } + +public void ruleRef(GrammarAST ref, ActionAST arg) { } +public void tokenRef(TerminalAST ref) { } +public void elementOption(GrammarASTWithOptions t, GrammarAST ID, GrammarAST valueAST) { } +public void stringRef(TerminalAST ref) { } +public void wildcardRef(GrammarAST ref) { } +public void actionInAlt(ActionAST action) { } +public void sempredInAlt(PredAST pred) { } +public void label(GrammarAST op, GrammarAST ID, GrammarAST element) { } +public void lexerCallCommand(int outerAltNumber, GrammarAST ID, GrammarAST arg) { } +public void lexerCommand(int outerAltNumber, GrammarAST ID) { } + +protected void enterGrammarSpec(GrammarAST tree) { } +protected void exitGrammarSpec(GrammarAST tree) { } + +protected void enterPrequelConstructs(GrammarAST tree) { } +protected void exitPrequelConstructs(GrammarAST tree) { } + +protected void enterPrequelConstruct(GrammarAST tree) { } +protected void exitPrequelConstruct(GrammarAST tree) { } + +protected void enterOptionsSpec(GrammarAST tree) { } +protected void exitOptionsSpec(GrammarAST tree) { } + +protected void enterOption(GrammarAST tree) { } +protected void exitOption(GrammarAST tree) { } + +protected void enterOptionValue(GrammarAST tree) { } +protected void exitOptionValue(GrammarAST tree) { } + +protected void enterDelegateGrammars(GrammarAST tree) { } +protected void exitDelegateGrammars(GrammarAST tree) { } + +protected void enterDelegateGrammar(GrammarAST tree) { } +protected void exitDelegateGrammar(GrammarAST tree) { } + +protected void enterTokensSpec(GrammarAST tree) { } +protected void exitTokensSpec(GrammarAST tree) { } + +protected void enterTokenSpec(GrammarAST tree) { } +protected void exitTokenSpec(GrammarAST tree) { } + +protected void enterChannelsSpec(GrammarAST tree) { } +protected void exitChannelsSpec(GrammarAST tree) { } + +protected void enterChannelSpec(GrammarAST tree) { } +protected void exitChannelSpec(GrammarAST tree) { } + +protected void enterAction(GrammarAST tree) { } +protected void exitAction(GrammarAST tree) { } + +protected void enterRules(GrammarAST tree) { } +protected void exitRules(GrammarAST tree) { } + +protected void enterMode(GrammarAST tree) { } +protected void exitMode(GrammarAST tree) { } + +protected void enterLexerRule(GrammarAST tree) { } +protected void exitLexerRule(GrammarAST tree) { } + +protected void enterRule(GrammarAST tree) { } +protected void exitRule(GrammarAST tree) { } + +protected void enterExceptionGroup(GrammarAST tree) { } +protected void exitExceptionGroup(GrammarAST tree) { } + +protected void enterExceptionHandler(GrammarAST tree) { } +protected void exitExceptionHandler(GrammarAST tree) { } + +protected void enterFinallyClause(GrammarAST tree) { } +protected void exitFinallyClause(GrammarAST tree) { } + +protected void enterLocals(GrammarAST tree) { } +protected void exitLocals(GrammarAST tree) { } + +protected void enterRuleReturns(GrammarAST tree) { } +protected void exitRuleReturns(GrammarAST tree) { } + +protected void enterThrowsSpec(GrammarAST tree) { } +protected void exitThrowsSpec(GrammarAST tree) { } + +protected void enterRuleAction(GrammarAST tree) { } +protected void exitRuleAction(GrammarAST tree) { } + +protected void enterRuleModifier(GrammarAST tree) { } +protected void exitRuleModifier(GrammarAST tree) { } + +protected void enterLexerRuleBlock(GrammarAST tree) { } +protected void exitLexerRuleBlock(GrammarAST tree) { } + +protected void enterRuleBlock(GrammarAST tree) { } +protected void exitRuleBlock(GrammarAST tree) { } + +protected void enterLexerOuterAlternative(AltAST tree) { } +protected void exitLexerOuterAlternative(AltAST tree) { } + +protected void enterOuterAlternative(AltAST tree) { } +protected void exitOuterAlternative(AltAST tree) { } + +protected void enterLexerAlternative(GrammarAST tree) { } +protected void exitLexerAlternative(GrammarAST tree) { } + +protected void enterLexerElements(GrammarAST tree) { } +protected void exitLexerElements(GrammarAST tree) { } + +protected void enterLexerElement(GrammarAST tree) { } +protected void exitLexerElement(GrammarAST tree) { } + +protected void enterLabeledLexerElement(GrammarAST tree) { } +protected void exitLabeledLexerElement(GrammarAST tree) { } + +protected void enterLexerBlock(GrammarAST tree) { } +protected void exitLexerBlock(GrammarAST tree) { } + +protected void enterLexerAtom(GrammarAST tree) { } +protected void exitLexerAtom(GrammarAST tree) { } + +protected void enterActionElement(GrammarAST tree) { } +protected void exitActionElement(GrammarAST tree) { } + +protected void enterAlternative(AltAST tree) { } +protected void exitAlternative(AltAST tree) { } + +protected void enterLexerCommand(GrammarAST tree) { } +protected void exitLexerCommand(GrammarAST tree) { } + +protected void enterLexerCommandExpr(GrammarAST tree) { } +protected void exitLexerCommandExpr(GrammarAST tree) { } + +protected void enterElement(GrammarAST tree) { } +protected void exitElement(GrammarAST tree) { } + +protected void enterAstOperand(GrammarAST tree) { } +protected void exitAstOperand(GrammarAST tree) { } + +protected void enterLabeledElement(GrammarAST tree) { } +protected void exitLabeledElement(GrammarAST tree) { } + +protected void enterSubrule(GrammarAST tree) { } +protected void exitSubrule(GrammarAST tree) { } + +protected void enterLexerSubrule(GrammarAST tree) { } +protected void exitLexerSubrule(GrammarAST tree) { } + +protected void enterBlockSuffix(GrammarAST tree) { } +protected void exitBlockSuffix(GrammarAST tree) { } + +protected void enterEbnfSuffix(GrammarAST tree) { } +protected void exitEbnfSuffix(GrammarAST tree) { } + +protected void enterAtom(GrammarAST tree) { } +protected void exitAtom(GrammarAST tree) { } + +protected void enterBlockSet(GrammarAST tree) { } +protected void exitBlockSet(GrammarAST tree) { } + +protected void enterSetElement(GrammarAST tree) { } +protected void exitSetElement(GrammarAST tree) { } + +protected void enterBlock(GrammarAST tree) { } +protected void exitBlock(GrammarAST tree) { } + +protected void enterRuleref(GrammarAST tree) { } +protected void exitRuleref(GrammarAST tree) { } + +protected void enterRange(GrammarAST tree) { } +protected void exitRange(GrammarAST tree) { } + +protected void enterTerminal(GrammarAST tree) { } +protected void exitTerminal(GrammarAST tree) { } + +protected void enterElementOptions(GrammarAST tree) { } +protected void exitElementOptions(GrammarAST tree) { } + +protected void enterElementOption(GrammarAST tree) { } +protected void exitElementOption(GrammarAST tree) { } + + @Override + public void traceIn(String ruleName, int ruleIndex) { + System.err.println("enter "+ruleName+": "+input.LT(1)); + } + + @Override + public void traceOut(String ruleName, int ruleIndex) { + System.err.println("exit "+ruleName+": "+input.LT(1)); + } +} + +grammarSpec +@init { + enterGrammarSpec($start); +} +@after { + exitGrammarSpec($start); +} + : ^( GRAMMAR ID {grammarName=$ID.text;} + {discoverGrammar((GrammarRootAST)$GRAMMAR, $ID);} + prequelConstructs + {finishPrequels($prequelConstructs.firstOne);} + rules mode* + {finishGrammar((GrammarRootAST)$GRAMMAR, $ID);} + ) + ; + +prequelConstructs returns [GrammarAST firstOne=null] +@init { + enterPrequelConstructs($start); +} +@after { + exitPrequelConstructs($start); +} + : {$firstOne=$start;} prequelConstruct+ + | + ; + +prequelConstruct +@init { + enterPrequelConstructs($start); +} +@after { + exitPrequelConstructs($start); +} + : optionsSpec + | delegateGrammars + | tokensSpec + | channelsSpec + | action + ; + +optionsSpec +@init { + enterOptionsSpec($start); +} +@after { + exitOptionsSpec($start); +} + : ^(OPTIONS option*) + ; + +option +@init { + enterOption($start); + boolean rule = inContext("RULE ..."); + boolean block = inContext("BLOCK ..."); +} +@after { + exitOption($start); +} + : ^(a=ASSIGN ID v=optionValue) + { + if ( block ) blockOption($ID, $v.start); // most specific first + else if ( rule ) ruleOption($ID, $v.start); + else grammarOption($ID, $v.start); + } + ; + +optionValue returns [String v] +@init { + enterOptionValue($start); + $v = $start.token.getText(); +} +@after { + exitOptionValue($start); +} + : ID + | STRING_LITERAL + | INT + ; + +delegateGrammars +@init { + enterDelegateGrammars($start); +} +@after { + exitDelegateGrammars($start); +} + : ^(IMPORT delegateGrammar+) + ; + +delegateGrammar +@init { + enterDelegateGrammar($start); +} +@after { + exitDelegateGrammar($start); +} + : ^(ASSIGN label=ID id=ID) {importGrammar($label, $id);} + | id=ID {importGrammar(null, $id);} + ; + +tokensSpec +@init { + enterTokensSpec($start); +} +@after { + exitTokensSpec($start); +} + : ^(TOKENS_SPEC tokenSpec+) + ; + +tokenSpec +@init { + enterTokenSpec($start); +} +@after { + exitTokenSpec($start); +} + : ID {defineToken($ID);} + ; + +channelsSpec +@init { + enterChannelsSpec($start); +} +@after { + exitChannelsSpec($start); +} + : ^(CHANNELS channelSpec+) + ; + +channelSpec +@init { + enterChannelSpec($start); +} +@after { + exitChannelSpec($start); +} + : ID {defineChannel($ID);} + ; + +action +@init { + enterAction($start); +} +@after { + exitAction($start); +} + : ^(AT sc=ID? name=ID ACTION) {globalNamedAction($sc, $name, (ActionAST)$ACTION);} + ; + +rules +@init { + enterRules($start); +} +@after { + exitRules($start); +} + : ^(RULES {discoverRules($RULES);} (rule|lexerRule)* {finishRules($RULES);}) + ; + +mode +@init { + enterMode($start); +} +@after { + exitMode($start); +} + : ^( MODE ID {currentModeName=$ID.text; modeDef($MODE, $ID);} lexerRule* ) + ; + +lexerRule +@init { + enterLexerRule($start); + List<GrammarAST> mods = new ArrayList<GrammarAST>(); + currentOuterAltNumber=0; +} +@after { + exitLexerRule($start); +} + : ^( RULE TOKEN_REF + {currentRuleName=$TOKEN_REF.text; currentRuleAST=$RULE;} + (^(RULEMODIFIERS m=FRAGMENT {mods.add($m);}))? + {discoverLexerRule((RuleAST)$RULE, $TOKEN_REF, mods, (GrammarAST)input.LT(1));} + lexerRuleBlock + { + finishLexerRule((RuleAST)$RULE, $TOKEN_REF, $lexerRuleBlock.start); + currentRuleName=null; currentRuleAST=null; + } + ) + ; + +rule +@init { + enterRule($start); + List<GrammarAST> mods = new ArrayList<GrammarAST>(); + List<GrammarAST> actions = new ArrayList<GrammarAST>(); // track roots + currentOuterAltNumber=0; +} +@after { + exitRule($start); +} + : ^( RULE RULE_REF {currentRuleName=$RULE_REF.text; currentRuleAST=$RULE;} + (^(RULEMODIFIERS (m=ruleModifier{mods.add($m.start);})+))? + ARG_ACTION? + ret=ruleReturns? + thr=throwsSpec? + loc=locals? + ( opts=optionsSpec + | a=ruleAction {actions.add($a.start);} + )* + {discoverRule((RuleAST)$RULE, $RULE_REF, mods, (ActionAST)$ARG_ACTION, + $ret.start!=null?(ActionAST)$ret.start.getChild(0):null, + $thr.start, $opts.start, + $loc.start!=null?(ActionAST)$loc.start.getChild(0):null, + actions, (GrammarAST)input.LT(1));} + ruleBlock exceptionGroup + {finishRule((RuleAST)$RULE, $RULE_REF, $ruleBlock.start); currentRuleName=null; currentRuleAST=null;} + ) + ; + +exceptionGroup +@init { + enterExceptionGroup($start); +} +@after { + exitExceptionGroup($start); +} + : exceptionHandler* finallyClause? + ; + +exceptionHandler +@init { + enterExceptionHandler($start); +} +@after { + exitExceptionHandler($start); +} + : ^(CATCH ARG_ACTION ACTION) {ruleCatch($ARG_ACTION, (ActionAST)$ACTION);} + ; + +finallyClause +@init { + enterFinallyClause($start); +} +@after { + exitFinallyClause($start); +} + : ^(FINALLY ACTION) {finallyAction((ActionAST)$ACTION);} + ; + +locals +@init { + enterLocals($start); +} +@after { + exitLocals($start); +} + : ^(LOCALS ARG_ACTION) + ; + +ruleReturns +@init { + enterRuleReturns($start); +} +@after { + exitRuleReturns($start); +} + : ^(RETURNS ARG_ACTION) + ; + +throwsSpec +@init { + enterThrowsSpec($start); +} +@after { + exitThrowsSpec($start); +} + : ^(THROWS ID+) + ; + +ruleAction +@init { + enterRuleAction($start); +} +@after { + exitRuleAction($start); +} + : ^(AT ID ACTION) + ; + +ruleModifier +@init { + enterRuleModifier($start); +} +@after { + exitRuleModifier($start); +} + : PUBLIC + | PRIVATE + | PROTECTED + | FRAGMENT + ; + +lexerRuleBlock +@init { + enterLexerRuleBlock($start); +} +@after { + exitLexerRuleBlock($start); +} + : ^( BLOCK + ( { + currentOuterAltRoot = (GrammarAST)input.LT(1); + currentOuterAltNumber++; + } + lexerOuterAlternative + )+ + ) + ; + +ruleBlock +@init { + enterRuleBlock($start); +} +@after { + exitRuleBlock($start); +} + : ^( BLOCK + ( { + currentOuterAltRoot = (GrammarAST)input.LT(1); + currentOuterAltNumber++; + } + outerAlternative + )+ + ) + ; + +lexerOuterAlternative +@init { + enterLexerOuterAlternative((AltAST)$start); + discoverOuterAlt((AltAST)$start); +} +@after { + finishOuterAlt((AltAST)$start); + exitLexerOuterAlternative((AltAST)$start); +} + : lexerAlternative + ; + + +outerAlternative +@init { + enterOuterAlternative((AltAST)$start); + discoverOuterAlt((AltAST)$start); +} +@after { + finishOuterAlt((AltAST)$start); + exitOuterAlternative((AltAST)$start); +} + : alternative + ; + +lexerAlternative +@init { + enterLexerAlternative($start); +} +@after { + exitLexerAlternative($start); +} + : ^(LEXER_ALT_ACTION lexerElements lexerCommand+) + | lexerElements + ; + +lexerElements +@init { + enterLexerElements($start); +} +@after { + exitLexerElements($start); +} + : ^(ALT lexerElement+) + ; + +lexerElement +@init { + enterLexerElement($start); +} +@after { + exitLexerElement($start); +} + : labeledLexerElement + | lexerAtom + | lexerSubrule + | ACTION {actionInAlt((ActionAST)$ACTION);} + | SEMPRED {sempredInAlt((PredAST)$SEMPRED);} + | ^(ACTION elementOptions) {actionInAlt((ActionAST)$ACTION);} + | ^(SEMPRED elementOptions) {sempredInAlt((PredAST)$SEMPRED);} + | EPSILON + ; + +labeledLexerElement +@init { + enterLabeledLexerElement($start); +} +@after { + exitLabeledLexerElement($start); +} + : ^((ASSIGN|PLUS_ASSIGN) ID (lexerAtom|block)) + ; + +lexerBlock +@init { + enterLexerBlock($start); +} +@after { + exitLexerBlock($start); +} + : ^(BLOCK optionsSpec? lexerAlternative+) + ; + +lexerAtom +@init { + enterLexerAtom($start); +} +@after { + exitLexerAtom($start); +} + : terminal + | ^(NOT blockSet) + | blockSet + | ^(WILDCARD elementOptions) + | WILDCARD + | LEXER_CHAR_SET + | range + | ruleref + ; + +actionElement +@init { + enterActionElement($start); +} +@after { + exitActionElement($start); +} + : ACTION + | ^(ACTION elementOptions) + | SEMPRED + | ^(SEMPRED elementOptions) + ; + +alternative +@init { + enterAlternative((AltAST)$start); + discoverAlt((AltAST)$start); +} +@after { + finishAlt((AltAST)$start); + exitAlternative((AltAST)$start); +} + : ^(ALT elementOptions? element+) + | ^(ALT elementOptions? EPSILON) + ; + +lexerCommand +@init { + enterLexerCommand($start); +} +@after { + exitLexerCommand($start); +} + : ^(LEXER_ACTION_CALL ID lexerCommandExpr) + {lexerCallCommand(currentOuterAltNumber, $ID, $lexerCommandExpr.start);} + | ID + {lexerCommand(currentOuterAltNumber, $ID);} + ; + +lexerCommandExpr +@init { + enterLexerCommandExpr($start); +} +@after { + exitLexerCommandExpr($start); +} + : ID + | INT + ; + +element +@init { + enterElement($start); +} +@after { + exitElement($start); +} + : labeledElement + | atom + | subrule + | ACTION {actionInAlt((ActionAST)$ACTION);} + | SEMPRED {sempredInAlt((PredAST)$SEMPRED);} + | ^(ACTION elementOptions) {actionInAlt((ActionAST)$ACTION);} + | ^(SEMPRED elementOptions) {sempredInAlt((PredAST)$SEMPRED);} + + | ^(NOT blockSet) + | ^(NOT block) + ; + +astOperand +@init { + enterAstOperand($start); +} +@after { + exitAstOperand($start); +} + : atom + | ^(NOT blockSet) + | ^(NOT block) + ; + +labeledElement +@init { + enterLabeledElement($start); +} +@after { + exitLabeledElement($start); +} + : ^((ASSIGN|PLUS_ASSIGN) ID element) {label($start, $ID, $element.start);} + ; + +subrule +@init { + enterSubrule($start); +} +@after { + exitSubrule($start); +} + : ^(blockSuffix block) + | block + ; + +lexerSubrule +@init { + enterLexerSubrule($start); +} +@after { + exitLexerSubrule($start); +} + : ^(blockSuffix lexerBlock) + | lexerBlock + ; + +blockSuffix +@init { + enterBlockSuffix($start); +} +@after { + exitBlockSuffix($start); +} + : ebnfSuffix + ; + +ebnfSuffix +@init { + enterEbnfSuffix($start); +} +@after { + exitEbnfSuffix($start); +} + : OPTIONAL + | CLOSURE + | POSITIVE_CLOSURE + ; + +atom +@init { + enterAtom($start); +} +@after { + exitAtom($start); +} + : ^(DOT ID terminal) + | ^(DOT ID ruleref) + | ^(WILDCARD elementOptions) {wildcardRef($WILDCARD);} + | WILDCARD {wildcardRef($WILDCARD);} + | terminal + | blockSet + | ruleref + ; + +blockSet +@init { + enterBlockSet($start); +} +@after { + exitBlockSet($start); +} + : ^(SET setElement+) + ; + +setElement +@init { + enterSetElement($start); +} +@after { + exitSetElement($start); +} + : ^(STRING_LITERAL elementOptions) {stringRef((TerminalAST)$STRING_LITERAL);} + | ^(TOKEN_REF elementOptions) {tokenRef((TerminalAST)$TOKEN_REF);} + | STRING_LITERAL {stringRef((TerminalAST)$STRING_LITERAL);} + | TOKEN_REF {tokenRef((TerminalAST)$TOKEN_REF);} + | ^(RANGE a=STRING_LITERAL b=STRING_LITERAL) + { + stringRef((TerminalAST)$a); + stringRef((TerminalAST)$b); + } + | LEXER_CHAR_SET + ; + +block +@init { + enterBlock($start); +} +@after { + exitBlock($start); +} + : ^(BLOCK optionsSpec? ruleAction* ACTION? alternative+) + ; + +ruleref +@init { + enterRuleref($start); +} +@after { + exitRuleref($start); +} + : ^(RULE_REF arg=ARG_ACTION? elementOptions?) + { + ruleRef($RULE_REF, (ActionAST)$ARG_ACTION); + if ( $arg!=null ) actionInAlt((ActionAST)$arg); + } + ; + +range +@init { + enterRange($start); +} +@after { + exitRange($start); +} + : ^(RANGE STRING_LITERAL STRING_LITERAL) + ; + +terminal +@init { + enterTerminal($start); +} +@after { + exitTerminal($start); +} + : ^(STRING_LITERAL elementOptions) + {stringRef((TerminalAST)$STRING_LITERAL);} + | STRING_LITERAL {stringRef((TerminalAST)$STRING_LITERAL);} + | ^(TOKEN_REF elementOptions) {tokenRef((TerminalAST)$TOKEN_REF);} + | TOKEN_REF {tokenRef((TerminalAST)$TOKEN_REF);} + ; + +elementOptions +@init { + enterElementOptions($start); +} +@after { + exitElementOptions($start); +} + : ^(ELEMENT_OPTIONS elementOption[(GrammarASTWithOptions)$start.getParent()]*) + ; + +elementOption[GrammarASTWithOptions t] +@init { + enterElementOption($start); +} +@after { + exitElementOption($start); +} + : ID {elementOption(t, $ID, null);} + | ^(ASSIGN id=ID v=ID) {elementOption(t, $id, $v);} + | ^(ASSIGN ID v=STRING_LITERAL) {elementOption(t, $ID, $v);} + | ^(ASSIGN ID v=ACTION) {elementOption(t, $ID, $v);} + | ^(ASSIGN ID v=INT) {elementOption(t, $ID, $v);} + ; diff --git a/tool/src/org/antlr/v4/parse/LeftRecursiveRuleWalker.g b/tool/src/org/antlr/v4/parse/LeftRecursiveRuleWalker.g new file mode 100644 index 0000000..774243f --- /dev/null +++ b/tool/src/org/antlr/v4/parse/LeftRecursiveRuleWalker.g @@ -0,0 +1,224 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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. + */ + +/** Find left-recursive rules */ +tree grammar LeftRecursiveRuleWalker; + +options { + tokenVocab=ANTLRParser; + ASTLabelType=GrammarAST; +} + +@header { +package org.antlr.v4.parse; + +import org.antlr.v4.misc.*; +import org.antlr.v4.tool.*; +import org.antlr.v4.tool.ast.*; +} + +@members { +private String ruleName; +private int currentOuterAltNumber; // which outer alt of rule? +public int numAlts; // how many alts for this rule total? + +public void setAltAssoc(AltAST altTree, int alt) {} +public void binaryAlt(AltAST altTree, int alt) {} +public void prefixAlt(AltAST altTree, int alt) {} +public void suffixAlt(AltAST altTree, int alt) {} +public void otherAlt(AltAST altTree, int alt) {} +public void setReturnValues(GrammarAST t) {} +} + +@rulecatch { } + +// TODO: can get parser errors for not matching pattern; make them go away +public +rec_rule returns [boolean isLeftRec] +@init +{ + currentOuterAltNumber = 1; +} + : ^( r=RULE id=RULE_REF {ruleName=$id.getText();} + ruleModifier? +// (ARG_ACTION)? shouldn't allow args, right? + (^(RETURNS a=ARG_ACTION {setReturnValues($a);}))? +// ( ^(THROWS .+) )? don't allow + ( ^(LOCALS ARG_ACTION) )? // TODO: copy these to gen'd code + ( ^(OPTIONS .*) + | ^(AT ID ACTION) // TODO: copy + )* + ruleBlock {$isLeftRec = $ruleBlock.isLeftRec;} + exceptionGroup + ) + ; + +exceptionGroup + : exceptionHandler* finallyClause? + ; + +exceptionHandler + : ^(CATCH ARG_ACTION ACTION) + ; + +finallyClause + : ^(FINALLY ACTION) + ; + +ruleModifier + : PUBLIC + | PRIVATE + | PROTECTED + ; + +ruleBlock returns [boolean isLeftRec] +@init{boolean lr=false; this.numAlts = $start.getChildCount();} + : ^( BLOCK + ( + o=outerAlternative + {if ($o.isLeftRec) $isLeftRec = true;} + {currentOuterAltNumber++;} + )+ + ) + ; + +/** An alt is either prefix, suffix, binary, or ternary operation or "other" */ +outerAlternative returns [boolean isLeftRec] + : (binary)=> binary + {binaryAlt((AltAST)$start, currentOuterAltNumber); $isLeftRec=true;} + | (prefix)=> prefix + {prefixAlt((AltAST)$start, currentOuterAltNumber);} + | (suffix)=> suffix + {suffixAlt((AltAST)$start, currentOuterAltNumber); $isLeftRec=true;} + | nonLeftRecur {otherAlt((AltAST)$start, currentOuterAltNumber);} + ; + +binary + : ^( ALT elementOptions? recurse element* recurse epsilonElement* ) + {setAltAssoc((AltAST)$ALT,currentOuterAltNumber);} + ; + +prefix + : ^( ALT elementOptions? + ({!((CommonTree)input.LT(1)).getText().equals(ruleName)}? element)+ + recurse epsilonElement* + ) + {setAltAssoc((AltAST)$ALT,currentOuterAltNumber);} + ; + +suffix + : ^( ALT elementOptions? recurse element+ ) + {setAltAssoc((AltAST)$ALT,currentOuterAltNumber);} + ; + +nonLeftRecur + : ^(ALT elementOptions? element+) + ; + +recurse + : ^(ASSIGN ID recurseNoLabel) + | ^(PLUS_ASSIGN ID recurseNoLabel) + | recurseNoLabel + ; + +recurseNoLabel : {((CommonTree)input.LT(1)).getText().equals(ruleName)}? RULE_REF; + +token returns [GrammarAST t=null] + : ^(ASSIGN ID s=token {$t = $s.t;}) + | ^(PLUS_ASSIGN ID s=token {$t = $s.t;}) + | b=STRING_LITERAL {$t = $b;} + | ^(b=STRING_LITERAL elementOptions) {$t = $b;} + | ^(c=TOKEN_REF elementOptions) {$t = $c;} + | c=TOKEN_REF {$t = $c;} + ; + +elementOptions + : ^(ELEMENT_OPTIONS elementOption*) + ; + +elementOption + : ID + | ^(ASSIGN ID ID) + | ^(ASSIGN ID STRING_LITERAL) + | ^(ASSIGN ID ACTION) + | ^(ASSIGN ID INT) + ; + +element + : atom + | ^(NOT element) + | ^(RANGE atom atom) + | ^(ASSIGN ID element) + | ^(PLUS_ASSIGN ID element) + | ^(SET setElement+) + | RULE_REF + | ebnf + | epsilonElement + ; + +epsilonElement + : ACTION + | SEMPRED + | EPSILON + | ^(ACTION elementOptions) + | ^(SEMPRED elementOptions) + ; + +setElement + : ^(STRING_LITERAL elementOptions) + | ^(TOKEN_REF elementOptions) + | STRING_LITERAL + | TOKEN_REF + ; + +ebnf: block + | ^( OPTIONAL block ) + | ^( CLOSURE block ) + | ^( POSITIVE_CLOSURE block ) + ; + +block + : ^(BLOCK ACTION? alternative+) + ; + +alternative + : ^(ALT elementOptions? element+) + ; + +atom + : ^(RULE_REF ARG_ACTION? elementOptions?) + | ^(STRING_LITERAL elementOptions) + | STRING_LITERAL + | ^(TOKEN_REF elementOptions) + | TOKEN_REF + | ^(WILDCARD elementOptions) + | WILDCARD + | ^(DOT ID element) + ; diff --git a/tool/src/org/antlr/v4/parse/ResyncToEndOfRuleBlock.java b/tool/src/org/antlr/v4/parse/ResyncToEndOfRuleBlock.java new file mode 100644 index 0000000..aefff83 --- /dev/null +++ b/tool/src/org/antlr/v4/parse/ResyncToEndOfRuleBlock.java @@ -0,0 +1,37 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.parse; + +/** Used to throw us out of deeply nested element back to end of a rule's + * alt list. Note it's not under RecognitionException. + */ +public class ResyncToEndOfRuleBlock extends RuntimeException { +} diff --git a/tool/src/org/antlr/v4/parse/ScopeParser.java b/tool/src/org/antlr/v4/parse/ScopeParser.java new file mode 100644 index 0000000..a0ce182 --- /dev/null +++ b/tool/src/org/antlr/v4/parse/ScopeParser.java @@ -0,0 +1,297 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.parse; + +import org.antlr.runtime.BaseRecognizer; +import org.antlr.runtime.CommonToken; +import org.antlr.v4.runtime.misc.Pair; +import org.antlr.v4.tool.Attribute; +import org.antlr.v4.tool.AttributeDict; +import org.antlr.v4.tool.ErrorType; +import org.antlr.v4.tool.Grammar; +import org.antlr.v4.tool.ast.ActionAST; + +import java.util.ArrayList; +import java.util.List; + +/** Parse args, return values, locals + * + * rule[arg1, arg2, ..., argN] returns [ret1, ..., retN] + * + * text is target language dependent. Java/C#/C/C++ would + * use "int i" but ruby/python would use "i". + */ +public class ScopeParser { + /** Given an arg or retval scope definition list like + * + * Map<String, String>, int[] j3, char *foo32[3] + * + * or + * + * int i=3, j=a[34]+20 + * + * convert to an attribute scope. + */ + public static AttributeDict parseTypedArgList(ActionAST action, String s, Grammar g) { + return parse(action, s, ',', g); + } + + public static AttributeDict parse(ActionAST action, String s, char separator, Grammar g) { + AttributeDict dict = new AttributeDict(); + List<Pair<String, Integer>> decls = splitDecls(s, separator); + for (Pair<String, Integer> decl : decls) { +// System.out.println("decl="+decl); + if ( decl.a.trim().length()>0 ) { + Attribute a = parseAttributeDef(action, decl, g); + dict.add(a); + } + } + return dict; + } + + /** For decls like "String foo" or "char *foo32[]" compute the ID + * and type declarations. Also handle "int x=3" and 'T t = new T("foo")' + * but if the separator is ',' you cannot use ',' in the initvalue + * unless you escape use "\," escape. + */ + public static Attribute parseAttributeDef(ActionAST action, Pair<String, Integer> decl, Grammar g) { + if ( decl.a==null ) return null; + Attribute attr = new Attribute(); + boolean inID = false; + int start = -1; + int rightEdgeOfDeclarator = decl.a.length()-1; + int equalsIndex = decl.a.indexOf('='); + if ( equalsIndex>0 ) { + // everything after the '=' is the init value + attr.initValue = decl.a.substring(equalsIndex+1,decl.a.length()); + rightEdgeOfDeclarator = equalsIndex-1; + } + // walk backwards looking for start of an ID + for (int i=rightEdgeOfDeclarator; i>=0; i--) { + // if we haven't found the end yet, keep going + if ( !inID && Character.isLetterOrDigit(decl.a.charAt(i)) ) { + inID = true; + } + else if ( inID && + !(Character.isLetterOrDigit(decl.a.charAt(i))|| + decl.a.charAt(i)=='_') ) { + start = i+1; + break; + } + } + if ( start<0 && inID ) { + start = 0; + } + if ( start<0 ) { + g.tool.errMgr.grammarError(ErrorType.CANNOT_FIND_ATTRIBUTE_NAME_IN_DECL, g.fileName, action.token, decl); + } + // walk forwards looking for end of an ID + int stop=-1; + for (int i=start; i<=rightEdgeOfDeclarator; i++) { + // if we haven't found the end yet, keep going + if ( !(Character.isLetterOrDigit(decl.a.charAt(i))|| + decl.a.charAt(i)=='_') ) + { + stop = i; + break; + } + if ( i==rightEdgeOfDeclarator ) { + stop = i+1; + } + } + + // the name is the last ID + attr.name = decl.a.substring(start,stop); + + // the type is the decl minus the ID (could be empty) + attr.type = decl.a.substring(0,start); + if ( stop<=rightEdgeOfDeclarator ) { + attr.type += decl.a.substring(stop,rightEdgeOfDeclarator+1); + } + attr.type = attr.type.trim(); + if ( attr.type.length()==0 ) { + attr.type = null; + } + + attr.decl = decl.a; + + if (action != null) { + String actionText = action.getText(); + int[] lines = new int[actionText.length()]; + int[] charPositionInLines = new int[actionText.length()]; + for (int i = 0, line = 0, col = 0; i < actionText.length(); i++, col++) { + lines[i] = line; + charPositionInLines[i] = col; + if (actionText.charAt(i) == '\n') { + line++; + col = -1; + } + } + + int[] charIndexes = new int[actionText.length()]; + for (int i = 0, j = 0; i < actionText.length(); i++, j++) { + charIndexes[j] = i; + if (i < actionText.length() - 1 && actionText.charAt(i) == '/' && actionText.charAt(i + 1) == '/') { + while (i < actionText.length() && actionText.charAt(i) != '\n') { + i++; + } + } + } + + int declOffset = charIndexes[decl.b]; + int declLine = lines[declOffset + start]; + + int line = action.getToken().getLine() + declLine; + int charPositionInLine = charPositionInLines[declOffset + start]; + if (declLine == 0) { + /* offset for the start position of the ARG_ACTION token, plus 1 + * since the ARG_ACTION text had the leading '[' stripped before + * reaching the scope parser. + */ + charPositionInLine += action.getToken().getCharPositionInLine() + 1; + } + + int offset = ((CommonToken)action.getToken()).getStartIndex(); + attr.token = new CommonToken(action.getToken().getInputStream(), ANTLRParser.ID, BaseRecognizer.DEFAULT_TOKEN_CHANNEL, offset + declOffset + start + 1, offset + declOffset + stop); + attr.token.setLine(line); + attr.token.setCharPositionInLine(charPositionInLine); + assert attr.name.equals(attr.token.getText()) : "Attribute text should match the pseudo-token text at this point."; + } + + return attr; + } + + /** Given an argument list like + * + * x, (*a).foo(21,33), 3.2+1, '\n', + * "a,oo\nick", {bl, "fdkj"eck}, ["cat\n,", x, 43] + * + * convert to a list of attributes. Allow nested square brackets etc... + * Set separatorChar to ';' or ',' or whatever you want. + */ + public static List<Pair<String, Integer>> splitDecls(String s, int separatorChar) { + List<Pair<String, Integer>> args = new ArrayList<Pair<String, Integer>>(); + _splitArgumentList(s, 0, -1, separatorChar, args); + return args; + } + + public static int _splitArgumentList(String actionText, + int start, + int targetChar, + int separatorChar, + List<Pair<String, Integer>> args) + { + if ( actionText==null ) { + return -1; + } + + actionText = actionText.replaceAll("//[^\\n]*", ""); + int n = actionText.length(); + //System.out.println("actionText@"+start+"->"+(char)targetChar+"="+actionText.substring(start,n)); + int p = start; + int last = p; + while ( p<n && actionText.charAt(p)!=targetChar ) { + int c = actionText.charAt(p); + switch ( c ) { + case '\'' : + p++; + while ( p<n && actionText.charAt(p)!='\'' ) { + if ( actionText.charAt(p)=='\\' && (p+1)<n && + actionText.charAt(p+1)=='\'' ) + { + p++; // skip escaped quote + } + p++; + } + p++; + break; + case '"' : + p++; + while ( p<n && actionText.charAt(p)!='\"' ) { + if ( actionText.charAt(p)=='\\' && (p+1)<n && + actionText.charAt(p+1)=='\"' ) + { + p++; // skip escaped quote + } + p++; + } + p++; + break; + case '(' : + p = _splitArgumentList(actionText,p+1,')',separatorChar,args); + break; + case '{' : + p = _splitArgumentList(actionText,p+1,'}',separatorChar,args); + break; + case '<' : + if ( actionText.indexOf('>',p+1)>=p ) { + // do we see a matching '>' ahead? if so, hope it's a generic + // and not less followed by expr with greater than + p = _splitArgumentList(actionText,p+1,'>',separatorChar,args); + } + else { + p++; // treat as normal char + } + break; + case '[' : + p = _splitArgumentList(actionText,p+1,']',separatorChar,args); + break; + default : + if ( c==separatorChar && targetChar==-1 ) { + String arg = actionText.substring(last, p); + int index = last; + while (index < p && Character.isWhitespace(actionText.charAt(index))) { + index++; + } + //System.out.println("arg="+arg); + args.add(new Pair<String, Integer>(arg.trim(), index)); + last = p+1; + } + p++; + break; + } + } + if ( targetChar==-1 && p<=n ) { + String arg = actionText.substring(last, p).trim(); + int index = last; + while (index < p && Character.isWhitespace(actionText.charAt(index))) { + index++; + } + //System.out.println("arg="+arg); + if ( arg.length()>0 ) { + args.add(new Pair<String, Integer>(arg.trim(), index)); + } + } + p++; + return p; + } + +} diff --git a/tool/src/org/antlr/v4/parse/TokenVocabParser.java b/tool/src/org/antlr/v4/parse/TokenVocabParser.java new file mode 100644 index 0000000..85398e6 --- /dev/null +++ b/tool/src/org/antlr/v4/parse/TokenVocabParser.java @@ -0,0 +1,175 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.parse; + +import org.antlr.runtime.Token; +import org.antlr.v4.Tool; +import org.antlr.v4.codegen.CodeGenerator; +import org.antlr.v4.tool.ErrorType; +import org.antlr.v4.tool.Grammar; +import org.antlr.v4.tool.ast.GrammarAST; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** */ +public class TokenVocabParser { + protected final Grammar g; + + public TokenVocabParser(Grammar g) { + this.g = g; + } + + /** Load a vocab file {@code <vocabName>.tokens} and return mapping. */ + public Map<String,Integer> load() { + Map<String,Integer> tokens = new LinkedHashMap<String,Integer>(); + int maxTokenType = -1; + File fullFile = getImportedVocabFile(); + FileInputStream fis = null; + BufferedReader br = null; + Tool tool = g.tool; + String vocabName = g.getOptionString("tokenVocab"); + try { + Pattern tokenDefPattern = Pattern.compile("([^\n]+?)[ \\t]*?=[ \\t]*?([0-9]+)"); + fis = new FileInputStream(fullFile); + InputStreamReader isr; + if (tool.grammarEncoding != null) { + isr = new InputStreamReader(fis, tool.grammarEncoding); + } + else { + isr = new InputStreamReader(fis); + } + + br = new BufferedReader(isr); + String tokenDef = br.readLine(); + int lineNum = 1; + while ( tokenDef!=null ) { + Matcher matcher = tokenDefPattern.matcher(tokenDef); + if ( matcher.find() ) { + String tokenID = matcher.group(1); + String tokenTypeS = matcher.group(2); + int tokenType; + try { + tokenType = Integer.valueOf(tokenTypeS); + } + catch (NumberFormatException nfe) { + tool.errMgr.toolError(ErrorType.TOKENS_FILE_SYNTAX_ERROR, + vocabName + CodeGenerator.VOCAB_FILE_EXTENSION, + " bad token type: "+tokenTypeS, + lineNum); + tokenType = Token.INVALID_TOKEN_TYPE; + } + tool.log("grammar", "import "+tokenID+"="+tokenType); + tokens.put(tokenID, tokenType); + maxTokenType = Math.max(maxTokenType,tokenType); + lineNum++; + } + else { + if ( tokenDef.length()>0 ) { // ignore blank lines + tool.errMgr.toolError(ErrorType.TOKENS_FILE_SYNTAX_ERROR, + vocabName + CodeGenerator.VOCAB_FILE_EXTENSION, + " bad token def: " + tokenDef, + lineNum); + } + } + tokenDef = br.readLine(); + } + } + catch (FileNotFoundException fnfe) { + GrammarAST inTree = g.ast.getOptionAST("tokenVocab"); + String inTreeValue = inTree.getToken().getText(); + if ( vocabName.equals(inTreeValue) ) { + tool.errMgr.grammarError(ErrorType.CANNOT_FIND_TOKENS_FILE_REFD_IN_GRAMMAR, + g.fileName, + inTree.getToken(), + fullFile); + } + else { // must be from -D option on cmd-line not token in tree + tool.errMgr.toolError(ErrorType.CANNOT_FIND_TOKENS_FILE_GIVEN_ON_CMDLINE, + fullFile, + g.name); + } + } + catch (Exception e) { + tool.errMgr.toolError(ErrorType.ERROR_READING_TOKENS_FILE, + e, + fullFile, + e.getMessage()); + } + finally { + try { + if ( br!=null ) br.close(); + } + catch (IOException ioe) { + tool.errMgr.toolError(ErrorType.ERROR_READING_TOKENS_FILE, + ioe, + fullFile, + ioe.getMessage()); + } + } + return tokens; + } + + /** Return a File descriptor for vocab file. Look in library or + * in -o output path. antlr -o foo T.g4 U.g4 where U needs T.tokens + * won't work unless we look in foo too. If we do not find the + * file in the lib directory then must assume that the .tokens file + * is going to be generated as part of this build and we have defined + * .tokens files so that they ALWAYS are generated in the base output + * directory, which means the current directory for the command line tool if there + * was no output directory specified. + */ + public File getImportedVocabFile() { + String vocabName = g.getOptionString("tokenVocab"); + File f = new File(g.tool.libDirectory, + File.separator + + vocabName + + CodeGenerator.VOCAB_FILE_EXTENSION); + if (f.exists()) { + return f; + } + + // We did not find the vocab file in the lib directory, so we need + // to look for it in the output directory which is where .tokens + // files are generated (in the base, not relative to the input + // location.) + f = new File(g.tool.outputDirectory, vocabName + CodeGenerator.VOCAB_FILE_EXTENSION); + return f; + } +} diff --git a/tool/src/org/antlr/v4/parse/ToolANTLRLexer.java b/tool/src/org/antlr/v4/parse/ToolANTLRLexer.java new file mode 100644 index 0000000..625fdd7 --- /dev/null +++ b/tool/src/org/antlr/v4/parse/ToolANTLRLexer.java @@ -0,0 +1,57 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.parse; + +import org.antlr.runtime.CharStream; +import org.antlr.runtime.RecognitionException; +import org.antlr.runtime.Token; +import org.antlr.v4.Tool; +import org.antlr.v4.tool.ErrorType; + +public class ToolANTLRLexer extends ANTLRLexer { + public Tool tool; + + public ToolANTLRLexer(CharStream input, Tool tool) { + super(input); + this.tool = tool; + } + + @Override + public void displayRecognitionError(String[] tokenNames, RecognitionException e) { + String msg = getErrorMessage(e, tokenNames); + tool.errMgr.syntaxError(ErrorType.SYNTAX_ERROR, getSourceName(), e.token, e, msg); + } + + @Override + public void grammarError(ErrorType etype, Token token, Object... args) { + tool.errMgr.grammarError(etype, getSourceName(), token, args); + } +} diff --git a/tool/src/org/antlr/v4/parse/ToolANTLRParser.java b/tool/src/org/antlr/v4/parse/ToolANTLRParser.java new file mode 100644 index 0000000..168b0f8 --- /dev/null +++ b/tool/src/org/antlr/v4/parse/ToolANTLRParser.java @@ -0,0 +1,84 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.parse; + +import org.antlr.runtime.NoViableAltException; +import org.antlr.runtime.Parser; +import org.antlr.runtime.RecognitionException; +import org.antlr.runtime.TokenStream; +import org.antlr.v4.Tool; +import org.antlr.v4.tool.ErrorType; + +/** Override error handling for use with ANTLR tool itself; leaves + * nothing in grammar associated with Tool so others can use in IDEs, ... + */ +public class ToolANTLRParser extends ANTLRParser { + public Tool tool; + + public ToolANTLRParser(TokenStream input, Tool tool) { + super(input); + this.tool = tool; + } + + @Override + public void displayRecognitionError(String[] tokenNames, + RecognitionException e) + { + String msg = getParserErrorMessage(this, e); + if ( !paraphrases.isEmpty() ) { + String paraphrase = paraphrases.peek(); + msg = msg+" while "+paraphrase; + } + // List stack = getRuleInvocationStack(e, this.getClass().getName()); + // msg += ", rule stack = "+stack; + tool.errMgr.syntaxError(ErrorType.SYNTAX_ERROR, getSourceName(), e.token, e, msg); + } + + public String getParserErrorMessage(Parser parser, RecognitionException e) { + String msg; + if ( e instanceof NoViableAltException) { + String name = parser.getTokenErrorDisplay(e.token); + msg = name+" came as a complete surprise to me"; + } + else if ( e instanceof v4ParserException) { + msg = ((v4ParserException)e).msg; + } + else { + msg = parser.getErrorMessage(e, parser.getTokenNames()); + } + return msg; + } + + @Override + public void grammarError(ErrorType etype, org.antlr.runtime.Token token, Object... args) { + tool.errMgr.grammarError(etype, getSourceName(), token, args); + } +} diff --git a/tool/src/org/antlr/v4/parse/v3TreeGrammarException.java b/tool/src/org/antlr/v4/parse/v3TreeGrammarException.java new file mode 100644 index 0000000..c1fabfe --- /dev/null +++ b/tool/src/org/antlr/v4/parse/v3TreeGrammarException.java @@ -0,0 +1,42 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.parse; + +import org.antlr.runtime.Token; +import org.antlr.v4.runtime.misc.ParseCancellationException; + +public class v3TreeGrammarException extends ParseCancellationException { + public Token location; + + public v3TreeGrammarException(Token location) { + this.location = location; + } +} diff --git a/tool/src/org/antlr/v4/parse/v4ParserException.java b/tool/src/org/antlr/v4/parse/v4ParserException.java new file mode 100644 index 0000000..80cbb75 --- /dev/null +++ b/tool/src/org/antlr/v4/parse/v4ParserException.java @@ -0,0 +1,47 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.parse; + +import org.antlr.runtime.IntStream; +import org.antlr.runtime.RecognitionException; + +/** */ +public class v4ParserException extends RecognitionException { + public String msg; + /** Used for remote debugger deserialization */ + public v4ParserException() {} + + public v4ParserException(String msg, IntStream input) { + super(input); + this.msg = msg; + } + +} diff --git a/tool/src/org/antlr/v4/semantics/ActionSniffer.java b/tool/src/org/antlr/v4/semantics/ActionSniffer.java new file mode 100644 index 0000000..2d48e25 --- /dev/null +++ b/tool/src/org/antlr/v4/semantics/ActionSniffer.java @@ -0,0 +1,113 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.semantics; + +import org.antlr.runtime.ANTLRStringStream; +import org.antlr.runtime.Token; +import org.antlr.v4.parse.ActionSplitter; +import org.antlr.v4.tool.Alternative; +import org.antlr.v4.tool.ErrorManager; +import org.antlr.v4.tool.Grammar; +import org.antlr.v4.tool.Rule; +import org.antlr.v4.tool.ast.ActionAST; +import org.antlr.v4.tool.ast.GrammarAST; +import org.antlr.v4.tool.ast.TerminalAST; + +import java.util.List; + +/** Find token and rule refs plus refs to them in actions; + * side-effect: update Alternatives + */ +public class ActionSniffer extends BlankActionSplitterListener { + public Grammar g; + public Rule r; // null if action outside of rule + public Alternative alt; // null if action outside of alt; could be in rule + public ActionAST node; + public Token actionToken; // token within action + public ErrorManager errMgr; + + public ActionSniffer(Grammar g, Rule r, Alternative alt, ActionAST node, Token actionToken) { + this.g = g; + this.r = r; + this.alt = alt; + this.node = node; + this.actionToken = actionToken; + this.errMgr = g.tool.errMgr; + } + + public void examineAction() { + //System.out.println("examine "+actionToken); + ANTLRStringStream in = new ANTLRStringStream(actionToken.getText()); + in.setLine(actionToken.getLine()); + in.setCharPositionInLine(actionToken.getCharPositionInLine()); + ActionSplitter splitter = new ActionSplitter(in, this); + // forces eval, triggers listener methods + node.chunks = splitter.getActionTokens(); + } + + public void processNested(Token actionToken) { + ANTLRStringStream in = new ANTLRStringStream(actionToken.getText()); + in.setLine(actionToken.getLine()); + in.setCharPositionInLine(actionToken.getCharPositionInLine()); + ActionSplitter splitter = new ActionSplitter(in, this); + // forces eval, triggers listener methods + splitter.getActionTokens(); + } + + + @Override + public void attr(String expr, Token x) { trackRef(x); } + + @Override + public void qualifiedAttr(String expr, Token x, Token y) { trackRef(x); } + + @Override + public void setAttr(String expr, Token x, Token rhs) { + trackRef(x); + processNested(rhs); + } + + @Override + public void setNonLocalAttr(String expr, Token x, Token y, Token rhs) { + processNested(rhs); + } + + public void trackRef(Token x) { + List<TerminalAST> xRefs = alt.tokenRefs.get(x.getText()); + if ( xRefs!=null ) { + alt.tokenRefsInActions.map(x.getText(), node); + } + List<GrammarAST> rRefs = alt.ruleRefs.get(x.getText()); + if ( rRefs!=null ) { + alt.ruleRefsInActions.map(x.getText(), node); + } + } +} diff --git a/tool/src/org/antlr/v4/semantics/AttributeChecks.java b/tool/src/org/antlr/v4/semantics/AttributeChecks.java new file mode 100644 index 0000000..f702185 --- /dev/null +++ b/tool/src/org/antlr/v4/semantics/AttributeChecks.java @@ -0,0 +1,259 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.semantics; + +import org.antlr.runtime.ANTLRStringStream; +import org.antlr.runtime.Token; +import org.antlr.v4.parse.ActionSplitter; +import org.antlr.v4.parse.ActionSplitterListener; +import org.antlr.v4.tool.Alternative; +import org.antlr.v4.tool.ErrorManager; +import org.antlr.v4.tool.ErrorType; +import org.antlr.v4.tool.Grammar; +import org.antlr.v4.tool.LabelElementPair; +import org.antlr.v4.tool.LabelType; +import org.antlr.v4.tool.Rule; +import org.antlr.v4.tool.ast.ActionAST; +import org.antlr.v4.tool.ast.GrammarAST; + +import java.util.List; + +/** Trigger checks for various kinds of attribute expressions. + * no side-effects. + */ +public class AttributeChecks implements ActionSplitterListener { + public Grammar g; + public Rule r; // null if action outside of rule + public Alternative alt; // null if action outside of alt; could be in rule + public ActionAST node; + public Token actionToken; // token within action + public ErrorManager errMgr; + + public AttributeChecks(Grammar g, Rule r, Alternative alt, ActionAST node, Token actionToken) { + this.g = g; + this.r = r; + this.alt = alt; + this.node = node; + this.actionToken = actionToken; + this.errMgr = g.tool.errMgr; + } + + public static void checkAllAttributeExpressions(Grammar g) { + for (ActionAST act : g.namedActions.values()) { + AttributeChecks checker = new AttributeChecks(g, null, null, act, act.token); + checker.examineAction(); + } + + for (Rule r : g.rules.values()) { + for (ActionAST a : r.namedActions.values()) { + AttributeChecks checker = new AttributeChecks(g, r, null, a, a.token); + checker.examineAction(); + } + for (int i=1; i<=r.numberOfAlts; i++) { + Alternative alt = r.alt[i]; + for (ActionAST a : alt.actions) { + AttributeChecks checker = + new AttributeChecks(g, r, alt, a, a.token); + checker.examineAction(); + } + } + for (GrammarAST e : r.exceptions) { + ActionAST a = (ActionAST)e.getChild(1); + AttributeChecks checker = new AttributeChecks(g, r, null, a, a.token); + checker.examineAction(); + } + if ( r.finallyAction!=null ) { + AttributeChecks checker = + new AttributeChecks(g, r, null, r.finallyAction, r.finallyAction.token); + checker.examineAction(); + } + } + } + + public void examineAction() { + //System.out.println("examine "+actionToken); + ANTLRStringStream in = new ANTLRStringStream(actionToken.getText()); + in.setLine(actionToken.getLine()); + in.setCharPositionInLine(actionToken.getCharPositionInLine()); + ActionSplitter splitter = new ActionSplitter(in, this); + // forces eval, triggers listener methods + node.chunks = splitter.getActionTokens(); + } + + // LISTENER METHODS + + // $x.y + @Override + public void qualifiedAttr(String expr, Token x, Token y) { + if ( g.isLexer() ) { + errMgr.grammarError(ErrorType.ATTRIBUTE_IN_LEXER_ACTION, + g.fileName, x, x.getText()+"."+y.getText(), expr); + return; + } + if ( node.resolver.resolveToAttribute(x.getText(), node)!=null ) { + // must be a member access to a predefined attribute like $ctx.foo + attr(expr, x); + return; + } + + if ( node.resolver.resolveToAttribute(x.getText(), y.getText(), node)==null ) { + Rule rref = isolatedRuleRef(x.getText()); + if ( rref!=null ) { + if ( rref.args!=null && rref.args.get(y.getText())!=null ) { + g.tool.errMgr.grammarError(ErrorType.INVALID_RULE_PARAMETER_REF, + g.fileName, y, y.getText(), rref.name, expr); + } + else { + errMgr.grammarError(ErrorType.UNKNOWN_RULE_ATTRIBUTE, + g.fileName, y, y.getText(), rref.name, expr); + } + } + else if ( !node.resolver.resolvesToAttributeDict(x.getText(), node) ) { + errMgr.grammarError(ErrorType.UNKNOWN_SIMPLE_ATTRIBUTE, + g.fileName, x, x.getText(), expr); + } + else { + errMgr.grammarError(ErrorType.UNKNOWN_ATTRIBUTE_IN_SCOPE, + g.fileName, y, y.getText(), expr); + } + } + } + + @Override + public void setAttr(String expr, Token x, Token rhs) { + if ( g.isLexer() ) { + errMgr.grammarError(ErrorType.ATTRIBUTE_IN_LEXER_ACTION, + g.fileName, x, x.getText(), expr); + return; + } + if ( node.resolver.resolveToAttribute(x.getText(), node)==null ) { + ErrorType errorType = ErrorType.UNKNOWN_SIMPLE_ATTRIBUTE; + if ( node.resolver.resolvesToListLabel(x.getText(), node) ) { + // $ids for ids+=ID etc... + errorType = ErrorType.ASSIGNMENT_TO_LIST_LABEL; + } + + errMgr.grammarError(errorType, + g.fileName, x, x.getText(), expr); + } + new AttributeChecks(g, r, alt, node, rhs).examineAction(); + } + + @Override + public void attr(String expr, Token x) { + if ( g.isLexer() ) { + errMgr.grammarError(ErrorType.ATTRIBUTE_IN_LEXER_ACTION, + g.fileName, x, x.getText(), expr); + return; + } + if ( node.resolver.resolveToAttribute(x.getText(), node)==null ) { + if ( node.resolver.resolvesToToken(x.getText(), node) ) { + return; // $ID for token ref or label of token + } + if ( node.resolver.resolvesToListLabel(x.getText(), node) ) { + return; // $ids for ids+=ID etc... + } + if ( isolatedRuleRef(x.getText())!=null ) { + errMgr.grammarError(ErrorType.ISOLATED_RULE_REF, + g.fileName, x, x.getText(), expr); + return; + } + errMgr.grammarError(ErrorType.UNKNOWN_SIMPLE_ATTRIBUTE, + g.fileName, x, x.getText(), expr); + } + } + + @Override + public void nonLocalAttr(String expr, Token x, Token y) { + Rule r = g.getRule(x.getText()); + if ( r==null ) { + errMgr.grammarError(ErrorType.UNDEFINED_RULE_IN_NONLOCAL_REF, + g.fileName, x, x.getText(), y.getText(), expr); + } + else if ( r.resolveToAttribute(y.getText(), null)==null ) { + errMgr.grammarError(ErrorType.UNKNOWN_RULE_ATTRIBUTE, + g.fileName, y, y.getText(), x.getText(), expr); + + } + } + + @Override + public void setNonLocalAttr(String expr, Token x, Token y, Token rhs) { + Rule r = g.getRule(x.getText()); + if ( r==null ) { + errMgr.grammarError(ErrorType.UNDEFINED_RULE_IN_NONLOCAL_REF, + g.fileName, x, x.getText(), y.getText(), expr); + } + else if ( r.resolveToAttribute(y.getText(), null)==null ) { + errMgr.grammarError(ErrorType.UNKNOWN_RULE_ATTRIBUTE, + g.fileName, y, y.getText(), x.getText(), expr); + + } + } + + @Override + public void text(String text) { } + + // don't care + public void templateInstance(String expr) { } + public void indirectTemplateInstance(String expr) { } + public void setExprAttribute(String expr) { } + public void setSTAttribute(String expr) { } + public void templateExpr(String expr) { } + + // SUPPORT + + public Rule isolatedRuleRef(String x) { + if ( node.resolver instanceof Grammar ) return null; + + if ( x.equals(r.name) ) return r; + List<LabelElementPair> labels = null; + if ( node.resolver instanceof Rule ) { + labels = r.getElementLabelDefs().get(x); + } + else if ( node.resolver instanceof Alternative ) { + labels = ((Alternative)node.resolver).labelDefs.get(x); + } + if ( labels!=null ) { // it's a label ref. is it a rule label? + LabelElementPair anyLabelDef = labels.get(0); + if ( anyLabelDef.type==LabelType.RULE_LABEL ) { + return g.getRule(anyLabelDef.element.getText()); + } + } + if ( node.resolver instanceof Alternative ) { + if ( ((Alternative)node.resolver).ruleRefs.get(x)!=null ) { + return g.getRule(x); + } + } + return null; + } + +} diff --git a/tool/src/org/antlr/v4/semantics/BasicSemanticChecks.java b/tool/src/org/antlr/v4/semantics/BasicSemanticChecks.java new file mode 100644 index 0000000..a578a66 --- /dev/null +++ b/tool/src/org/antlr/v4/semantics/BasicSemanticChecks.java @@ -0,0 +1,637 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.semantics; + +import org.antlr.runtime.Token; +import org.antlr.runtime.tree.CommonTree; +import org.antlr.runtime.tree.Tree; +import org.antlr.v4.misc.Utils; +import org.antlr.v4.parse.ANTLRParser; +import org.antlr.v4.parse.GrammarTreeVisitor; +import org.antlr.v4.tool.ErrorManager; +import org.antlr.v4.tool.ErrorType; +import org.antlr.v4.tool.Grammar; +import org.antlr.v4.tool.Rule; +import org.antlr.v4.tool.ast.ActionAST; +import org.antlr.v4.tool.ast.AltAST; +import org.antlr.v4.tool.ast.BlockAST; +import org.antlr.v4.tool.ast.GrammarAST; +import org.antlr.v4.tool.ast.GrammarASTWithOptions; +import org.antlr.v4.tool.ast.GrammarRootAST; +import org.antlr.v4.tool.ast.RuleAST; +import org.antlr.v4.tool.ast.RuleRefAST; +import org.antlr.v4.tool.ast.TerminalAST; +import org.stringtemplate.v4.misc.MultiMap; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** No side-effects except for setting options into the appropriate node. + * TODO: make the side effects into a separate pass this + * + * Invokes check rules for these: + * + * FILE_AND_GRAMMAR_NAME_DIFFER + * LEXER_RULES_NOT_ALLOWED + * PARSER_RULES_NOT_ALLOWED + * CANNOT_ALIAS_TOKENS + * ARGS_ON_TOKEN_REF + * ILLEGAL_OPTION + * REWRITE_OR_OP_WITH_NO_OUTPUT_OPTION + * NO_RULES + * REWRITE_FOR_MULTI_ELEMENT_ALT + * HETERO_ILLEGAL_IN_REWRITE_ALT + * AST_OP_WITH_NON_AST_OUTPUT_OPTION + * AST_OP_IN_ALT_WITH_REWRITE + * CONFLICTING_OPTION_IN_TREE_FILTER + * WILDCARD_AS_ROOT + * INVALID_IMPORT + * TOKEN_VOCAB_IN_DELEGATE + * IMPORT_NAME_CLASH + * REPEATED_PREQUEL + * TOKEN_NAMES_MUST_START_UPPER + */ +public class BasicSemanticChecks extends GrammarTreeVisitor { + /** Set of valid imports. Maps delegate to set of delegator grammar types. + * validDelegations.get(LEXER) gives list of the kinds of delegators + * that can import lexers. + */ + public static MultiMap<Integer,Integer> validImportTypes = + new MultiMap<Integer,Integer>() { + { + map(ANTLRParser.LEXER, ANTLRParser.LEXER); + map(ANTLRParser.LEXER, ANTLRParser.COMBINED); + + map(ANTLRParser.PARSER, ANTLRParser.PARSER); + map(ANTLRParser.PARSER, ANTLRParser.COMBINED); + + map(ANTLRParser.COMBINED, ANTLRParser.COMBINED); + } + }; + + public Grammar g; + public RuleCollector ruleCollector; + public ErrorManager errMgr; + + /** + * When this is {@code true}, the semantic checks will report + * {@link ErrorType#UNRECOGNIZED_ASSOC_OPTION} where appropriate. This may + * be set to {@code false} to disable this specific check. + * + * <p>The default value is {@code true}.</p> + */ + public boolean checkAssocElementOption = true; + + /** + * This field is used for reporting the {@link ErrorType#MODE_WITHOUT_RULES} + * error when necessary. + */ + protected int nonFragmentRuleCount; + + /** + * This is {@code true} from the time {@link #discoverLexerRule} is called + * for a lexer rule with the {@code fragment} modifier until + * {@link #exitLexerRule} is called. + */ + private boolean inFragmentRule; + + public BasicSemanticChecks(Grammar g, RuleCollector ruleCollector) { + this.g = g; + this.ruleCollector = ruleCollector; + this.errMgr = g.tool.errMgr; + } + + @Override + public ErrorManager getErrorManager() { return errMgr; } + + public void process() { visitGrammar(g.ast); } + + // Routines to route visitor traffic to the checking routines + + @Override + public void discoverGrammar(GrammarRootAST root, GrammarAST ID) { + checkGrammarName(ID.token); + } + + @Override + public void finishPrequels(GrammarAST firstPrequel) { + if ( firstPrequel==null ) return; + GrammarAST parent = (GrammarAST)firstPrequel.parent; + List<GrammarAST> options = parent.getAllChildrenWithType(OPTIONS); + List<GrammarAST> imports = parent.getAllChildrenWithType(IMPORT); + List<GrammarAST> tokens = parent.getAllChildrenWithType(TOKENS_SPEC); + checkNumPrequels(options, imports, tokens); + } + + @Override + public void importGrammar(GrammarAST label, GrammarAST ID) { + checkImport(ID.token); + } + + @Override + public void discoverRules(GrammarAST rules) { + checkNumRules(rules); + } + + @Override + protected void enterMode(GrammarAST tree) { + nonFragmentRuleCount = 0; + } + + @Override + protected void exitMode(GrammarAST tree) { + if (nonFragmentRuleCount == 0) { + Token token = tree.getToken(); + String name = "?"; + if (tree.getChildCount() > 0) { + name = tree.getChild(0).getText(); + if (name == null || name.isEmpty()) { + name = "?"; + } + + token = ((GrammarAST)tree.getChild(0)).getToken(); + } + + g.tool.errMgr.grammarError(ErrorType.MODE_WITHOUT_RULES, g.fileName, token, name, g); + } + } + + @Override + public void modeDef(GrammarAST m, GrammarAST ID) { + if ( !g.isLexer() ) { + g.tool.errMgr.grammarError(ErrorType.MODE_NOT_IN_LEXER, g.fileName, + ID.token, ID.token.getText(), g); + } + } + + @Override + public void discoverRule(RuleAST rule, GrammarAST ID, + List<GrammarAST> modifiers, + ActionAST arg, ActionAST returns, + GrammarAST thrws, GrammarAST options, + ActionAST locals, + List<GrammarAST> actions, GrammarAST block) + { + // TODO: chk that all or no alts have "# label" + checkInvalidRuleDef(ID.token); + } + + @Override + public void discoverLexerRule(RuleAST rule, GrammarAST ID, List<GrammarAST> modifiers, + GrammarAST block) + { + checkInvalidRuleDef(ID.token); + + if (modifiers != null) { + for (GrammarAST tree : modifiers) { + if (tree.getType() == ANTLRParser.FRAGMENT) { + inFragmentRule = true; + } + } + } + + if (!inFragmentRule) { + nonFragmentRuleCount++; + } + } + + @Override + protected void exitLexerRule(GrammarAST tree) { + inFragmentRule = false; + } + + @Override + public void ruleRef(GrammarAST ref, ActionAST arg) { + checkInvalidRuleRef(ref.token); + } + + @Override + public void ruleOption(GrammarAST ID, GrammarAST valueAST) { + checkOptions((GrammarAST)ID.getAncestor(RULE), ID.token, valueAST); + } + + @Override + public void blockOption(GrammarAST ID, GrammarAST valueAST) { + checkOptions((GrammarAST)ID.getAncestor(BLOCK), ID.token, valueAST); + } + + @Override + public void grammarOption(GrammarAST ID, GrammarAST valueAST) { + boolean ok = checkOptions(g.ast, ID.token, valueAST); + //if ( ok ) g.ast.setOption(ID.getText(), value); + } + + @Override + public void defineToken(GrammarAST ID) { + checkTokenDefinition(ID.token); + } + + @Override + protected void enterChannelsSpec(GrammarAST tree) { + if (g.isParser()) { + g.tool.errMgr.grammarError(ErrorType.CHANNELS_BLOCK_IN_PARSER_GRAMMAR, g.fileName, tree.token); + } + else if (g.isCombined()) { + g.tool.errMgr.grammarError(ErrorType.CHANNELS_BLOCK_IN_COMBINED_GRAMMAR, g.fileName, tree.token); + } + } + + @Override + public void defineChannel(GrammarAST ID) { + checkChannelDefinition(ID.token); + } + + @Override + public void elementOption(GrammarASTWithOptions elem, GrammarAST ID, GrammarAST valueAST) { + String v = null; + boolean ok = checkElementOptions(elem, ID, valueAST); +// if ( ok ) { +// if ( v!=null ) { +// t.setOption(ID.getText(), v); +// } +// else { +// t.setOption(TerminalAST.defaultTokenOption, v); +// } +// } + } + + @Override + public void finishRule(RuleAST rule, GrammarAST ID, GrammarAST block) { + if ( rule.isLexerRule() ) return; + BlockAST blk = (BlockAST)rule.getFirstChildWithType(BLOCK); + int nalts = blk.getChildCount(); + GrammarAST idAST = (GrammarAST)rule.getChild(0); + for (int i=0; i< nalts; i++) { + AltAST altAST = (AltAST)blk.getChild(i); + if ( altAST.altLabel!=null ) { + String altLabel = altAST.altLabel.getText(); + // first check that label doesn't conflict with a rule + // label X or x can't be rule x. + Rule r = ruleCollector.rules.get(Utils.decapitalize(altLabel)); + if ( r!=null ) { + g.tool.errMgr.grammarError(ErrorType.ALT_LABEL_CONFLICTS_WITH_RULE, + g.fileName, altAST.altLabel.token, + altLabel, + r.name); + } + // Now verify that label X or x doesn't conflict with label + // in another rule. altLabelToRuleName has both X and x mapped. + String prevRuleForLabel = ruleCollector.altLabelToRuleName.get(altLabel); + if ( prevRuleForLabel!=null && !prevRuleForLabel.equals(rule.getRuleName()) ) { + g.tool.errMgr.grammarError(ErrorType.ALT_LABEL_REDEF, + g.fileName, altAST.altLabel.token, + altLabel, + rule.getRuleName(), + prevRuleForLabel); + } + } + } + List<GrammarAST> altLabels = ruleCollector.ruleToAltLabels.get(rule.getRuleName()); + int numAltLabels = 0; + if ( altLabels!=null ) numAltLabels = altLabels.size(); + if ( numAltLabels>0 && nalts != numAltLabels ) { + g.tool.errMgr.grammarError(ErrorType.RULE_WITH_TOO_FEW_ALT_LABELS, + g.fileName, idAST.token, rule.getRuleName()); + } + } + + // Routines to do the actual work of checking issues with a grammar. + // They are triggered by the visitor methods above. + + void checkGrammarName(Token nameToken) { + String fullyQualifiedName = nameToken.getInputStream().getSourceName(); + if (fullyQualifiedName == null) { + // This wasn't read from a file. + return; + } + + File f = new File(fullyQualifiedName); + String fileName = f.getName(); + if ( g.originalGrammar!=null ) return; // don't warn about diff if this is implicit lexer + if ( !Utils.stripFileExtension(fileName).equals(nameToken.getText()) && + !fileName.equals(Grammar.GRAMMAR_FROM_STRING_NAME)) { + g.tool.errMgr.grammarError(ErrorType.FILE_AND_GRAMMAR_NAME_DIFFER, + fileName, nameToken, nameToken.getText(), fileName); + } + } + + void checkNumRules(GrammarAST rulesNode) { + if ( rulesNode.getChildCount()==0 ) { + GrammarAST root = (GrammarAST)rulesNode.getParent(); + GrammarAST IDNode = (GrammarAST)root.getChild(0); + g.tool.errMgr.grammarError(ErrorType.NO_RULES, g.fileName, + null, IDNode.getText(), g); + } + } + + void checkNumPrequels(List<GrammarAST> options, + List<GrammarAST> imports, + List<GrammarAST> tokens) + { + List<Token> secondOptionTokens = new ArrayList<Token>(); + if ( options!=null && options.size()>1 ) { + secondOptionTokens.add(options.get(1).token); + } + if ( imports!=null && imports.size()>1 ) { + secondOptionTokens.add(imports.get(1).token); + } + if ( tokens!=null && tokens.size()>1 ) { + secondOptionTokens.add(tokens.get(1).token); + } + for (Token t : secondOptionTokens) { + String fileName = t.getInputStream().getSourceName(); + g.tool.errMgr.grammarError(ErrorType.REPEATED_PREQUEL, + fileName, t); + } + } + + void checkInvalidRuleDef(Token ruleID) { + String fileName = null; + if ( ruleID.getInputStream()!=null ) { + fileName = ruleID.getInputStream().getSourceName(); + } + if ( g.isLexer() && Character.isLowerCase(ruleID.getText().charAt(0)) ) { + g.tool.errMgr.grammarError(ErrorType.PARSER_RULES_NOT_ALLOWED, + fileName, ruleID, ruleID.getText()); + } + if ( g.isParser() && + Grammar.isTokenName(ruleID.getText()) ) + { + g.tool.errMgr.grammarError(ErrorType.LEXER_RULES_NOT_ALLOWED, + fileName, ruleID, ruleID.getText()); + } + } + + void checkInvalidRuleRef(Token ruleID) { + String fileName = ruleID.getInputStream().getSourceName(); + if ( g.isLexer() && Character.isLowerCase(ruleID.getText().charAt(0)) ) { + g.tool.errMgr.grammarError(ErrorType.PARSER_RULE_REF_IN_LEXER_RULE, + fileName, ruleID, ruleID.getText(), currentRuleName); + } + } + + void checkTokenDefinition(Token tokenID) { + String fileName = tokenID.getInputStream().getSourceName(); + if ( !Grammar.isTokenName(tokenID.getText()) ) { + g.tool.errMgr.grammarError(ErrorType.TOKEN_NAMES_MUST_START_UPPER, + fileName, + tokenID, + tokenID.getText()); + } + } + + void checkChannelDefinition(Token tokenID) { + } + + @Override + protected void enterLexerElement(GrammarAST tree) { + } + + @Override + protected void enterLexerCommand(GrammarAST tree) { + checkElementIsOuterMostInSingleAlt(tree); + + if (inFragmentRule) { + String fileName = tree.token.getInputStream().getSourceName(); + String ruleName = currentRuleName; + g.tool.errMgr.grammarError(ErrorType.FRAGMENT_ACTION_IGNORED, fileName, tree.token, ruleName); + } + } + + @Override + public void actionInAlt(ActionAST action) { + if (inFragmentRule) { + String fileName = action.token.getInputStream().getSourceName(); + String ruleName = currentRuleName; + g.tool.errMgr.grammarError(ErrorType.FRAGMENT_ACTION_IGNORED, fileName, action.token, ruleName); + } + } + + /** + Make sure that action is last element in outer alt; here action, + a2, z, and zz are bad, but a3 is ok: + (RULE A (BLOCK (ALT {action} 'a'))) + (RULE B (BLOCK (ALT (BLOCK (ALT {a2} 'x') (ALT 'y')) {a3}))) + (RULE C (BLOCK (ALT 'd' {z}) (ALT 'e' {zz}))) + */ + protected void checkElementIsOuterMostInSingleAlt(GrammarAST tree) { + CommonTree alt = tree.parent; + CommonTree blk = alt.parent; + boolean outerMostAlt = blk.parent.getType() == RULE; + Tree rule = tree.getAncestor(RULE); + String fileName = tree.getToken().getInputStream().getSourceName(); + if ( !outerMostAlt || blk.getChildCount()>1 ) + { + ErrorType e = ErrorType.LEXER_COMMAND_PLACEMENT_ISSUE; + g.tool.errMgr.grammarError(e, + fileName, + tree.getToken(), + rule.getChild(0).getText()); + + } + } + + @Override + public void label(GrammarAST op, GrammarAST ID, GrammarAST element) { + switch (element.getType()) { + // token atoms + case TOKEN_REF: + case STRING_LITERAL: + case RANGE: + // token sets + case SET: + case NOT: + // rule atoms + case RULE_REF: + case WILDCARD: + return; + + default: + String fileName = ID.token.getInputStream().getSourceName(); + g.tool.errMgr.grammarError(ErrorType.LABEL_BLOCK_NOT_A_SET, fileName, ID.token, ID.getText()); + break; + } + } + + @Override + protected void enterLabeledLexerElement(GrammarAST tree) { + Token label = ((GrammarAST)tree.getChild(0)).getToken(); + g.tool.errMgr.grammarError(ErrorType.V3_LEXER_LABEL, + g.fileName, + label, + label.getText()); + } + + /** Check option is appropriate for grammar, rule, subrule */ + boolean checkOptions(GrammarAST parent, + Token optionID, + GrammarAST valueAST) + { + boolean ok = true; + if ( parent.getType()==ANTLRParser.BLOCK ) { + if ( g.isLexer() && !Grammar.LexerBlockOptions.contains(optionID.getText()) ) { // block + g.tool.errMgr.grammarError(ErrorType.ILLEGAL_OPTION, + g.fileName, + optionID, + optionID.getText()); + ok = false; + } + if ( !g.isLexer() && !Grammar.ParserBlockOptions.contains(optionID.getText()) ) { // block + g.tool.errMgr.grammarError(ErrorType.ILLEGAL_OPTION, + g.fileName, + optionID, + optionID.getText()); + ok = false; + } + } + else if ( parent.getType()==ANTLRParser.RULE ) { + if ( !Grammar.ruleOptions.contains(optionID.getText()) ) { // rule + g.tool.errMgr.grammarError(ErrorType.ILLEGAL_OPTION, + g.fileName, + optionID, + optionID.getText()); + ok = false; + } + } + else if ( parent.getType()==ANTLRParser.GRAMMAR && + !legalGrammarOption(optionID.getText()) ) { // grammar + g.tool.errMgr.grammarError(ErrorType.ILLEGAL_OPTION, + g.fileName, + optionID, + optionID.getText()); + ok = false; + } + + return ok; + } + + /** Check option is appropriate for elem; parent of ID is ELEMENT_OPTIONS */ + boolean checkElementOptions(GrammarASTWithOptions elem, + GrammarAST ID, + GrammarAST valueAST) + { + if (checkAssocElementOption && ID != null && "assoc".equals(ID.getText())) { + if (elem.getType() != ANTLRParser.ALT) { + Token optionID = ID.token; + String fileName = optionID.getInputStream().getSourceName(); + g.tool.errMgr.grammarError(ErrorType.UNRECOGNIZED_ASSOC_OPTION, + fileName, + optionID, + currentRuleName); + } + } + + if ( elem instanceof RuleRefAST ) { + return checkRuleRefOptions((RuleRefAST)elem, ID, valueAST); + } + if ( elem instanceof TerminalAST ) { + return checkTokenOptions((TerminalAST)elem, ID, valueAST); + } + if ( elem.getType()==ANTLRParser.ACTION ) { + return false; + } + if ( elem.getType()==ANTLRParser.SEMPRED ) { + Token optionID = ID.token; + String fileName = optionID.getInputStream().getSourceName(); + if ( valueAST!=null && !Grammar.semPredOptions.contains(optionID.getText()) ) { + g.tool.errMgr.grammarError(ErrorType.ILLEGAL_OPTION, + fileName, + optionID, + optionID.getText()); + return false; + } + } + return false; + } + + boolean checkRuleRefOptions(RuleRefAST elem, GrammarAST ID, GrammarAST valueAST) { + Token optionID = ID.token; + String fileName = optionID.getInputStream().getSourceName(); + // don't care about id<SimpleValue> options + if ( valueAST!=null && !Grammar.ruleRefOptions.contains(optionID.getText()) ) { + g.tool.errMgr.grammarError(ErrorType.ILLEGAL_OPTION, + fileName, + optionID, + optionID.getText()); + return false; + } + // TODO: extra checks depending on rule kind? + return true; + } + + boolean checkTokenOptions(TerminalAST elem, GrammarAST ID, GrammarAST valueAST) { + Token optionID = ID.token; + String fileName = optionID.getInputStream().getSourceName(); + // don't care about ID<ASTNodeName> options + if ( valueAST!=null && !Grammar.tokenOptions.contains(optionID.getText()) ) { + g.tool.errMgr.grammarError(ErrorType.ILLEGAL_OPTION, + fileName, + optionID, + optionID.getText()); + return false; + } + // TODO: extra checks depending on terminal kind? + return true; + } + + boolean legalGrammarOption(String key) { + switch ( g.getType() ) { + case ANTLRParser.LEXER : + return Grammar.lexerOptions.contains(key); + case ANTLRParser.PARSER : + return Grammar.parserOptions.contains(key); + default : + return Grammar.parserOptions.contains(key); + } + } + + void checkImport(Token importID) { + Grammar delegate = g.getImportedGrammar(importID.getText()); + if ( delegate==null ) return; + List<Integer> validDelegators = validImportTypes.get(delegate.getType()); + if ( validDelegators!=null && !validDelegators.contains(g.getType()) ) { + g.tool.errMgr.grammarError(ErrorType.INVALID_IMPORT, + g.fileName, + importID, + g, delegate); + } + if ( g.isCombined() && + (delegate.name.equals(g.name+Grammar.getGrammarTypeToFileNameSuffix(ANTLRParser.LEXER))|| + delegate.name.equals(g.name+Grammar.getGrammarTypeToFileNameSuffix(ANTLRParser.PARSER))) ) + { + g.tool.errMgr.grammarError(ErrorType.IMPORT_NAME_CLASH, + g.fileName, + importID, + g, delegate); + } + } +} diff --git a/tool/src/org/antlr/v4/semantics/BlankActionSplitterListener.java b/tool/src/org/antlr/v4/semantics/BlankActionSplitterListener.java new file mode 100644 index 0000000..e919890 --- /dev/null +++ b/tool/src/org/antlr/v4/semantics/BlankActionSplitterListener.java @@ -0,0 +1,75 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.semantics; + +import org.antlr.runtime.Token; +import org.antlr.v4.parse.ActionSplitterListener; + +public class BlankActionSplitterListener implements ActionSplitterListener { + @Override + public void qualifiedAttr(String expr, Token x, Token y) { + } + + @Override + public void setAttr(String expr, Token x, Token rhs) { + } + + @Override + public void attr(String expr, Token x) { + } + + public void templateInstance(String expr) { + } + + @Override + public void nonLocalAttr(String expr, Token x, Token y) { + } + + @Override + public void setNonLocalAttr(String expr, Token x, Token y, Token rhs) { + } + + public void indirectTemplateInstance(String expr) { + } + + public void setExprAttribute(String expr) { + } + + public void setSTAttribute(String expr) { + } + + public void templateExpr(String expr) { + } + + @Override + public void text(String text) { + } +} diff --git a/tool/src/org/antlr/v4/semantics/RuleCollector.java b/tool/src/org/antlr/v4/semantics/RuleCollector.java new file mode 100644 index 0000000..fa7eb2d --- /dev/null +++ b/tool/src/org/antlr/v4/semantics/RuleCollector.java @@ -0,0 +1,138 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.semantics; + +import org.antlr.v4.analysis.LeftRecursiveRuleAnalyzer; +import org.antlr.v4.misc.OrderedHashMap; +import org.antlr.v4.misc.Utils; +import org.antlr.v4.parse.GrammarTreeVisitor; +import org.antlr.v4.parse.ScopeParser; +import org.antlr.v4.tool.AttributeDict; +import org.antlr.v4.tool.ErrorManager; +import org.antlr.v4.tool.Grammar; +import org.antlr.v4.tool.LeftRecursiveRule; +import org.antlr.v4.tool.Rule; +import org.antlr.v4.tool.ast.ActionAST; +import org.antlr.v4.tool.ast.AltAST; +import org.antlr.v4.tool.ast.GrammarAST; +import org.antlr.v4.tool.ast.RuleAST; +import org.stringtemplate.v4.misc.MultiMap; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class RuleCollector extends GrammarTreeVisitor { + /** which grammar are we checking */ + public Grammar g; + public ErrorManager errMgr; + + // stuff to collect. this is the output + public OrderedHashMap<String, Rule> rules = new OrderedHashMap<String, Rule>(); + public MultiMap<String,GrammarAST> ruleToAltLabels = new MultiMap<String, GrammarAST>(); + public Map<String,String> altLabelToRuleName = new HashMap<String, String>(); + + public RuleCollector(Grammar g) { + this.g = g; + this.errMgr = g.tool.errMgr; + } + + @Override + public ErrorManager getErrorManager() { return errMgr; } + + public void process(GrammarAST ast) { visitGrammar(ast); } + + @Override + public void discoverRule(RuleAST rule, GrammarAST ID, + List<GrammarAST> modifiers, ActionAST arg, + ActionAST returns, GrammarAST thrws, + GrammarAST options, ActionAST locals, + List<GrammarAST> actions, + GrammarAST block) + { + int numAlts = block.getChildCount(); + Rule r; + if ( LeftRecursiveRuleAnalyzer.hasImmediateRecursiveRuleRefs(rule, ID.getText()) ) { + r = new LeftRecursiveRule(g, ID.getText(), rule); + } + else { + r = new Rule(g, ID.getText(), rule, numAlts); + } + rules.put(r.name, r); + + if ( arg!=null ) { + r.args = ScopeParser.parseTypedArgList(arg, arg.getText(), g); + r.args.type = AttributeDict.DictType.ARG; + r.args.ast = arg; + arg.resolver = r.alt[currentOuterAltNumber]; + } + + if ( returns!=null ) { + r.retvals = ScopeParser.parseTypedArgList(returns, returns.getText(), g); + r.retvals.type = AttributeDict.DictType.RET; + r.retvals.ast = returns; + } + + if ( locals!=null ) { + r.locals = ScopeParser.parseTypedArgList(locals, locals.getText(), g); + r.locals.type = AttributeDict.DictType.LOCAL; + r.locals.ast = locals; + } + + for (GrammarAST a : actions) { + // a = ^(AT ID ACTION) + ActionAST action = (ActionAST) a.getChild(1); + r.namedActions.put(a.getChild(0).getText(), action); + action.resolver = r; + } + } + + @Override + public void discoverOuterAlt(AltAST alt) { + if ( alt.altLabel!=null ) { + ruleToAltLabels.map(currentRuleName, alt.altLabel); + String altLabel = alt.altLabel.getText(); + altLabelToRuleName.put(Utils.capitalize(altLabel), currentRuleName); + altLabelToRuleName.put(Utils.decapitalize(altLabel), currentRuleName); + } + } + + @Override + public void discoverLexerRule(RuleAST rule, GrammarAST ID, List<GrammarAST> modifiers, + GrammarAST block) + { + int numAlts = block.getChildCount(); + Rule r = new Rule(g, ID.getText(), rule, numAlts); + r.mode = currentModeName; + if ( !modifiers.isEmpty() ) r.modifiers = modifiers; + rules.put(r.name, r); + } +} diff --git a/tool/src/org/antlr/v4/semantics/SemanticPipeline.java b/tool/src/org/antlr/v4/semantics/SemanticPipeline.java new file mode 100644 index 0000000..3561f91 --- /dev/null +++ b/tool/src/org/antlr/v4/semantics/SemanticPipeline.java @@ -0,0 +1,300 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.semantics; + +import org.antlr.v4.analysis.LeftRecursiveRuleTransformer; +import org.antlr.v4.parse.ANTLRParser; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.misc.Pair; +import org.antlr.v4.tool.ErrorType; +import org.antlr.v4.tool.Grammar; +import org.antlr.v4.tool.LexerGrammar; +import org.antlr.v4.tool.Rule; +import org.antlr.v4.tool.ast.GrammarAST; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** Do as much semantic checking as we can and fill in grammar + * with rules, actions, and token definitions. + * The only side effects are in the grammar passed to process(). + * We consume a bunch of memory here while we build up data structures + * to perform checking, but all of it goes away after this pipeline object + * gets garbage collected. + * + * After this pipeline finishes, we can be sure that the grammar + * is syntactically correct and that it's semantically correct enough for us + * to attempt grammar analysis. We have assigned all token types. + * Note that imported grammars bring in token and rule definitions + * but only the root grammar and any implicitly created lexer grammar + * get their token definitions filled up. We are treating the + * imported grammars like includes. + * + * The semantic pipeline works on root grammars (those that do the importing, + * if any). Upon entry to the semantic pipeline, all imported grammars + * should have been loaded into delegate grammar objects with their + * ASTs created. The pipeline does the BasicSemanticChecks on the + * imported grammar before collecting symbols. We cannot perform the + * simple checks such as undefined rule until we have collected all + * tokens and rules from the imported grammars into a single collection. + */ +public class SemanticPipeline { + public Grammar g; + + public SemanticPipeline(Grammar g) { + this.g = g; + } + + public void process() { + if ( g.ast==null ) return; + + // COLLECT RULE OBJECTS + RuleCollector ruleCollector = new RuleCollector(g); + ruleCollector.process(g.ast); + + // DO BASIC / EASY SEMANTIC CHECKS + BasicSemanticChecks basics = new BasicSemanticChecks(g, ruleCollector); + basics.process(); + + // TRANSFORM LEFT-RECURSIVE RULES + int prevErrors = g.tool.errMgr.getNumErrors(); + LeftRecursiveRuleTransformer lrtrans = + new LeftRecursiveRuleTransformer(g.ast, ruleCollector.rules.values(), g); + lrtrans.translateLeftRecursiveRules(); + + // don't continue if we got errors during left-recursion elimination + if ( g.tool.errMgr.getNumErrors()>prevErrors ) return; + + // STORE RULES IN GRAMMAR + for (Rule r : ruleCollector.rules.values()) { + g.defineRule(r); + } + + // COLLECT SYMBOLS: RULES, ACTIONS, TERMINALS, ... + SymbolCollector collector = new SymbolCollector(g); + collector.process(g.ast); + + // CHECK FOR SYMBOL COLLISIONS + SymbolChecks symcheck = new SymbolChecks(g, collector); + symcheck.process(); // side-effect: strip away redef'd rules. + + for (GrammarAST a : collector.namedActions) { + g.defineAction(a); + } + + // LINK (outermost) ALT NODES WITH Alternatives + for (Rule r : g.rules.values()) { + for (int i=1; i<=r.numberOfAlts; i++) { + r.alt[i].ast.alt = r.alt[i]; + } + } + + // ASSIGN TOKEN TYPES + g.importTokensFromTokensFile(); + if ( g.isLexer() ) { + assignLexerTokenTypes(g, collector.tokensDefs); + } + else { + assignTokenTypes(g, collector.tokensDefs, + collector.tokenIDRefs, collector.terminals); + } + + assignChannelTypes(g, collector.channelDefs); + + // CHECK RULE REFS NOW (that we've defined rules in grammar) + symcheck.checkRuleArgs(g, collector.rulerefs); + identifyStartRules(collector); + symcheck.checkForQualifiedRuleIssues(g, collector.qualifiedRulerefs); + + // don't continue if we got symbol errors + if ( g.tool.getNumErrors()>0 ) return; + + // CHECK ATTRIBUTE EXPRESSIONS FOR SEMANTIC VALIDITY + AttributeChecks.checkAllAttributeExpressions(g); + + UseDefAnalyzer.trackTokenRuleRefsInActions(g); + } + + void identifyStartRules(SymbolCollector collector) { + for (GrammarAST ref : collector.rulerefs) { + String ruleName = ref.getText(); + Rule r = g.getRule(ruleName); + if ( r!=null ) r.isStartRule = false; + } + } + + void assignLexerTokenTypes(Grammar g, List<GrammarAST> tokensDefs) { + Grammar G = g.getOutermostGrammar(); // put in root, even if imported + for (GrammarAST def : tokensDefs) { + // tokens { id (',' id)* } so must check IDs not TOKEN_REF + if ( Grammar.isTokenName(def.getText()) ) { + G.defineTokenName(def.getText()); + } + } + + /* Define token types for nonfragment rules which do not include a 'type(...)' + * or 'more' lexer command. + */ + for (Rule r : g.rules.values()) { + if ( !r.isFragment() && !hasTypeOrMoreCommand(r) ) { + G.defineTokenName(r.name); + } + } + + // FOR ALL X : 'xxx'; RULES, DEFINE 'xxx' AS TYPE X + List<Pair<GrammarAST,GrammarAST>> litAliases = + Grammar.getStringLiteralAliasesFromLexerRules(g.ast); + Set<String> conflictingLiterals = new HashSet<String>(); + if ( litAliases!=null ) { + for (Pair<GrammarAST,GrammarAST> pair : litAliases) { + GrammarAST nameAST = pair.a; + GrammarAST litAST = pair.b; + if ( !G.stringLiteralToTypeMap.containsKey(litAST.getText()) ) { + G.defineTokenAlias(nameAST.getText(), litAST.getText()); + } + else { + // oops two literal defs in two rules (within or across modes). + conflictingLiterals.add(litAST.getText()); + } + } + for (String lit : conflictingLiterals) { + // Remove literal if repeated across rules so it's not + // found by parser grammar. + Integer value = G.stringLiteralToTypeMap.remove(lit); + if (value != null && value > 0 && value < G.typeToStringLiteralList.size() && lit.equals(G.typeToStringLiteralList.get(value))) { + G.typeToStringLiteralList.set(value, null); + } + } + } + + } + + boolean hasTypeOrMoreCommand(Rule r) { + GrammarAST ast = r.ast; + if (ast == null) { + return false; + } + + GrammarAST altActionAst = (GrammarAST)ast.getFirstDescendantWithType(ANTLRParser.LEXER_ALT_ACTION); + if (altActionAst == null) { + // the rule isn't followed by any commands + return false; + } + + // first child is the alt itself, subsequent are the actions + for (int i = 1; i < altActionAst.getChildCount(); i++) { + GrammarAST node = (GrammarAST)altActionAst.getChild(i); + if (node.getType() == ANTLRParser.LEXER_ACTION_CALL) { + if ("type".equals(node.getChild(0).getText())) { + return true; + } + } + else if ("more".equals(node.getText())) { + return true; + } + } + + return false; + } + + void assignTokenTypes(Grammar g, List<GrammarAST> tokensDefs, + List<GrammarAST> tokenIDs, List<GrammarAST> terminals) + { + //Grammar G = g.getOutermostGrammar(); // put in root, even if imported + + // create token types for tokens { A, B, C } ALIASES + for (GrammarAST alias : tokensDefs) { + if (g.getTokenType(alias.getText()) != Token.INVALID_TYPE) { + g.tool.errMgr.grammarError(ErrorType.TOKEN_NAME_REASSIGNMENT, g.fileName, alias.token, alias.getText()); + } + + g.defineTokenName(alias.getText()); + } + + // DEFINE TOKEN TYPES FOR TOKEN REFS LIKE ID, INT + for (GrammarAST idAST : tokenIDs) { + if (g.getTokenType(idAST.getText()) == Token.INVALID_TYPE) { + g.tool.errMgr.grammarError(ErrorType.IMPLICIT_TOKEN_DEFINITION, g.fileName, idAST.token, idAST.getText()); + } + + g.defineTokenName(idAST.getText()); + } + + // VERIFY TOKEN TYPES FOR STRING LITERAL REFS LIKE 'while', ';' + for (GrammarAST termAST : terminals) { + if (termAST.getType() != ANTLRParser.STRING_LITERAL) { + continue; + } + + if (g.getTokenType(termAST.getText()) == Token.INVALID_TYPE) { + g.tool.errMgr.grammarError(ErrorType.IMPLICIT_STRING_DEFINITION, g.fileName, termAST.token, termAST.getText()); + } + } + + g.tool.log("semantics", "tokens="+g.tokenNameToTypeMap); + g.tool.log("semantics", "strings="+g.stringLiteralToTypeMap); + } + + /** + * Assign constant values to custom channels defined in a grammar. + * + * @param g The grammar. + * @param channelDefs A collection of AST nodes defining individual channels + * within a {@code channels{}} block in the grammar. + */ + void assignChannelTypes(Grammar g, List<GrammarAST> channelDefs) { + Grammar outermost = g.getOutermostGrammar(); + for (GrammarAST channel : channelDefs) { + String channelName = channel.getText(); + + // Channel names can't alias tokens or modes, because constant + // values are also assigned to them and the ->channel(NAME) lexer + // command does not distinguish between the various ways a constant + // can be declared. This method does not verify that channels do not + // alias rules, because rule names are not associated with constant + // values in ANTLR grammar semantics. + + if (g.getTokenType(channelName) != Token.INVALID_TYPE) { + g.tool.errMgr.grammarError(ErrorType.CHANNEL_CONFLICTS_WITH_TOKEN, g.fileName, channel.token, channelName); + } + + if (outermost instanceof LexerGrammar) { + LexerGrammar lexerGrammar = (LexerGrammar)outermost; + if (lexerGrammar.modes.containsKey(channelName)) { + g.tool.errMgr.grammarError(ErrorType.CHANNEL_CONFLICTS_WITH_MODE, g.fileName, channel.token, channelName); + } + } + + outermost.defineChannelName(channel.getText()); + } + } +} diff --git a/tool/src/org/antlr/v4/semantics/SymbolChecks.java b/tool/src/org/antlr/v4/semantics/SymbolChecks.java new file mode 100644 index 0000000..b280f40 --- /dev/null +++ b/tool/src/org/antlr/v4/semantics/SymbolChecks.java @@ -0,0 +1,312 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.semantics; + +import org.antlr.v4.parse.ANTLRParser; +import org.antlr.v4.tool.Alternative; +import org.antlr.v4.tool.Attribute; +import org.antlr.v4.tool.AttributeDict; +import org.antlr.v4.tool.ErrorManager; +import org.antlr.v4.tool.ErrorType; +import org.antlr.v4.tool.Grammar; +import org.antlr.v4.tool.LabelElementPair; +import org.antlr.v4.tool.Rule; +import org.antlr.v4.tool.ast.GrammarAST; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** Check for symbol problems; no side-effects. Inefficient to walk rules + * and such multiple times, but I like isolating all error checking outside + * of code that actually defines symbols etc... + * + * Side-effect: strip away redef'd rules. + */ +public class SymbolChecks { + Grammar g; + SymbolCollector collector; + Map<String, Rule> nameToRuleMap = new HashMap<String, Rule>(); + Set<String> tokenIDs = new HashSet<String>(); + Map<String, Set<String>> actionScopeToActionNames = new HashMap<String, Set<String>>(); +// DoubleKeyMap<String, String, GrammarAST> namedActions = +// new DoubleKeyMap<String, String, GrammarAST>(); + + public ErrorManager errMgr; + + protected final Set<String> reservedNames = new HashSet<String>(); + { + reservedNames.add("EOF"); + } + + public SymbolChecks(Grammar g, SymbolCollector collector) { + this.g = g; + this.collector = collector; + this.errMgr = g.tool.errMgr; + + for (GrammarAST tokenId : collector.tokenIDRefs) { + tokenIDs.add(tokenId.getText()); + } + /* + System.out.println("rules="+collector.rules); + System.out.println("rulerefs="+collector.rulerefs); + System.out.println("tokenIDRefs="+collector.tokenIDRefs); + System.out.println("terminals="+collector.terminals); + System.out.println("strings="+collector.strings); + System.out.println("tokensDef="+collector.tokensDefs); + System.out.println("actions="+collector.actions); + System.out.println("scopes="+collector.scopes); + */ + } + + public void process() { + // methods affect fields, but no side-effects outside this object + // So, call order sensitive + // First collect all rules for later use in checkForLabelConflict() + if ( g.rules!=null ) { + for (Rule r : g.rules.values()) nameToRuleMap.put(r.name, r); + } + checkReservedNames(g.rules.values()); + checkActionRedefinitions(collector.namedActions); + checkForTokenConflicts(collector.tokenIDRefs); // sets tokenIDs + checkForLabelConflicts(g.rules.values()); + } + + public void checkActionRedefinitions(List<GrammarAST> actions) { + if ( actions==null ) return; + String scope = g.getDefaultActionScope(); + String name; + GrammarAST nameNode; + for (GrammarAST ampersandAST : actions) { + nameNode = (GrammarAST)ampersandAST.getChild(0); + if ( ampersandAST.getChildCount()==2 ) { + name = nameNode.getText(); + } + else { + scope = nameNode.getText(); + name = ampersandAST.getChild(1).getText(); + } + Set<String> scopeActions = actionScopeToActionNames.get(scope); + if ( scopeActions==null ) { // init scope + scopeActions = new HashSet<String>(); + actionScopeToActionNames.put(scope, scopeActions); + } + if ( !scopeActions.contains(name) ) { + scopeActions.add(name); + } + else { + errMgr.grammarError(ErrorType.ACTION_REDEFINITION, + g.fileName, nameNode.token, name); + } + } + } + + public void checkForTokenConflicts(List<GrammarAST> tokenIDRefs) { +// for (GrammarAST a : tokenIDRefs) { +// Token t = a.token; +// String ID = t.getText(); +// tokenIDs.add(ID); +// } + } + + /** Make sure a label doesn't conflict with another symbol. + * Labels must not conflict with: rules, tokens, scope names, + * return values, parameters, and rule-scope dynamic attributes + * defined in surrounding rule. Also they must have same type + * for repeated defs. + */ + public void checkForLabelConflicts(Collection<Rule> rules) { + for (Rule r : rules) { + checkForAttributeConflicts(r); + Map<String, LabelElementPair> labelNameSpace = + new HashMap<String, LabelElementPair>(); + for (int i=1; i<=r.numberOfAlts; i++) { + if (r.hasAltSpecificContexts()) { + labelNameSpace.clear(); + } + + Alternative a = r.alt[i]; + for (List<LabelElementPair> pairs : a.labelDefs.values() ) { + for (LabelElementPair p : pairs) { + checkForLabelConflict(r, p.label); + String name = p.label.getText(); + LabelElementPair prev = labelNameSpace.get(name); + if ( prev==null ) labelNameSpace.put(name, p); + else checkForTypeMismatch(prev, p); + } + } + } + } + } + + void checkForTypeMismatch(LabelElementPair prevLabelPair, + LabelElementPair labelPair) + { + // label already defined; if same type, no problem + if ( prevLabelPair.type != labelPair.type ) { + String typeMismatchExpr = labelPair.type+"!="+prevLabelPair.type; + errMgr.grammarError( + ErrorType.LABEL_TYPE_CONFLICT, + g.fileName, + labelPair.label.token, + labelPair.label.getText(), + typeMismatchExpr); + } + } + + public void checkForLabelConflict(Rule r, GrammarAST labelID) { + String name = labelID.getText(); + if (nameToRuleMap.containsKey(name)) { + ErrorType etype = ErrorType.LABEL_CONFLICTS_WITH_RULE; + errMgr.grammarError(etype, g.fileName, labelID.token, name, r.name); + } + + if (tokenIDs.contains(name)) { + ErrorType etype = ErrorType.LABEL_CONFLICTS_WITH_TOKEN; + errMgr.grammarError(etype, g.fileName, labelID.token, name, r.name); + } + + if (r.args != null && r.args.get(name) != null) { + ErrorType etype = ErrorType.LABEL_CONFLICTS_WITH_ARG; + errMgr.grammarError(etype, g.fileName, labelID.token, name, r.name); + } + + if (r.retvals != null && r.retvals.get(name) != null) { + ErrorType etype = ErrorType.LABEL_CONFLICTS_WITH_RETVAL; + errMgr.grammarError(etype, g.fileName, labelID.token, name, r.name); + } + + if (r.locals != null && r.locals.get(name) != null) { + ErrorType etype = ErrorType.LABEL_CONFLICTS_WITH_LOCAL; + errMgr.grammarError(etype, g.fileName, labelID.token, name, r.name); + } + } + + public void checkForAttributeConflicts(Rule r) { + checkDeclarationRuleConflicts(r, r.args, nameToRuleMap.keySet(), ErrorType.ARG_CONFLICTS_WITH_RULE); + checkDeclarationRuleConflicts(r, r.args, tokenIDs, ErrorType.ARG_CONFLICTS_WITH_TOKEN); + + checkDeclarationRuleConflicts(r, r.retvals, nameToRuleMap.keySet(), ErrorType.RETVAL_CONFLICTS_WITH_RULE); + checkDeclarationRuleConflicts(r, r.retvals, tokenIDs, ErrorType.RETVAL_CONFLICTS_WITH_TOKEN); + + checkDeclarationRuleConflicts(r, r.locals, nameToRuleMap.keySet(), ErrorType.LOCAL_CONFLICTS_WITH_RULE); + checkDeclarationRuleConflicts(r, r.locals, tokenIDs, ErrorType.LOCAL_CONFLICTS_WITH_TOKEN); + + checkLocalConflictingDeclarations(r, r.retvals, r.args, ErrorType.RETVAL_CONFLICTS_WITH_ARG); + checkLocalConflictingDeclarations(r, r.locals, r.args, ErrorType.LOCAL_CONFLICTS_WITH_ARG); + checkLocalConflictingDeclarations(r, r.locals, r.retvals, ErrorType.LOCAL_CONFLICTS_WITH_RETVAL); + } + + protected void checkDeclarationRuleConflicts(Rule r, AttributeDict attributes, Set<String> ruleNames, ErrorType errorType) { + if (attributes == null) { + return; + } + + for (Attribute attribute : attributes.attributes.values()) { + if (ruleNames.contains(attribute.name)) { + errMgr.grammarError( + errorType, + g.fileName, + attribute.token != null ? attribute.token : ((GrammarAST)r.ast.getChild(0)).token, + attribute.name, + r.name); + } + } + } + + protected void checkLocalConflictingDeclarations(Rule r, AttributeDict attributes, AttributeDict referenceAttributes, ErrorType errorType) { + if (attributes == null || referenceAttributes == null) { + return; + } + + Set<String> conflictingKeys = attributes.intersection(referenceAttributes); + for (String key : conflictingKeys) { + errMgr.grammarError( + errorType, + g.fileName, + attributes.get(key).token != null ? attributes.get(key).token : ((GrammarAST) r.ast.getChild(0)).token, + key, + r.name); + } + } + + protected void checkReservedNames(Collection<Rule> rules) { + for (Rule rule : rules) { + if (reservedNames.contains(rule.name)) { + errMgr.grammarError(ErrorType.RESERVED_RULE_NAME, g.fileName, ((GrammarAST)rule.ast.getChild(0)).getToken(), rule.name); + } + } + } + + // CAN ONLY CALL THE TWO NEXT METHODS AFTER GRAMMAR HAS RULE DEFS (see semanticpipeline) + + public void checkRuleArgs(Grammar g, List<GrammarAST> rulerefs) { + if ( rulerefs==null ) return; + for (GrammarAST ref : rulerefs) { + String ruleName = ref.getText(); + Rule r = g.getRule(ruleName); + GrammarAST arg = (GrammarAST)ref.getFirstChildWithType(ANTLRParser.ARG_ACTION); + if ( arg!=null && (r==null || r.args==null) ) { + errMgr.grammarError(ErrorType.RULE_HAS_NO_ARGS, + g.fileName, ref.token, ruleName); + + } + else if ( arg==null && (r!=null&&r.args!=null) ) { + errMgr.grammarError(ErrorType.MISSING_RULE_ARGS, + g.fileName, ref.token, ruleName); + } + } + } + + public void checkForQualifiedRuleIssues(Grammar g, List<GrammarAST> qualifiedRuleRefs) { + for (GrammarAST dot : qualifiedRuleRefs) { + GrammarAST grammar = (GrammarAST)dot.getChild(0); + GrammarAST rule = (GrammarAST)dot.getChild(1); + g.tool.log("semantics", grammar.getText()+"."+rule.getText()); + Grammar delegate = g.getImportedGrammar(grammar.getText()); + if ( delegate==null ) { + errMgr.grammarError(ErrorType.NO_SUCH_GRAMMAR_SCOPE, + g.fileName, grammar.token, grammar.getText(), + rule.getText()); + } + else { + if ( g.getRule(grammar.getText(), rule.getText())==null ) { + errMgr.grammarError(ErrorType.NO_SUCH_RULE_IN_SCOPE, + g.fileName, rule.token, grammar.getText(), + rule.getText()); + } + } + } + } +} diff --git a/tool/src/org/antlr/v4/semantics/SymbolCollector.java b/tool/src/org/antlr/v4/semantics/SymbolCollector.java new file mode 100644 index 0000000..b0c43a4 --- /dev/null +++ b/tool/src/org/antlr/v4/semantics/SymbolCollector.java @@ -0,0 +1,213 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.semantics; + +import org.antlr.v4.parse.GrammarTreeVisitor; +import org.antlr.v4.tool.ErrorManager; +import org.antlr.v4.tool.Grammar; +import org.antlr.v4.tool.LabelElementPair; +import org.antlr.v4.tool.Rule; +import org.antlr.v4.tool.ast.ActionAST; +import org.antlr.v4.tool.ast.AltAST; +import org.antlr.v4.tool.ast.GrammarAST; +import org.antlr.v4.tool.ast.GrammarASTWithOptions; +import org.antlr.v4.tool.ast.PredAST; +import org.antlr.v4.tool.ast.RuleAST; +import org.antlr.v4.tool.ast.TerminalAST; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** Collects (create) rules, terminals, strings, actions, scopes etc... from AST + * side-effects: sets resolver field of asts for actions and + * defines predicates via definePredicateInAlt(), collects actions and stores + * in alts. + * TODO: remove side-effects! + */ +public class SymbolCollector extends GrammarTreeVisitor { + /** which grammar are we checking */ + public Grammar g; + + // stuff to collect + public List<GrammarAST> rulerefs = new ArrayList<GrammarAST>(); + public List<GrammarAST> qualifiedRulerefs = new ArrayList<GrammarAST>(); + public List<GrammarAST> terminals = new ArrayList<GrammarAST>(); + public List<GrammarAST> tokenIDRefs = new ArrayList<GrammarAST>(); + public Set<String> strings = new HashSet<String>(); + public List<GrammarAST> tokensDefs = new ArrayList<GrammarAST>(); + public List<GrammarAST> channelDefs = new ArrayList<GrammarAST>(); + + /** Track action name node in @parser::members {...} or @members {...} */ + List<GrammarAST> namedActions = new ArrayList<GrammarAST>(); + + public ErrorManager errMgr; + + // context + public Rule currentRule; + + public SymbolCollector(Grammar g) { + this.g = g; + this.errMgr = g.tool.errMgr; + } + + @Override + public ErrorManager getErrorManager() { return errMgr; } + + public void process(GrammarAST ast) { visitGrammar(ast); } + + @Override + public void globalNamedAction(GrammarAST scope, GrammarAST ID, ActionAST action) { + namedActions.add((GrammarAST)ID.getParent()); + action.resolver = g; + } + + @Override + public void defineToken(GrammarAST ID) { + terminals.add(ID); + tokenIDRefs.add(ID); + tokensDefs.add(ID); + } + + @Override + public void defineChannel(GrammarAST ID) { + channelDefs.add(ID); + } + + @Override + public void discoverRule(RuleAST rule, GrammarAST ID, + List<GrammarAST> modifiers, ActionAST arg, + ActionAST returns, GrammarAST thrws, + GrammarAST options, ActionAST locals, + List<GrammarAST> actions, + GrammarAST block) + { + currentRule = g.getRule(ID.getText()); + } + + @Override + public void discoverLexerRule(RuleAST rule, GrammarAST ID, List<GrammarAST> modifiers, + GrammarAST block) + { + currentRule = g.getRule(ID.getText()); + } + + @Override + public void discoverOuterAlt(AltAST alt) { + currentRule.alt[currentOuterAltNumber].ast = alt; + } + + @Override + public void actionInAlt(ActionAST action) { + currentRule.defineActionInAlt(currentOuterAltNumber, action); + action.resolver = currentRule.alt[currentOuterAltNumber]; + } + + @Override + public void sempredInAlt(PredAST pred) { + currentRule.definePredicateInAlt(currentOuterAltNumber, pred); + pred.resolver = currentRule.alt[currentOuterAltNumber]; + } + + @Override + public void ruleCatch(GrammarAST arg, ActionAST action) { + GrammarAST catchme = (GrammarAST)action.getParent(); + currentRule.exceptions.add(catchme); + action.resolver = currentRule; + } + + @Override + public void finallyAction(ActionAST action) { + currentRule.finallyAction = action; + action.resolver = currentRule; + } + + @Override + public void label(GrammarAST op, GrammarAST ID, GrammarAST element) { + LabelElementPair lp = new LabelElementPair(g, ID, element, op.getType()); + currentRule.alt[currentOuterAltNumber].labelDefs.map(ID.getText(), lp); + } + + @Override + public void stringRef(TerminalAST ref) { + terminals.add(ref); + strings.add(ref.getText()); + if ( currentRule!=null ) { + currentRule.alt[currentOuterAltNumber].tokenRefs.map(ref.getText(), ref); + } + } + + @Override + public void tokenRef(TerminalAST ref) { + terminals.add(ref); + tokenIDRefs.add(ref); + if ( currentRule!=null ) { + currentRule.alt[currentOuterAltNumber].tokenRefs.map(ref.getText(), ref); + } + } + + @Override + public void ruleRef(GrammarAST ref, ActionAST arg) { +// if ( inContext("DOT ...") ) qualifiedRulerefs.add((GrammarAST)ref.getParent()); + rulerefs.add(ref); + if ( currentRule!=null ) { + currentRule.alt[currentOuterAltNumber].ruleRefs.map(ref.getText(), ref); + } + } + + @Override + public void grammarOption(GrammarAST ID, GrammarAST valueAST) { + setActionResolver(valueAST); + } + + @Override + public void ruleOption(GrammarAST ID, GrammarAST valueAST) { + setActionResolver(valueAST); + } + + @Override + public void blockOption(GrammarAST ID, GrammarAST valueAST) { + setActionResolver(valueAST); + } + + @Override + public void elementOption(GrammarASTWithOptions t, GrammarAST ID, GrammarAST valueAST) { + setActionResolver(valueAST); + } + + /** In case of option id={...}, set resolve in case they use $foo */ + private void setActionResolver(GrammarAST valueAST) { + if ( valueAST instanceof ActionAST) { + ((ActionAST)valueAST).resolver = currentRule.alt[currentOuterAltNumber]; + } + } +} diff --git a/tool/src/org/antlr/v4/semantics/UseDefAnalyzer.java b/tool/src/org/antlr/v4/semantics/UseDefAnalyzer.java new file mode 100644 index 0000000..ac788e1 --- /dev/null +++ b/tool/src/org/antlr/v4/semantics/UseDefAnalyzer.java @@ -0,0 +1,119 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.semantics; + +import org.antlr.runtime.ANTLRStringStream; +import org.antlr.runtime.Token; +import org.antlr.v4.parse.ANTLRParser; +import org.antlr.v4.parse.ActionSplitter; +import org.antlr.v4.parse.ActionSplitterListener; +import org.antlr.v4.tool.Alternative; +import org.antlr.v4.tool.Grammar; +import org.antlr.v4.tool.LexerGrammar; +import org.antlr.v4.tool.Rule; +import org.antlr.v4.tool.ast.ActionAST; +import org.antlr.v4.tool.ast.GrammarAST; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** Look for errors and deadcode stuff */ +public class UseDefAnalyzer { + // side-effect: updates Alternative with refs in actions + public static void trackTokenRuleRefsInActions(Grammar g) { + for (Rule r : g.rules.values()) { + for (int i=1; i<=r.numberOfAlts; i++) { + Alternative alt = r.alt[i]; + for (ActionAST a : alt.actions) { + ActionSniffer sniffer = new ActionSniffer(g, r, alt, a, a.token); + sniffer.examineAction(); + } + } + } + } + + public static boolean actionIsContextDependent(ActionAST actionAST) { + ANTLRStringStream in = new ANTLRStringStream(actionAST.token.getText()); + in.setLine(actionAST.token.getLine()); + in.setCharPositionInLine(actionAST.token.getCharPositionInLine()); + final boolean[] dependent = new boolean[] {false}; // can't be simple bool with anon class + ActionSplitterListener listener = new BlankActionSplitterListener() { + @Override + public void nonLocalAttr(String expr, Token x, Token y) { dependent[0] = true; } + @Override + public void qualifiedAttr(String expr, Token x, Token y) { dependent[0] = true; } + @Override + public void setAttr(String expr, Token x, Token rhs) { dependent[0] = true; } + @Override + public void setExprAttribute(String expr) { dependent[0] = true; } + @Override + public void setNonLocalAttr(String expr, Token x, Token y, Token rhs) { dependent[0] = true; } + @Override + public void attr(String expr, Token x) { dependent[0] = true; } + }; + ActionSplitter splitter = new ActionSplitter(in, listener); + // forces eval, triggers listener methods + splitter.getActionTokens(); + return dependent[0]; + } + + /** Find all rules reachable from r directly or indirectly for all r in g */ + public static Map<Rule, Set<Rule>> getRuleDependencies(Grammar g) { + return getRuleDependencies(g, g.rules.values()); + } + + public static Map<Rule, Set<Rule>> getRuleDependencies(LexerGrammar g, String modeName) { + return getRuleDependencies(g, g.modes.get(modeName)); + } + + public static Map<Rule, Set<Rule>> getRuleDependencies(Grammar g, Collection<Rule> rules) { + Map<Rule, Set<Rule>> dependencies = new HashMap<Rule, Set<Rule>>(); + + for (Rule r : rules) { + List<GrammarAST> tokenRefs = r.ast.getNodesWithType(ANTLRParser.TOKEN_REF); + for (GrammarAST tref : tokenRefs) { + Set<Rule> calls = dependencies.get(r); + if ( calls==null ) { + calls = new HashSet<Rule>(); + dependencies.put(r, calls); + } + calls.add(g.getRule(tref.getText())); + } + } + + return dependencies; + } + +} diff --git a/tool/src/org/antlr/v4/tool/ANTLRMessage.java b/tool/src/org/antlr/v4/tool/ANTLRMessage.java new file mode 100644 index 0000000..36d9ab9 --- /dev/null +++ b/tool/src/org/antlr/v4/tool/ANTLRMessage.java @@ -0,0 +1,130 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.runtime.Token; +import org.stringtemplate.v4.ST; + +import java.util.Arrays; + +public class ANTLRMessage { + private static final Object[] EMPTY_ARGS = new Object[0]; + + + private final ErrorType errorType; + + private final Object[] args; + + private final Throwable e; + + // used for location template + public String fileName; + public int line = -1; + public int charPosition = -1; + + public Grammar g; + /** Most of the time, we'll have a token such as an undefined rule ref + * and so this will be set. + */ + public Token offendingToken; + + public ANTLRMessage(ErrorType errorType) { + this(errorType, (Throwable)null, Token.INVALID_TOKEN); + } + + public ANTLRMessage(ErrorType errorType, Token offendingToken, Object... args) { + this(errorType, null, offendingToken, args); + } + + public ANTLRMessage(ErrorType errorType, Throwable e, Token offendingToken, Object... args) { + this.errorType = errorType; + this.e = e; + this.args = args; + this.offendingToken = offendingToken; + } + + + public ErrorType getErrorType() { + return errorType; + } + + + public Object[] getArgs() { + if (args == null) { + return EMPTY_ARGS; + } + + return args; + } + + public ST getMessageTemplate(boolean verbose) { + ST messageST = new ST(getErrorType().msg); + messageST.impl.name = errorType.name(); + + messageST.add("verbose", verbose); + Object[] args = getArgs(); + for (int i=0; i<args.length; i++) { + String attr = "arg"; + if ( i>0 ) attr += i + 1; + messageST.add(attr, args[i]); + } + if ( args.length<2 ) messageST.add("arg2", null); // some messages ref arg2 + + Throwable cause = getCause(); + if ( cause!=null ) { + messageST.add("exception", cause); + messageST.add("stackTrace", cause.getStackTrace()); + } + else { + messageST.add("exception", null); // avoid ST error msg + messageST.add("stackTrace", null); + } + + return messageST; + } + + + public Throwable getCause() { + return e; + } + + @Override + public String toString() { + return "Message{" + + "errorType=" + getErrorType() + + ", args=" + Arrays.asList(getArgs()) + + ", e=" + getCause() + + ", fileName='" + fileName + '\'' + + ", line=" + line + + ", charPosition=" + charPosition + + '}'; + } +} diff --git a/tool/src/org/antlr/v4/tool/ANTLRToolListener.java b/tool/src/org/antlr/v4/tool/ANTLRToolListener.java new file mode 100644 index 0000000..76a73a0 --- /dev/null +++ b/tool/src/org/antlr/v4/tool/ANTLRToolListener.java @@ -0,0 +1,44 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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; + +/** Defines behavior of object able to handle error messages from ANTLR including + * both tool errors like "can't write file" and grammar ambiguity warnings. + * To avoid having to change tools that use ANTLR (like GUIs), I am + * wrapping error data in Message objects and passing them to the listener. + * In this way, users of this interface are less sensitive to changes in + * the info I need for error messages. + */ +public interface ANTLRToolListener { + public void info(String msg); + public void error(ANTLRMessage msg); + public void warning(ANTLRMessage msg); +} diff --git a/tool/src/org/antlr/v4/tool/Alternative.java b/tool/src/org/antlr/v4/tool/Alternative.java new file mode 100644 index 0000000..3d5e1cb --- /dev/null +++ b/tool/src/org/antlr/v4/tool/Alternative.java @@ -0,0 +1,163 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.ast.ActionAST; +import org.antlr.v4.tool.ast.AltAST; +import org.antlr.v4.tool.ast.GrammarAST; +import org.antlr.v4.tool.ast.TerminalAST; +import org.stringtemplate.v4.misc.MultiMap; + +import java.util.ArrayList; +import java.util.List; + +/** An outermost alternative for a rule. We don't track inner alternatives. */ +public class Alternative implements AttributeResolver { + public Rule rule; + + public AltAST ast; + + /** What alternative number is this outermost alt? 1..n */ + public int altNum; + + // token IDs, string literals in this alt + public MultiMap<String, TerminalAST> tokenRefs = new MultiMap<String, TerminalAST>(); + + // does not include labels + public MultiMap<String, GrammarAST> tokenRefsInActions = new MultiMap<String, GrammarAST>(); + + // all rule refs in this alt + public MultiMap<String, GrammarAST> ruleRefs = new MultiMap<String, GrammarAST>(); + + // does not include labels + public MultiMap<String, GrammarAST> ruleRefsInActions = new MultiMap<String, GrammarAST>(); + + /** A list of all LabelElementPair attached to tokens like id=ID, ids+=ID */ + public MultiMap<String, LabelElementPair> labelDefs = new MultiMap<String, LabelElementPair>(); + + // track all token, rule, label refs in rewrite (right of ->) + //public List<GrammarAST> rewriteElements = new ArrayList<GrammarAST>(); + + /** Track all executable actions other than named actions like @init + * and catch/finally (not in an alt). Also tracks predicates, rewrite actions. + * We need to examine these actions before code generation so + * that we can detect refs to $rule.attr etc... + * + * This tracks per alt + */ + public List<ActionAST> actions = new ArrayList<ActionAST>(); + + public Alternative(Rule r, int altNum) { this.rule = r; this.altNum = altNum; } + + @Override + public boolean resolvesToToken(String x, ActionAST node) { + if ( tokenRefs.get(x)!=null ) return true; + LabelElementPair anyLabelDef = getAnyLabelDef(x); + if ( anyLabelDef!=null && anyLabelDef.type==LabelType.TOKEN_LABEL ) return true; + return false; + } + + @Override + public boolean resolvesToAttributeDict(String x, ActionAST node) { + if ( resolvesToToken(x, node) ) return true; + if ( ruleRefs.get(x)!=null ) return true; // rule ref in this alt? + LabelElementPair anyLabelDef = getAnyLabelDef(x); + if ( anyLabelDef!=null && anyLabelDef.type==LabelType.RULE_LABEL ) return true; + return false; + } + + /** $x Attribute: rule arguments, return values, predefined rule prop. + */ + @Override + public Attribute resolveToAttribute(String x, ActionAST node) { + return rule.resolveToAttribute(x, node); // reuse that code + } + + /** $x.y, x can be surrounding rule, token/rule/label ref. y is visible + * attr in that dictionary. Can't see args on rule refs. + */ + @Override + public Attribute resolveToAttribute(String x, String y, ActionAST node) { + if ( tokenRefs.get(x)!=null ) { // token ref in this alt? + return rule.getPredefinedScope(LabelType.TOKEN_LABEL).get(y); + } + if ( ruleRefs.get(x)!=null ) { // rule ref in this alt? + // look up rule, ask it to resolve y (must be retval or predefined) + return rule.g.getRule(x).resolveRetvalOrProperty(y); + } + LabelElementPair anyLabelDef = getAnyLabelDef(x); + if ( anyLabelDef!=null && anyLabelDef.type==LabelType.RULE_LABEL ) { + return rule.g.getRule(anyLabelDef.element.getText()).resolveRetvalOrProperty(y); + } + else if ( anyLabelDef!=null ) { + AttributeDict scope = rule.getPredefinedScope(anyLabelDef.type); + if (scope == null) { + return null; + } + + return scope.get(y); + } + return null; + } + + @Override + public boolean resolvesToLabel(String x, ActionAST node) { + LabelElementPair anyLabelDef = getAnyLabelDef(x); + return anyLabelDef!=null && + (anyLabelDef.type==LabelType.TOKEN_LABEL || + anyLabelDef.type==LabelType.RULE_LABEL); + } + + @Override + public boolean resolvesToListLabel(String x, ActionAST node) { + LabelElementPair anyLabelDef = getAnyLabelDef(x); + return anyLabelDef!=null && + (anyLabelDef.type==LabelType.RULE_LIST_LABEL || + anyLabelDef.type==LabelType.TOKEN_LIST_LABEL); + } + + public LabelElementPair getAnyLabelDef(String x) { + List<LabelElementPair> labels = labelDefs.get(x); + if ( labels!=null ) return labels.get(0); + return null; + } + + /** x can be ruleref or rule label. */ + public Rule resolveToRule(String x) { + if ( ruleRefs.get(x)!=null ) return rule.g.getRule(x); + LabelElementPair anyLabelDef = getAnyLabelDef(x); + if ( anyLabelDef!=null && anyLabelDef.type==LabelType.RULE_LABEL ) { + return rule.g.getRule(anyLabelDef.element.getText()); + } + return null; + } +} diff --git a/tool/src/org/antlr/v4/tool/Attribute.java b/tool/src/org/antlr/v4/tool/Attribute.java new file mode 100644 index 0000000..3dc0258 --- /dev/null +++ b/tool/src/org/antlr/v4/tool/Attribute.java @@ -0,0 +1,73 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.runtime.Token; + +/** Track the names of attributes define in arg lists, return values, + * scope blocks etc... + */ +public class Attribute { + /** The entire declaration such as "String foo;" */ + public String decl; + + /** The type; might be empty such as for Python which has no static typing */ + public String type; + + /** The name of the attribute "foo" */ + public String name; + + /** A {@link Token} giving the position of the name of this attribute in the grammar. */ + public Token token; + + /** The optional attribute initialization expression */ + public String initValue; + + /** Who contains us? */ + public AttributeDict dict; + + public Attribute() {} + + public Attribute(String name) { this(name,null); } + + public Attribute(String name, String decl) { + this.name = name; + this.decl = decl; + } + + @Override + public String toString() { + if ( initValue!=null ) { + return type+" "+name+"="+initValue; + } + return type+" "+name; + } +}
\ No newline at end of file diff --git a/tool/src/org/antlr/v4/tool/AttributeDict.java b/tool/src/org/antlr/v4/tool/AttributeDict.java new file mode 100644 index 0000000..911170d --- /dev/null +++ b/tool/src/org/antlr/v4/tool/AttributeDict.java @@ -0,0 +1,108 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.runtime.Token; +import org.antlr.v4.tool.ast.GrammarAST; + +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Set; + +/** Track the attributes within retval, arg lists etc... + * <p/> + * Each rule has potentially 3 scopes: return values, + * parameters, and an implicitly-named scope (i.e., a scope defined in a rule). + * Implicitly-defined scopes are named after the rule; rules and scopes then + * must live in the same name space--no collisions allowed. + */ +public class AttributeDict { + public String name; + public GrammarAST ast; + public DictType type; + + /** All {@link Token} scopes (token labels) share the same fixed scope of + * of predefined attributes. I keep this out of the {@link Token} + * interface to avoid a runtime type leakage. + */ + public static final AttributeDict predefinedTokenDict = new AttributeDict(DictType.TOKEN); + static { + predefinedTokenDict.add(new Attribute("text")); + predefinedTokenDict.add(new Attribute("type")); + predefinedTokenDict.add(new Attribute("line")); + predefinedTokenDict.add(new Attribute("index")); + predefinedTokenDict.add(new Attribute("pos")); + predefinedTokenDict.add(new Attribute("channel")); + predefinedTokenDict.add(new Attribute("int")); + } + + public static enum DictType { + ARG, RET, LOCAL, TOKEN, + PREDEFINED_RULE, PREDEFINED_LEXER_RULE, + } + + /** The list of {@link Attribute} objects. */ + + public final LinkedHashMap<String, Attribute> attributes = + new LinkedHashMap<String, Attribute>(); + + public AttributeDict() {} + public AttributeDict(DictType type) { this.type = type; } + + public Attribute add(Attribute a) { a.dict = this; return attributes.put(a.name, a); } + public Attribute get(String name) { return attributes.get(name); } + + public String getName() { + return name; + } + + public int size() { return attributes.size(); } + + /** Return the set of keys that collide from + * {@code this} and {@code other}. + */ + + public Set<String> intersection(AttributeDict other) { + if ( other==null || other.size()==0 || size()==0 ) { + return Collections.emptySet(); + } + + Set<String> result = new HashSet<String>(attributes.keySet()); + result.retainAll(other.attributes.keySet()); + return result; + } + + @Override + public String toString() { + return getName()+":"+attributes; + } +} diff --git a/tool/src/org/antlr/v4/tool/AttributeResolver.java b/tool/src/org/antlr/v4/tool/AttributeResolver.java new file mode 100644 index 0000000..ee01ee1 --- /dev/null +++ b/tool/src/org/antlr/v4/tool/AttributeResolver.java @@ -0,0 +1,71 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.ast.ActionAST; + +/** Grammars, rules, and alternatives all have symbols visible to + * actions. To evaluate attr exprs, ask action for its resolver + * then ask resolver to look up various symbols. Depending on the context, + * some symbols are available at some aren't. + * + * Alternative level: + * + * $x Attribute: rule arguments, return values, predefined rule prop. + * AttributeDict: references to tokens and token labels in the + * current alt (including any elements within subrules contained + * in that outermost alt). x can be rule with scope or a global scope. + * List label: x is a token/rule list label. + * $x.y Attribute: x is surrounding rule, rule/token/label ref + * $s::y Attribute: s is any rule with scope or global scope; y is prop within + * + * Rule level: + * + * $x Attribute: rule arguments, return values, predefined rule prop. + * AttributeDict: references to token labels in *any* alt. x can + * be any rule with scope or global scope. + * List label: x is a token/rule list label. + * $x.y Attribute: x is surrounding rule, label ref (in any alts) + * $s::y Attribute: s is any rule with scope or global scope; y is prop within + * + * Grammar level: + * + * $s AttributeDict: s is a global scope + * $s::y Attribute: s is a global scope; y is prop within + */ +public interface AttributeResolver { + public boolean resolvesToListLabel(String x, ActionAST node); + public boolean resolvesToLabel(String x, ActionAST node); + public boolean resolvesToAttributeDict(String x, ActionAST node); + public boolean resolvesToToken(String x, ActionAST node); + public Attribute resolveToAttribute(String x, ActionAST node); + public Attribute resolveToAttribute(String x, String y, ActionAST node); +} diff --git a/tool/src/org/antlr/v4/tool/BuildDependencyGenerator.java b/tool/src/org/antlr/v4/tool/BuildDependencyGenerator.java new file mode 100644 index 0000000..69e71b5 --- /dev/null +++ b/tool/src/org/antlr/v4/tool/BuildDependencyGenerator.java @@ -0,0 +1,273 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.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 we are using the listener pattern (-listener on the command line) + * then we add: + * + * TListener.java : T.g + * TBaseListener.java : T.g + * + * If we are using the visitor pattern (-visitor on the command line) + * then we add: + * + * TVisitor.java : T.g + * TBaseVisitor.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.getTemplates().getInstanceOf("codeFileExtension"); + if (generator.getTemplates().isDefined("headerFile")) { + headerExtST = generator.getTemplates().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)); + String lexerTokens = g.name + suffix + CodeGenerator.VOCAB_FILE_EXTENSION; + files.add(getOutputFile(lexerTokens)); + + // TLexer.h + if (headerExtST != null) { + String header = g.name + suffix + headerExtST.render(); + files.add(getOutputFile(header)); + } + } + + if ( g.tool.gen_listener ) { + // add generated listener; e.g., TListener.java + files.add(getOutputFile(generator.getListenerFileName())); + // add generated base listener; e.g., TBaseListener.java + files.add(getOutputFile(generator.getBaseListenerFileName())); + } + + if ( g.tool.gen_visitor ) { + // add generated visitor; e.g., TVisitor.java + files.add(getOutputFile(generator.getVisitorFileName())); + // add generated base visitor; e.g., TBaseVisitor.java + files.add(getOutputFile(generator.getBaseVisitorFileName())); + } + + + // 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 = outputDir.toString().replace(" ", "\\ "); + 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, "UTF-8"); + } + + 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 = outputDir.replace(" ", "\\ "); + return escSpaces + File.separator + fileName; + } + else { + return outputDir + File.separator + fileName; + } + } +} diff --git a/tool/src/org/antlr/v4/tool/DOTGenerator.java b/tool/src/org/antlr/v4/tool/DOTGenerator.java new file mode 100644 index 0000000..740bfe4 --- /dev/null +++ b/tool/src/org/antlr/v4/tool/DOTGenerator.java @@ -0,0 +1,486 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.misc.Utils; +import org.antlr.v4.runtime.atn.ATNConfig; +import org.antlr.v4.runtime.atn.ATNState; +import org.antlr.v4.runtime.atn.AbstractPredicateTransition; +import org.antlr.v4.runtime.atn.ActionTransition; +import org.antlr.v4.runtime.atn.AtomTransition; +import org.antlr.v4.runtime.atn.BlockEndState; +import org.antlr.v4.runtime.atn.BlockStartState; +import org.antlr.v4.runtime.atn.DecisionState; +import org.antlr.v4.runtime.atn.NotSetTransition; +import org.antlr.v4.runtime.atn.PlusBlockStartState; +import org.antlr.v4.runtime.atn.PlusLoopbackState; +import org.antlr.v4.runtime.atn.RangeTransition; +import org.antlr.v4.runtime.atn.RuleStartState; +import org.antlr.v4.runtime.atn.RuleStopState; +import org.antlr.v4.runtime.atn.RuleTransition; +import org.antlr.v4.runtime.atn.SetTransition; +import org.antlr.v4.runtime.atn.StarBlockStartState; +import org.antlr.v4.runtime.atn.StarLoopEntryState; +import org.antlr.v4.runtime.atn.StarLoopbackState; +import org.antlr.v4.runtime.atn.Transition; +import org.antlr.v4.runtime.dfa.DFA; +import org.antlr.v4.runtime.dfa.DFAState; +import org.antlr.v4.runtime.misc.IntegerList; +import org.stringtemplate.v4.ST; +import org.stringtemplate.v4.STGroup; +import org.stringtemplate.v4.STGroupFile; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +/** The DOT (part of graphviz) generation aspect. */ +public class DOTGenerator { + public static final boolean STRIP_NONREDUCED_STATES = false; + + protected String arrowhead="normal"; + protected String rankdir="LR"; + + /** Library of output templates; use {@code <attrname>} format. */ + public static STGroup stlib = new STGroupFile("org/antlr/v4/tool/templates/dot/graphs.stg"); + + protected Grammar grammar; + + /** This aspect is associated with a grammar */ + public DOTGenerator(Grammar grammar) { + this.grammar = grammar; + } + + public String getDOT(DFA dfa, boolean isLexer) { + if ( dfa.s0==null ) return null; + + ST dot = stlib.getInstanceOf("dfa"); + dot.add("name", "DFA"+dfa.decision); + dot.add("startState", dfa.s0.stateNumber); +// dot.add("useBox", Tool.internalOption_ShowATNConfigsInDFA); + dot.add("rankdir", rankdir); + + // define stop states first; seems to be a bug in DOT where doublecircle + for (DFAState d : dfa.states.keySet()) { + if ( !d.isAcceptState ) continue; + ST st = stlib.getInstanceOf("stopstate"); + st.add("name", "s"+d.stateNumber); + st.add("label", getStateLabel(d)); + dot.add("states", st); + } + + for (DFAState d : dfa.states.keySet()) { + if ( d.isAcceptState ) continue; + if ( d.stateNumber == Integer.MAX_VALUE ) continue; + ST st = stlib.getInstanceOf("state"); + st.add("name", "s"+d.stateNumber); + st.add("label", getStateLabel(d)); + dot.add("states", st); + } + + for (DFAState d : dfa.states.keySet()) { + if ( d.edges!=null ) { + for (int i = 0; i < d.edges.length; i++) { + DFAState target = d.edges[i]; + if ( target==null) continue; + if ( target.stateNumber == Integer.MAX_VALUE ) continue; + int ttype = i-1; // we shift up for EOF as -1 for parser + String label = String.valueOf(ttype); + if ( isLexer ) label = "'"+getEdgeLabel(String.valueOf((char) i))+"'"; + else if ( grammar!=null ) label = grammar.getTokenDisplayName(ttype); + ST st = stlib.getInstanceOf("edge"); + st.add("label", label); + st.add("src", "s"+d.stateNumber); + st.add("target", "s"+target.stateNumber); + st.add("arrowhead", arrowhead); + dot.add("edges", st); + } + } + } + + String output = dot.render(); + return Utils.sortLinesInString(output); + } + + protected String getStateLabel(DFAState s) { + if ( s==null ) return "null"; + StringBuilder buf = new StringBuilder(250); + buf.append('s'); + buf.append(s.stateNumber); + if ( s.isAcceptState ) { + buf.append("=>").append(s.prediction); + } + if ( s.requiresFullContext) { + buf.append("^"); + } + if ( grammar!=null ) { + Set<Integer> alts = s.getAltSet(); + if ( alts!=null ) { + buf.append("\\n"); + // separate alts + IntegerList altList = new IntegerList(); + altList.addAll(alts); + altList.sort(); + Set<ATNConfig> configurations = s.configs; + for (int altIndex = 0; altIndex < altList.size(); altIndex++) { + int alt = altList.get(altIndex); + if ( altIndex>0 ) { + buf.append("\\n"); + } + buf.append("alt"); + buf.append(alt); + buf.append(':'); + // get a list of configs for just this alt + // it will help us print better later + List<ATNConfig> configsInAlt = new ArrayList<ATNConfig>(); + for (ATNConfig c : configurations) { + if (c.alt != alt) continue; + configsInAlt.add(c); + } + int n = 0; + for (int cIndex = 0; cIndex < configsInAlt.size(); cIndex++) { + ATNConfig c = configsInAlt.get(cIndex); + n++; + buf.append(c.toString(null, false)); + if ( (cIndex+1)<configsInAlt.size() ) { + buf.append(", "); + } + if ( n%5==0 && (configsInAlt.size()-cIndex)>3 ) { + buf.append("\\n"); + } + } + } + } + } + String stateLabel = buf.toString(); + return stateLabel; + } + + public String getDOT(ATNState startState) { + return getDOT(startState, false); + } + + public String getDOT(ATNState startState, boolean isLexer) { + Set<String> ruleNames = grammar.rules.keySet(); + String[] names = new String[ruleNames.size()+1]; + int i = 0; + for (String s : ruleNames) names[i++] = s; + return getDOT(startState, names, isLexer); + } + + /** Return a String containing a DOT description that, when displayed, + * will show the incoming state machine visually. All nodes reachable + * from startState will be included. + */ + public String getDOT(ATNState startState, String[] ruleNames, boolean isLexer) { + if ( startState==null ) return null; + + // The output DOT graph for visualization + Set<ATNState> markedStates = new HashSet<ATNState>(); + ST dot = stlib.getInstanceOf("atn"); + dot.add("startState", startState.stateNumber); + dot.add("rankdir", rankdir); + + List<ATNState> work = new LinkedList<ATNState>(); + + work.add(startState); + while ( !work.isEmpty() ) { + ATNState s = work.get(0); + if ( markedStates.contains(s) ) { work.remove(0); continue; } + markedStates.add(s); + + // don't go past end of rule node to the follow states + if ( s instanceof RuleStopState) continue; + + // special case: if decision point, then line up the alt start states + // unless it's an end of block +// if ( s instanceof BlockStartState ) { +// ST rankST = stlib.getInstanceOf("decision-rank"); +// DecisionState alt = (DecisionState)s; +// for (int i=0; i<alt.getNumberOfTransitions(); i++) { +// ATNState target = alt.transition(i).target; +// if ( target!=null ) { +// rankST.add("states", target.stateNumber); +// } +// } +// dot.add("decisionRanks", rankST); +// } + + // make a DOT edge for each transition + ST edgeST; + for (int i = 0; i < s.getNumberOfTransitions(); i++) { + Transition edge = s.transition(i); + if ( edge instanceof RuleTransition ) { + RuleTransition rr = ((RuleTransition)edge); + // don't jump to other rules, but display edge to follow node + edgeST = stlib.getInstanceOf("edge"); + + String label = "<" + ruleNames[rr.ruleIndex]; + if (((RuleStartState)rr.target).isLeftRecursiveRule) { + label += "[" + rr.precedence + "]"; + } + label += ">"; + + edgeST.add("label", label); + edgeST.add("src", "s"+s.stateNumber); + edgeST.add("target", "s"+rr.followState.stateNumber); + edgeST.add("arrowhead", arrowhead); + dot.add("edges", edgeST); + work.add(rr.followState); + continue; + } + if ( edge instanceof ActionTransition) { + edgeST = stlib.getInstanceOf("action-edge"); + edgeST.add("label", getEdgeLabel(edge.toString())); + } + else if ( edge instanceof AbstractPredicateTransition ) { + edgeST = stlib.getInstanceOf("edge"); + edgeST.add("label", getEdgeLabel(edge.toString())); + } + else if ( edge.isEpsilon() ) { + edgeST = stlib.getInstanceOf("epsilon-edge"); + edgeST.add("label", getEdgeLabel(edge.toString())); + boolean loopback = false; + if (edge.target instanceof PlusBlockStartState) { + loopback = s.equals(((PlusBlockStartState)edge.target).loopBackState); + } + else if (edge.target instanceof StarLoopEntryState) { + loopback = s.equals(((StarLoopEntryState)edge.target).loopBackState); + } + edgeST.add("loopback", loopback); + } + else if ( edge instanceof AtomTransition ) { + edgeST = stlib.getInstanceOf("edge"); + AtomTransition atom = (AtomTransition)edge; + String label = String.valueOf(atom.label); + if ( isLexer ) label = "'"+getEdgeLabel(String.valueOf((char)atom.label))+"'"; + else if ( grammar!=null ) label = grammar.getTokenDisplayName(atom.label); + edgeST.add("label", getEdgeLabel(label)); + } + else if ( edge instanceof SetTransition ) { + edgeST = stlib.getInstanceOf("edge"); + SetTransition set = (SetTransition)edge; + String label = set.label().toString(); + if ( isLexer ) label = set.label().toString(true); + else if ( grammar!=null ) label = set.label().toString(grammar.getVocabulary()); + if ( edge instanceof NotSetTransition ) label = "~"+label; + edgeST.add("label", getEdgeLabel(label)); + } + else if ( edge instanceof RangeTransition ) { + edgeST = stlib.getInstanceOf("edge"); + RangeTransition range = (RangeTransition)edge; + String label = range.label().toString(); + if ( isLexer ) label = range.toString(); + else if ( grammar!=null ) label = range.label().toString(grammar.getVocabulary()); + edgeST.add("label", getEdgeLabel(label)); + } + else { + edgeST = stlib.getInstanceOf("edge"); + edgeST.add("label", getEdgeLabel(edge.toString())); + } + edgeST.add("src", "s"+s.stateNumber); + edgeST.add("target", "s"+edge.target.stateNumber); + edgeST.add("arrowhead", arrowhead); + if (s.getNumberOfTransitions() > 1) { + edgeST.add("transitionIndex", i); + } else { + edgeST.add("transitionIndex", false); + } + dot.add("edges", edgeST); + work.add(edge.target); + } + } + + // define nodes we visited (they will appear first in DOT output) + // this is an example of ST's lazy eval :) + // define stop state first; seems to be a bug in DOT where doublecircle + // shape only works if we define them first. weird. +// ATNState stopState = startState.atn.ruleToStopState.get(startState.rule); +// if ( stopState!=null ) { +// ST st = stlib.getInstanceOf("stopstate"); +// st.add("name", "s"+stopState.stateNumber); +// st.add("label", getStateLabel(stopState)); +// dot.add("states", st); +// } + for (ATNState s : markedStates) { + if ( !(s instanceof RuleStopState) ) continue; + ST st = stlib.getInstanceOf("stopstate"); + st.add("name", "s"+s.stateNumber); + st.add("label", getStateLabel(s)); + dot.add("states", st); + } + + for (ATNState s : markedStates) { + if ( s instanceof RuleStopState ) continue; + ST st = stlib.getInstanceOf("state"); + st.add("name", "s"+s.stateNumber); + st.add("label", getStateLabel(s)); + st.add("transitions", s.getTransitions()); + dot.add("states", st); + } + + return dot.render(); + } + + + /** Do a depth-first walk of the state machine graph and + * fill a DOT description template. Keep filling the + * states and edges attributes. We know this is an ATN + * for a rule so don't traverse edges to other rules and + * don't go past rule end state. + */ +// protected void walkRuleATNCreatingDOT(ST dot, +// ATNState s) +// { +// if ( markedStates.contains(s) ) { +// return; // already visited this node +// } +// +// markedStates.add(s.stateNumber); // mark this node as completed. +// +// // first add this node +// ST stateST; +// if ( s instanceof RuleStopState ) { +// stateST = stlib.getInstanceOf("stopstate"); +// } +// else { +// stateST = stlib.getInstanceOf("state"); +// } +// stateST.add("name", getStateLabel(s)); +// dot.add("states", stateST); +// +// if ( s instanceof RuleStopState ) { +// return; // don't go past end of rule node to the follow states +// } +// +// // special case: if decision point, then line up the alt start states +// // unless it's an end of block +// if ( s instanceof DecisionState ) { +// GrammarAST n = ((ATNState)s).ast; +// if ( n!=null && s instanceof BlockEndState ) { +// ST rankST = stlib.getInstanceOf("decision-rank"); +// ATNState alt = (ATNState)s; +// while ( alt!=null ) { +// rankST.add("states", getStateLabel(alt)); +// if ( alt.transition(1) !=null ) { +// alt = (ATNState)alt.transition(1).target; +// } +// else { +// alt=null; +// } +// } +// dot.add("decisionRanks", rankST); +// } +// } +// +// // make a DOT edge for each transition +// ST edgeST = null; +// for (int i = 0; i < s.getNumberOfTransitions(); i++) { +// Transition edge = (Transition) s.transition(i); +// if ( edge instanceof RuleTransition ) { +// RuleTransition rr = ((RuleTransition)edge); +// // don't jump to other rules, but display edge to follow node +// edgeST = stlib.getInstanceOf("edge"); +// if ( rr.rule.g != grammar ) { +// edgeST.add("label", "<"+rr.rule.g.name+"."+rr.rule.name+">"); +// } +// else { +// edgeST.add("label", "<"+rr.rule.name+">"); +// } +// edgeST.add("src", getStateLabel(s)); +// edgeST.add("target", getStateLabel(rr.followState)); +// edgeST.add("arrowhead", arrowhead); +// dot.add("edges", edgeST); +// walkRuleATNCreatingDOT(dot, rr.followState); +// continue; +// } +// if ( edge instanceof ActionTransition ) { +// edgeST = stlib.getInstanceOf("action-edge"); +// } +// else if ( edge instanceof PredicateTransition ) { +// edgeST = stlib.getInstanceOf("edge"); +// } +// else if ( edge.isEpsilon() ) { +// edgeST = stlib.getInstanceOf("epsilon-edge"); +// } +// else { +// edgeST = stlib.getInstanceOf("edge"); +// } +// edgeST.add("label", getEdgeLabel(edge.toString(grammar))); +// edgeST.add("src", getStateLabel(s)); +// edgeST.add("target", getStateLabel(edge.target)); +// edgeST.add("arrowhead", arrowhead); +// dot.add("edges", edgeST); +// walkRuleATNCreatingDOT(dot, edge.target); // keep walkin' +// } +// } + + /** Fix edge strings so they print out in DOT properly; + * generate any gated predicates on edge too. + */ + protected String getEdgeLabel(String label) { + label = label.replace("\\", "\\\\"); + label = label.replace("\"", "\\\""); + label = label.replace("\n", "\\\\n"); + label = label.replace("\r", ""); + return label; + } + + protected String getStateLabel(ATNState s) { + if ( s==null ) return "null"; + String stateLabel = ""; + + if (s instanceof BlockStartState) { + stateLabel += "→\\n"; + } + else if (s instanceof BlockEndState) { + stateLabel += "←\\n"; + } + + stateLabel += String.valueOf(s.stateNumber); + + if (s instanceof PlusBlockStartState || s instanceof PlusLoopbackState) { + stateLabel += "+"; + } + else if (s instanceof StarBlockStartState || s instanceof StarLoopEntryState || s instanceof StarLoopbackState) { + stateLabel += "*"; + } + + if ( s instanceof DecisionState && ((DecisionState)s).decision>=0 ) { + stateLabel = stateLabel+"\\nd="+((DecisionState)s).decision; + } + + return stateLabel; + } + +} diff --git a/tool/src/org/antlr/v4/tool/DefaultToolListener.java b/tool/src/org/antlr/v4/tool/DefaultToolListener.java new file mode 100644 index 0000000..2005c85 --- /dev/null +++ b/tool/src/org/antlr/v4/tool/DefaultToolListener.java @@ -0,0 +1,69 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.stringtemplate.v4.ST; + +/** */ +public class DefaultToolListener implements ANTLRToolListener { + public Tool tool; + + public DefaultToolListener(Tool tool) { this.tool = tool; } + + @Override + public void info(String msg) { + if (tool.errMgr.formatWantsSingleLineMessage()) { + msg = msg.replace('\n', ' '); + } + System.out.println(msg); + } + + @Override + public void error(ANTLRMessage msg) { + ST msgST = tool.errMgr.getMessageTemplate(msg); + String outputMsg = msgST.render(); + if (tool.errMgr.formatWantsSingleLineMessage()) { + outputMsg = outputMsg.replace('\n', ' '); + } + System.err.println(outputMsg); + } + + @Override + public void warning(ANTLRMessage msg) { + ST msgST = tool.errMgr.getMessageTemplate(msg); + String outputMsg = msgST.render(); + if (tool.errMgr.formatWantsSingleLineMessage()) { + outputMsg = outputMsg.replace('\n', ' '); + } + System.err.println(outputMsg); + } +} diff --git a/tool/src/org/antlr/v4/tool/ErrorManager.java b/tool/src/org/antlr/v4/tool/ErrorManager.java new file mode 100644 index 0000000..983c41b --- /dev/null +++ b/tool/src/org/antlr/v4/tool/ErrorManager.java @@ -0,0 +1,322 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.stringtemplate.v4.ST; +import org.stringtemplate.v4.STGroup; +import org.stringtemplate.v4.STGroupFile; +import org.stringtemplate.v4.misc.ErrorBuffer; + +import java.io.File; +import java.net.URL; +import java.util.Collection; +import java.util.EnumSet; +import java.util.Locale; +import java.util.Set; + +public class ErrorManager { + public static final String FORMATS_DIR = "org/antlr/v4/tool/templates/messages/formats/"; + + public Tool tool; + public int errors; + public int warnings; + + /** All errors that have been generated */ + public Set<ErrorType> errorTypes = EnumSet.noneOf(ErrorType.class); + + /** The group of templates that represent the current message format. */ + STGroup format; + + /** Messages should be sensitive to the locale. */ + Locale locale; + String formatName; + + ErrorBuffer initSTListener = new ErrorBuffer(); + + public ErrorManager(Tool tool) { + this.tool = tool; + } + + public void resetErrorState() { + errors = 0; + warnings = 0; + } + + public ST getMessageTemplate(ANTLRMessage msg) { + ST messageST = msg.getMessageTemplate(tool.longMessages); + ST locationST = getLocationFormat(); + ST reportST = getReportFormat(msg.getErrorType().severity); + ST messageFormatST = getMessageFormat(); + + boolean locationValid = false; + if (msg.line != -1) { + locationST.add("line", msg.line); + locationValid = true; + } + if (msg.charPosition != -1) { + locationST.add("column", msg.charPosition); + locationValid = true; + } + if (msg.fileName != null) { + File f = new File(msg.fileName); + // Don't show path to file in messages; too long. + String displayFileName = msg.fileName; + if ( f.exists() ) { + displayFileName = f.getName(); + } + locationST.add("file", displayFileName); + locationValid = true; + } + + messageFormatST.add("id", msg.getErrorType().code); + messageFormatST.add("text", messageST); + + if (locationValid) reportST.add("location", locationST); + reportST.add("message", messageFormatST); + //((DebugST)reportST).inspect(); +// reportST.impl.dump(); + return reportST; + } + + /** Return a StringTemplate that refers to the current format used for + * emitting messages. + */ + public ST getLocationFormat() { + return format.getInstanceOf("location"); + } + + public ST getReportFormat(ErrorSeverity severity) { + ST st = format.getInstanceOf("report"); + st.add("type", severity.getText()); + return st; + } + + public ST getMessageFormat() { + return format.getInstanceOf("message"); + } + public boolean formatWantsSingleLineMessage() { + return format.getInstanceOf("wantsSingleLineMessage").render().equals("true"); + } + + public void info(String msg) { tool.info(msg); } + + public void syntaxError(ErrorType etype, + String fileName, + org.antlr.runtime.Token token, + org.antlr.runtime.RecognitionException antlrException, + Object... args) + { + ANTLRMessage msg = new GrammarSyntaxMessage(etype,fileName,token,antlrException,args); + emit(etype, msg); + } + + public static void fatalInternalError(String error, Throwable e) { + internalError(error, e); + throw new RuntimeException(error, e); + } + + public static void internalError(String error, Throwable e) { + StackTraceElement location = getLastNonErrorManagerCodeLocation(e); + internalError("Exception "+e+"@"+location+": "+error); + } + + public static void internalError(String error) { + StackTraceElement location = + getLastNonErrorManagerCodeLocation(new Exception()); + String msg = location+": "+error; + System.err.println("internal error: "+msg); + } + + /** + * Raise a predefined message with some number of paramters for the StringTemplate but for which there + * is no location information possible. + * @param errorType The Message Descriptor + * @param args The arguments to pass to the StringTemplate + */ + public void toolError(ErrorType errorType, Object... args) { + toolError(errorType, null, args); + } + + public void toolError(ErrorType errorType, Throwable e, Object... args) { + ToolMessage msg = new ToolMessage(errorType, e, args); + emit(errorType, msg); + } + + public void grammarError(ErrorType etype, + String fileName, + org.antlr.runtime.Token token, + Object... args) + { + ANTLRMessage msg = new GrammarSemanticsMessage(etype,fileName,token,args); + emit(etype, msg); + + } + + public void leftRecursionCycles(String fileName, Collection<? extends Collection<Rule>> cycles) { + errors++; + ANTLRMessage msg = new LeftRecursionCyclesMessage(fileName, cycles); + tool.error(msg); + } + + public int getNumErrors() { + return errors; + } + + /** Return first non ErrorManager code location for generating messages */ + private static StackTraceElement getLastNonErrorManagerCodeLocation(Throwable e) { + StackTraceElement[] stack = e.getStackTrace(); + int i = 0; + for (; i < stack.length; i++) { + StackTraceElement t = stack[i]; + if (!t.toString().contains("ErrorManager")) { + break; + } + } + StackTraceElement location = stack[i]; + return location; + } + + // S U P P O R T C O D E + + @SuppressWarnings("fallthrough") + public void emit(ErrorType etype, ANTLRMessage msg) { + switch ( etype.severity ) { + case WARNING_ONE_OFF: + if ( errorTypes.contains(etype) ) break; + // fall thru + case WARNING: + warnings++; + tool.warning(msg); + break; + case ERROR_ONE_OFF: + if ( errorTypes.contains(etype) ) break; + // fall thru + case ERROR: + errors++; + tool.error(msg); + break; + } + errorTypes.add(etype); + } + + /** The format gets reset either from the Tool if the user supplied a command line option to that effect + * Otherwise we just use the default "antlr". + */ + public void setFormat(String formatName) { + this.formatName = formatName; + String fileName = FORMATS_DIR +formatName+STGroup.GROUP_FILE_EXTENSION; + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + URL url = cl.getResource(fileName); + if ( url==null ) { + cl = ErrorManager.class.getClassLoader(); + url = cl.getResource(fileName); + } + if ( url==null && formatName.equals("antlr") ) { + rawError("ANTLR installation corrupted; cannot find ANTLR messages format file "+fileName); + panic(); + } + else if ( url==null ) { + rawError("no such message format file "+fileName+" retrying with default ANTLR format"); + setFormat("antlr"); // recurse on this rule, trying the default message format + return; + } + + format = new STGroupFile(fileName, "UTF-8"); + format.load(); + + if ( !initSTListener.errors.isEmpty() ) { + rawError("ANTLR installation corrupted; can't load messages format file:\n"+ + initSTListener.toString()); + panic(); + } + + boolean formatOK = verifyFormat(); + if ( !formatOK && formatName.equals("antlr") ) { + rawError("ANTLR installation corrupted; ANTLR messages format file "+formatName+".stg incomplete"); + panic(); + } + else if ( !formatOK ) { + setFormat("antlr"); // recurse on this rule, trying the default message format + } + } + + /** Verify the message format template group */ + protected boolean verifyFormat() { + boolean ok = true; + if (!format.isDefined("location")) { + System.err.println("Format template 'location' not found in " + formatName); + ok = false; + } + if (!format.isDefined("message")) { + System.err.println("Format template 'message' not found in " + formatName); + ok = false; + } + if (!format.isDefined("report")) { + System.err.println("Format template 'report' not found in " + formatName); + ok = false; + } + return ok; + } + + /** If there are errors during ErrorManager init, we have no choice + * but to go to System.err. + */ + static void rawError(String msg) { + System.err.println(msg); + } + + static void rawError(String msg, Throwable e) { + rawError(msg); + e.printStackTrace(System.err); + } + + public void panic(ErrorType errorType, Object... args) { + ToolMessage msg = new ToolMessage(errorType, args); + ST msgST = getMessageTemplate(msg); + String outputMsg = msgST.render(); + if ( formatWantsSingleLineMessage() ) { + outputMsg = outputMsg.replace('\n', ' '); + } + panic(outputMsg); + } + + public static void panic(String msg) { + rawError(msg); + panic(); + } + + public static void panic() { + // can't call tool.panic since there may be multiple tools; just + // one error manager + throw new Error("ANTLR ErrorManager panic"); + } +} diff --git a/tool/src/org/antlr/v4/tool/ErrorSeverity.java b/tool/src/org/antlr/v4/tool/ErrorSeverity.java new file mode 100644 index 0000000..f942d3e --- /dev/null +++ b/tool/src/org/antlr/v4/tool/ErrorSeverity.java @@ -0,0 +1,68 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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; + +/** + * Abstracts away the definition of Message severity and the text that should + * display to represent that severity if there is no StringTemplate available + * to do it. + * + * @author Jim Idle - Temporal Wave LLC (jimi@temporal-wave.com) + */ +public enum ErrorSeverity { + INFO ("info"), + WARNING ("warning"), + WARNING_ONE_OFF ("warning"), + ERROR ("error"), + ERROR_ONE_OFF ("error"), + FATAL ("fatal"), // TODO: add fatal for which phase? sync with ErrorManager + ; + + /** + * The text version of the ENUM value, used for display purposes + */ + private final String text; + + /** + * Standard getter method for the text that should be displayed in order to + * represent the severity to humans and product modelers. + * + * @return The human readable string representing the severity level + */ + public String getText() { return text; } + + /** + * Standard constructor to build an instance of the Enum entries + * + * @param text The human readable string representing the serverity level + */ + private ErrorSeverity(String text) { this.text = text; } +} + diff --git a/tool/src/org/antlr/v4/tool/ErrorType.java b/tool/src/org/antlr/v4/tool/ErrorType.java new file mode 100644 index 0000000..4868931 --- /dev/null +++ b/tool/src/org/antlr/v4/tool/ErrorType.java @@ -0,0 +1,1100 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.runtime.Lexer; + +/** + * A complex enumeration of all the error messages that the tool can issue. + * <p/> + * When adding error messages, also add a description of the message to the + * Wiki with a location under the Wiki page + * <a href="http://www.antlr.org/wiki/display/ANTLR4/Errors+Reported+by+the+ANTLR+Tool">Errors Reported by the ANTLR Tool</a>. + * + * @author Jim Idle <jimi@temporal-wave.com>, Terence Parr + * @since 4.0 + */ +public enum ErrorType { + /* + * Tool errors + */ + + /** + * Compiler Error 1. + * + * <p>cannot write file <em>filename</em>: <em>reason</em></p> + */ + CANNOT_WRITE_FILE(1, "cannot write file <arg>: <arg2>", ErrorSeverity.ERROR), + /** + * Compiler Error 2. + * + * <p>unknown command-line option <em>option</em></p> + */ + INVALID_CMDLINE_ARG(2, "unknown command-line option <arg>", ErrorSeverity.ERROR), + /** + * Compiler Error 3. + * + * <p>cannot find tokens file <em>filename</em></p> + */ + CANNOT_FIND_TOKENS_FILE_GIVEN_ON_CMDLINE(3, "cannot find tokens file <arg> given for <arg2>", ErrorSeverity.ERROR), + /** + * Compiler Error 4. + * + * <p>cannot find tokens file <em>filename</em>: <em>reason</em></p> + */ + ERROR_READING_TOKENS_FILE(4, "error reading tokens file <arg>: <arg2>", ErrorSeverity.ERROR), + /** + * Compiler Error 5. + * + * <p>directory not found: <em>directory</em></p> + */ + DIR_NOT_FOUND(5, "directory not found: <arg>", ErrorSeverity.ERROR), + /** + * Compiler Error 6. + * + * <p>output directory is a file: <em>filename</em></p> + */ + OUTPUT_DIR_IS_FILE(6, "output directory is a file: <arg>", ErrorSeverity.ERROR), + /** + * Compiler Error 7. + * + * <p>cannot find or open file: <em>filename</em></p> + */ + CANNOT_OPEN_FILE(7, "cannot find or open file: <arg><if(exception&&verbose)>; reason: <exception><endif>", ErrorSeverity.ERROR), + /** + * Compiler Error 8. + * + * <p> + * grammar name <em>name</em> and file name <em>filename</em> differ</p> + */ + FILE_AND_GRAMMAR_NAME_DIFFER(8, "grammar name <arg> and file name <arg2> differ", ErrorSeverity.ERROR), + /** + * Compiler Error 9. + * + * <p>invalid {@code -Dname=value} syntax: <em>syntax</em></p> + */ + BAD_OPTION_SET_SYNTAX(9, "invalid -Dname=value syntax: <arg>", ErrorSeverity.ERROR), + /** + * Compiler Error 10. + * + * <p>warning treated as error</p> + */ + WARNING_TREATED_AS_ERROR(10, "warning treated as error", ErrorSeverity.ERROR_ONE_OFF), + /** + * Compiler Error 11. + * + * <p>cannot find tokens file <em>filename</em>: <em>reason</em></p> + */ + ERROR_READING_IMPORTED_GRAMMAR(11, "error reading imported grammar <arg> referenced in <arg2>", ErrorSeverity.ERROR), + + /** + * Compiler Error 20. + * + * <p>internal error: <em>message</em></p> + */ + INTERNAL_ERROR(20, "internal error: <arg> <arg2><if(exception&&verbose)>: <exception>" + + "<stackTrace; separator=\"\\n\"><endif>", ErrorSeverity.ERROR), + /** + * Compiler Error 21. + * + * <p>.tokens file syntax error <em>filename</em>: <em>message</em></p> + */ + TOKENS_FILE_SYNTAX_ERROR(21, ".tokens file syntax error <arg>:<arg2>", ErrorSeverity.ERROR), + /** + * Compiler Warning 22. + * + * <p>template error: <em>message</em></p> + */ + STRING_TEMPLATE_WARNING(22, "template error: <arg> <arg2><if(exception&&verbose)>: <exception>" + + "<stackTrace; separator=\"\\n\"><endif>", ErrorSeverity.WARNING), + + /* + * Code generation errors + */ + + /** + * Compiler Error 30. + * + * <p>can't find code generation templates: <em>group</em></p> + */ + MISSING_CODE_GEN_TEMPLATES(30, "can't find code generation templates: <arg>", ErrorSeverity.ERROR), + /** + * Compiler Error 31. + * + * <p> + * ANTLR cannot generate <em>language</em> code as of version + * <em>version</em></p> + */ + CANNOT_CREATE_TARGET_GENERATOR(31, "ANTLR cannot generate <arg> code as of version "+ Tool.VERSION, ErrorSeverity.ERROR_ONE_OFF), + /** + * Compiler Error 32. + * + * <p> + * code generation template <em>template</em> has missing, misnamed, or + * incomplete arg list; missing <em>field</em></p> + */ + CODE_TEMPLATE_ARG_ISSUE(32, "code generation template <arg> has missing, misnamed, or incomplete arg list; missing <arg2>", ErrorSeverity.ERROR), + /** + * Compiler Error 33. + * + * <p>missing code generation template <em>template</em></p> + */ + CODE_GEN_TEMPLATES_INCOMPLETE(33, "missing code generation template <arg>", ErrorSeverity.ERROR), + /** + * Compiler Error 34. + * + * <p> + * no mapping to template name for output model class <em>class</em></p> + */ + NO_MODEL_TO_TEMPLATE_MAPPING(34, "no mapping to template name for output model class <arg>", ErrorSeverity.ERROR), + /** + * Compiler Error 35. + * + * <p>templates/target and tool aren't compatible</p> + */ + INCOMPATIBLE_TOOL_AND_TEMPLATES(35, "<arg3> code generation target requires ANTLR <arg2>; it can't be loaded by the current ANTLR <arg>", ErrorSeverity.ERROR), + + /* + * Grammar errors + */ + + /** + * Compiler Error 50. + * + * <p>syntax error: <em>message</em></p> + */ + SYNTAX_ERROR(50, "syntax error: <arg>", ErrorSeverity.ERROR), + /** + * Compiler Error 51. + * + * <p>rule <em>rule</em> redefinition; previous at line <em>line</em></p> + */ + RULE_REDEFINITION(51, "rule <arg> redefinition; previous at line <arg2>", ErrorSeverity.ERROR), + /** + * Compiler Error 52. + * + * <p>lexer rule <em>rule</em> not allowed in parser</p> + */ + LEXER_RULES_NOT_ALLOWED(52, "lexer rule <arg> not allowed in parser", ErrorSeverity.ERROR), + /** + * Compiler Error 53. + * + * <p>parser rule <em>rule</em> not allowed in lexer</p> + */ + PARSER_RULES_NOT_ALLOWED(53, "parser rule <arg> not allowed in lexer", ErrorSeverity.ERROR), + /** + * Compiler Error 54. + * + * <p> + * repeated grammar prequel spec ({@code options}, {@code tokens}, or + * {@code import}); please merge</p> + */ + REPEATED_PREQUEL(54, "repeated grammar prequel spec (options, tokens, or import); please merge", ErrorSeverity.ERROR), + /** + * Compiler Error 56. + * + * <p>reference to undefined rule: <em>rule</em></p> + * + * @see #PARSER_RULE_REF_IN_LEXER_RULE + */ + UNDEFINED_RULE_REF(56, "reference to undefined rule: <arg>", ErrorSeverity.ERROR), + /** + * Compiler Error 57. + * + * <p> + * reference to undefined rule <em>rule</em> in non-local ref + * <em>reference</em></p> + */ + UNDEFINED_RULE_IN_NONLOCAL_REF(57, "reference to undefined rule <arg> in non-local ref <arg3>", ErrorSeverity.ERROR), + /** + * Compiler Error 60. + * + * <p>token names must start with an uppercase letter: <em>name</em></p> + */ + TOKEN_NAMES_MUST_START_UPPER(60, "token names must start with an uppercase letter: <arg>", ErrorSeverity.ERROR), + /** + * Compiler Error 63. + * + * <p> + * unknown attribute reference <em>attribute</em> in + * <em>expression</em></p> + */ + UNKNOWN_SIMPLE_ATTRIBUTE(63, "unknown attribute reference <arg> in <arg2>", ErrorSeverity.ERROR), + /** + * Compiler Error 64. + * + * <p> + * parameter <em>parameter</em> of rule <em>rule</em> is not accessible + * in this scope: <em>expression</em></p> + */ + INVALID_RULE_PARAMETER_REF(64, "parameter <arg> of rule <arg2> is not accessible in this scope: <arg3>", ErrorSeverity.ERROR), + /** + * Compiler Error 65. + * + * <p> + * unknown attribute <em>attribute</em> for rule <em>rule</em> in + * <em>expression</em></p> + */ + UNKNOWN_RULE_ATTRIBUTE(65, "unknown attribute <arg> for rule <arg2> in <arg3>", ErrorSeverity.ERROR), + /** + * Compiler Error 66. + * + * <p> + * attribute <em>attribute</em> isn't a valid property in + * <em>expression</em></p> + */ + UNKNOWN_ATTRIBUTE_IN_SCOPE(66, "attribute <arg> isn't a valid property in <arg2>", ErrorSeverity.ERROR), + /** + * Compiler Error 67. + * + * <p> + * missing attribute access on rule reference <em>rule</em> in + * <em>expression</em></p> + */ + ISOLATED_RULE_REF(67, "missing attribute access on rule reference <arg> in <arg2>", ErrorSeverity.ERROR), + /** + * Compiler Error 69. + * + * <p>label <em>label</em> conflicts with rule with same name</p> + */ + LABEL_CONFLICTS_WITH_RULE(69, "label <arg> conflicts with rule with same name", ErrorSeverity.ERROR), + /** + * Compiler Error 70. + * + * <p>label <em>label</em> conflicts with token with same name</p> + */ + LABEL_CONFLICTS_WITH_TOKEN(70, "label <arg> conflicts with token with same name", ErrorSeverity.ERROR), + /** + * Compiler Error 72. + * + * <p>label <em>label</em> conflicts with parameter with same name</p> + */ + LABEL_CONFLICTS_WITH_ARG(72, "label <arg> conflicts with parameter with same name", ErrorSeverity.ERROR), + /** + * Compiler Error 73. + * + * <p>label <em>label</em> conflicts with return value with same name</p> + */ + LABEL_CONFLICTS_WITH_RETVAL(73, "label <arg> conflicts with return value with same name", ErrorSeverity.ERROR), + /** + * Compiler Error 74. + * + * <p>label <em>label</em> conflicts with local with same name</p> + */ + LABEL_CONFLICTS_WITH_LOCAL(74, "label <arg> conflicts with local with same name", ErrorSeverity.ERROR), + /** + * Compiler Error 75. + * + * <p> + * label <em>label</em> type mismatch with previous definition: + * <em>message</em></p> + */ + LABEL_TYPE_CONFLICT(75, "label <arg> type mismatch with previous definition: <arg2>", ErrorSeverity.ERROR), + /** + * Compiler Error 76. + * + * <p> + * return value <em>name</em> conflicts with parameter with same name</p> + */ + RETVAL_CONFLICTS_WITH_ARG(76, "return value <arg> conflicts with parameter with same name", ErrorSeverity.ERROR), + /** + * Compiler Error 79. + * + * <p>missing arguments(s) on rule reference: <em>rule</em></p> + */ + MISSING_RULE_ARGS(79, "missing arguments(s) on rule reference: <arg>", ErrorSeverity.ERROR), + /** + * Compiler Error 80. + * + * <p>rule <em>rule</em> has no defined parameters</p> + */ + RULE_HAS_NO_ARGS(80, "rule <arg> has no defined parameters", ErrorSeverity.ERROR), + /** + * Compiler Warning 83. + * + * <p>unsupported option <em>option</em></p> + */ + ILLEGAL_OPTION(83, "unsupported option <arg>", ErrorSeverity.WARNING), + /** + * Compiler Warning 84. + * + * <p>unsupported option value <em>name</em>=<em>value</em></p> + */ + ILLEGAL_OPTION_VALUE(84, "unsupported option value <arg>=<arg2>", ErrorSeverity.WARNING), + /** + * Compiler Error 94. + * + * <p>redefinition of <em>action</em> action</p> + */ + ACTION_REDEFINITION(94, "redefinition of <arg> action", ErrorSeverity.ERROR), + /** + * Compiler Error 99. + * + * <p>This error may take any of the following forms.</p> + * + * <ul> + * <li>grammar <em>grammar</em> has no rules</li> + * <li>implicitly generated grammar <em>grammar</em> has no rules</li> + * </ul> + */ + NO_RULES(99, "<if(arg2.implicitLexerOwner)>implicitly generated <endif>grammar <arg> has no rules", ErrorSeverity.ERROR), + /** + * Compiler Error 105. + * + * <p> + * reference to undefined grammar in rule reference: + * <em>grammar</em>.<em>rule</em></p> + */ + NO_SUCH_GRAMMAR_SCOPE(105, "reference to undefined grammar in rule reference: <arg>.<arg2>", ErrorSeverity.ERROR), + /** + * Compiler Error 106. + * + * <p>rule <em>rule</em> is not defined in grammar <em>grammar</em></p> + */ + NO_SUCH_RULE_IN_SCOPE(106, "rule <arg2> is not defined in grammar <arg>", ErrorSeverity.ERROR), + /** + * Compiler Warning 108. + * + * <p>token name <em>Token</em> is already defined</p> + */ + TOKEN_NAME_REASSIGNMENT(108, "token name <arg> is already defined", ErrorSeverity.WARNING), + /** + * Compiler Warning 109. + * + * <p>options ignored in imported grammar <em>grammar</em></p> + */ + OPTIONS_IN_DELEGATE(109, "options ignored in imported grammar <arg>", ErrorSeverity.WARNING), + /** + * Compiler Error 110. + * + * <p> + * can't find or load grammar <em>grammar</em> from + * <em>filename</em></p> + */ + CANNOT_FIND_IMPORTED_GRAMMAR(110, "can't find or load grammar <arg>", ErrorSeverity.ERROR), + /** + * Compiler Error 111. + * + * <p> + * <em>grammartype</em> grammar <em>grammar1</em> cannot import + * <em>grammartype</em> grammar <em>grammar2</em></p> + */ + INVALID_IMPORT(111, "<arg.typeString> grammar <arg.name> cannot import <arg2.typeString> grammar <arg2.name>", ErrorSeverity.ERROR), + /** + * Compiler Error 113. + * + * <p> + * <em>grammartype</em> grammar <em>grammar1</em> and imported + * <em>grammartype</em> grammar <em>grammar2</em> both generate + * <em>recognizer</em></p> + */ + IMPORT_NAME_CLASH(113, "<arg.typeString> grammar <arg.name> and imported <arg2.typeString> grammar <arg2.name> both generate <arg2.recognizerName>", ErrorSeverity.ERROR), + /** + * Compiler Error 160. + * + * <p>cannot find tokens file <em>filename</em></p> + */ + CANNOT_FIND_TOKENS_FILE_REFD_IN_GRAMMAR(160, "cannot find tokens file <arg>", ErrorSeverity.ERROR), + /** + * Compiler Warning 118. + * + * <p> + * all operators of alt <em>alt</em> of left-recursive rule must have same + * associativity</p> + * + * @deprecated This warning is no longer applicable with the current syntax for specifying associativity. + */ + @Deprecated + ALL_OPS_NEED_SAME_ASSOC(118, "all operators of alt <arg> of left-recursive rule must have same associativity", ErrorSeverity.WARNING), + /** + * Compiler Error 119. + * + * <p> + * The following sets of rules are mutually left-recursive + * <em>[rules]</em></p> + */ + LEFT_RECURSION_CYCLES(119, "The following sets of rules are mutually left-recursive <arg:{c| [<c:{r|<r.name>}; separator=\", \">]}; separator=\" and \">", ErrorSeverity.ERROR), + /** + * Compiler Error 120. + * + * <p>lexical modes are only allowed in lexer grammars</p> + */ + MODE_NOT_IN_LEXER(120, "lexical modes are only allowed in lexer grammars", ErrorSeverity.ERROR), + /** + * Compiler Error 121. + * + * <p>cannot find an attribute name in attribute declaration</p> + */ + CANNOT_FIND_ATTRIBUTE_NAME_IN_DECL(121, "cannot find an attribute name in attribute declaration", ErrorSeverity.ERROR), + /** + * Compiler Error 122. + * + * <p>rule <em>rule</em>: must label all alternatives or none</p> + */ + RULE_WITH_TOO_FEW_ALT_LABELS(122, "rule <arg>: must label all alternatives or none", ErrorSeverity.ERROR), + /** + * Compiler Error 123. + * + * <p> + * rule alt label <em>label</em> redefined in rule <em>rule1</em>, + * originally in rule <em>rule2</em></p> + */ + ALT_LABEL_REDEF(123, "rule alt label <arg> redefined in rule <arg2>, originally in rule <arg3>", ErrorSeverity.ERROR), + /** + * Compiler Error 124. + * + * <p> + * rule alt label <em>label</em> conflicts with rule <em>rule</em></p> + */ + ALT_LABEL_CONFLICTS_WITH_RULE(124, "rule alt label <arg> conflicts with rule <arg2>", ErrorSeverity.ERROR), + /** + * Compiler Warning 125. + * + * <p>implicit definition of token <em>Token</em> in parser</p> + */ + IMPLICIT_TOKEN_DEFINITION(125, "implicit definition of token <arg> in parser", ErrorSeverity.WARNING), + /** + * Compiler Error 126. + * + * <p> + * cannot create implicit token for string literal in non-combined grammar: + * <em>literal</em></p> + */ + IMPLICIT_STRING_DEFINITION(126, "cannot create implicit token for string literal in non-combined grammar: <arg>", ErrorSeverity.ERROR), + /** + * Compiler Error 128. + * + * <p> + * attribute references not allowed in lexer actions: + * <em>expression</em></p> + */ + ATTRIBUTE_IN_LEXER_ACTION(128, "attribute references not allowed in lexer actions: $<arg>", ErrorSeverity.ERROR), + /** + * Compiler Error 130. + * + * <p>label <em>label</em> assigned to a block which is not a set</p> + */ + LABEL_BLOCK_NOT_A_SET(130, "label <arg> assigned to a block which is not a set", ErrorSeverity.ERROR), + /** + * Compiler Warning 131. + * + * <p>This warning may take any of the following forms.</p> + * + * <ul> + * <li>greedy block {@code ()*} contains wildcard; the non-greedy syntax {@code ()*?} may be preferred</li> + * <li>greedy block {@code ()+} contains wildcard; the non-greedy syntax {@code ()+?} may be preferred</li> + * </ul> + */ + EXPECTED_NON_GREEDY_WILDCARD_BLOCK(131, "greedy block ()<arg> contains wildcard; the non-greedy syntax ()<arg>? may be preferred", ErrorSeverity.WARNING), + /** + * Compiler Error 132. + * + * <p> + * action in lexer rule <em>rule</em> must be last element of single + * outermost alt</p> + * + * @deprecated This error is no longer issued by ANTLR 4.2. + */ + @Deprecated + LEXER_ACTION_PLACEMENT_ISSUE(132, "action in lexer rule <arg> must be last element of single outermost alt", ErrorSeverity.ERROR), + /** + * Compiler Error 133. + * + * <p> + * {@code ->command} in lexer rule <em>rule</em> must be last element of + * single outermost alt</p> + */ + LEXER_COMMAND_PLACEMENT_ISSUE(133, "->command in lexer rule <arg> must be last element of single outermost alt", ErrorSeverity.ERROR), + /** + * Compiler Error 134. + * + * <p> + * symbol <em>symbol</em> conflicts with generated code in target language + * or runtime</p> + * + * <p> + * Note: This error has the same number as the unrelated error + * {@link #UNSUPPORTED_REFERENCE_IN_LEXER_SET}.</p> + */ + USE_OF_BAD_WORD(134, "symbol <arg> conflicts with generated code in target language or runtime", ErrorSeverity.ERROR), + /** + * Compiler Error 134. + * + * <p>rule reference <em>rule</em> is not currently supported in a set</p> + * + * <p> + * Note: This error has the same number as the unrelated error + * {@link #USE_OF_BAD_WORD}.</p> + */ + UNSUPPORTED_REFERENCE_IN_LEXER_SET(134, "rule reference <arg> is not currently supported in a set", ErrorSeverity.ERROR), + /** + * Compiler Error 135. + * + * <p>cannot assign a value to list label <em>label</em></p> + */ + ASSIGNMENT_TO_LIST_LABEL(135, "cannot assign a value to list label <arg>", ErrorSeverity.ERROR), + /** + * Compiler Error 136. + * + * <p>return value <em>name</em> conflicts with rule with same name</p> + */ + RETVAL_CONFLICTS_WITH_RULE(136, "return value <arg> conflicts with rule with same name", ErrorSeverity.ERROR), + /** + * Compiler Error 137. + * + * <p>return value <em>name</em> conflicts with token with same name</p> + */ + RETVAL_CONFLICTS_WITH_TOKEN(137, "return value <arg> conflicts with token with same name", ErrorSeverity.ERROR), + /** + * Compiler Error 138. + * + * <p>parameter <em>parameter</em> conflicts with rule with same name</p> + */ + ARG_CONFLICTS_WITH_RULE(138, "parameter <arg> conflicts with rule with same name", ErrorSeverity.ERROR), + /** + * Compiler Error 139. + * + * <p>parameter <em>parameter</em> conflicts with token with same name</p> + */ + ARG_CONFLICTS_WITH_TOKEN(139, "parameter <arg> conflicts with token with same name", ErrorSeverity.ERROR), + /** + * Compiler Error 140. + * + * <p>local <em>local</em> conflicts with rule with same name</p> + */ + LOCAL_CONFLICTS_WITH_RULE(140, "local <arg> conflicts with rule with same name", ErrorSeverity.ERROR), + /** + * Compiler Error 141. + * + * <p>local <em>local</em> conflicts with rule token same name</p> + */ + LOCAL_CONFLICTS_WITH_TOKEN(141, "local <arg> conflicts with rule token same name", ErrorSeverity.ERROR), + /** + * Compiler Error 142. + * + * <p>local <em>local</em> conflicts with parameter with same name</p> + */ + LOCAL_CONFLICTS_WITH_ARG(142, "local <arg> conflicts with parameter with same name", ErrorSeverity.ERROR), + /** + * Compiler Error 143. + * + * <p>local <em>local</em> conflicts with return value with same name</p> + */ + LOCAL_CONFLICTS_WITH_RETVAL(143, "local <arg> conflicts with return value with same name", ErrorSeverity.ERROR), + /** + * Compiler Error 144. + * + * <p> + * multi-character literals are not allowed in lexer sets: + * <em>literal</em></p> + */ + INVALID_LITERAL_IN_LEXER_SET(144, "multi-character literals are not allowed in lexer sets: <arg>", ErrorSeverity.ERROR), + /** + * Compiler Error 145. + * + * <p> + * lexer mode <em>mode</em> must contain at least one non-fragment + * rule</p> + * + * <p> + * Every lexer mode must contain at least one rule which is not declared + * with the {@code fragment} modifier.</p> + */ + MODE_WITHOUT_RULES(145, "lexer mode <arg> must contain at least one non-fragment rule", ErrorSeverity.ERROR), + /** + * Compiler Warning 146. + * + * <p>non-fragment lexer rule <em>rule</em> can match the empty string</p> + * + * <p>All non-fragment lexer rules must match at least one character.</p> + * + * <p>The following example shows this error.</p> + * + * <pre> + * Whitespace : [ \t]+; // ok + * Whitespace : [ \t]; // ok + * + * fragment WS : [ \t]*; // ok + * + * Whitespace : [ \t]*; // error 146 + * </pre> + */ + EPSILON_TOKEN(146, "non-fragment lexer rule <arg> can match the empty string", ErrorSeverity.WARNING), + /** + * Compiler Error 147. + * + * <p> + * left recursive rule <em>rule</em> must contain an alternative which is + * not left recursive</p> + * + * <p>Left-recursive rules must contain at least one alternative which is not + * left recursive.</p> + * + * <p>The following rule produces this error.</p> + * + * <pre> + * // error 147: + * a : a ID + * | a INT + * ; + * </pre> + */ + NO_NON_LR_ALTS(147, "left recursive rule <arg> must contain an alternative which is not left recursive", ErrorSeverity.ERROR), + /** + * Compiler Error 148. + * + * <p> + * left recursive rule <em>rule</em> contains a left recursive alternative + * which can be followed by the empty string</p> + * + * <p>In left-recursive rules, all left-recursive alternatives must match at + * least one symbol following the recursive rule invocation.</p> + * + * <p>The following rule produces this error.</p> + * + * <pre> + * a : ID // ok (alternative is not left recursive) + * | a INT // ok (a must be follow by INT) + * | a ID? // error 148 (the ID following a is optional) + * ; + * </pre> + */ + EPSILON_LR_FOLLOW(148, "left recursive rule <arg> contains a left recursive alternative which can be followed by the empty string", ErrorSeverity.ERROR), + /** + * Compiler Error 149. + * + * <p> + * lexer command <em>command</em> does not exist or is not supported by + * the current target</p> + * + * <p>Each lexer command requires an explicit implementation in the target + * templates. This error indicates that the command was incorrectly written + * or is not supported by the current target.</p> + * + * <p>The following rule produces this error.</p> + * + * <pre> + * X : 'foo' -> type(Foo); // ok + * Y : 'foo' -> token(Foo); // error 149 (token is not a supported lexer command) + * </pre> + * + * @since 4.1 + */ + INVALID_LEXER_COMMAND(149, "lexer command <arg> does not exist or is not supported by the current target", ErrorSeverity.ERROR), + /** + * Compiler Error 150. + * + * <p>missing argument for lexer command <em>command</em></p> + * + * <p>Some lexer commands require an argument.</p> + * + * <p>The following rule produces this error.</p> + * + * <pre> + * X : 'foo' -> type(Foo); // ok + * Y : 'foo' -> type; // error 150 (the type command requires an argument) + * </pre> + * + * @since 4.1 + */ + MISSING_LEXER_COMMAND_ARGUMENT(150, "missing argument for lexer command <arg>", ErrorSeverity.ERROR), + /** + * Compiler Error 151. + * + * <p>lexer command <em>command</em> does not take any arguments</p> + * + * <p>A lexer command which does not take parameters was invoked with an + * argument.</p> + * + * <p>The following rule produces this error.</p> + * + * <pre> + * X : 'foo' -> popMode; // ok + * Y : 'foo' -> popMode(A); // error 151 (the popMode command does not take an argument) + * </pre> + * + * @since 4.1 + */ + UNWANTED_LEXER_COMMAND_ARGUMENT(151, "lexer command <arg> does not take any arguments", ErrorSeverity.ERROR), + /** + * Compiler Error 152. + * + * <p>unterminated string literal</p> + * + * <p>The grammar contains an unterminated string literal.</p> + * + * <p>The following rule produces this error.</p> + * + * <pre> + * x : 'x'; // ok + * y : 'y'; // error 152 + * </pre> + * + * @since 4.1 + */ + UNTERMINATED_STRING_LITERAL(152, "unterminated string literal", ErrorSeverity.ERROR), + /** + * Compiler Error 153. + * + * <p> + * rule <em>rule</em> contains a closure with at least one alternative + * that can match an empty string</p> + * + * <p>A rule contains a closure ({@code (...)*}) or positive closure + * ({@code (...)+}) around an empty alternative.</p> + * + * <p>The following rule produces this error.</p> + * + * <pre> + * x : ; + * y : x+; // error 153 + * z1 : ('foo' | 'bar'? 'bar2'?)*; // error 153 + * z2 : ('foo' | 'bar' 'bar2'? | 'bar2')*; // ok + * </pre> + * + * @since 4.1 + */ + EPSILON_CLOSURE(153, "rule <arg> contains a closure with at least one alternative that can match an empty string", ErrorSeverity.ERROR), + /** + * Compiler Warning 154. + * + * <p> + * rule <em>rule</em> contains an optional block with at least one + * alternative that can match an empty string</p> + * + * <p>A rule contains an optional block ({@code (...)?}) around an empty + * alternative.</p> + * + * <p>The following rule produces this warning.</p> + * + * <pre> + * x : ; + * y : x?; // warning 154 + * z1 : ('foo' | 'bar'? 'bar2'?)?; // warning 154 + * z2 : ('foo' | 'bar' 'bar2'? | 'bar2')?; // ok + * </pre> + * + * @since 4.1 + */ + EPSILON_OPTIONAL(154, "rule <arg> contains an optional block with at least one alternative that can match an empty string", ErrorSeverity.WARNING), + /** + * Compiler Warning 155. + * + * <p> + * rule <em>rule</em> contains a lexer command with an unrecognized + * constant value; lexer interpreters may produce incorrect output</p> + * + * <p>A lexer rule contains a standard lexer command, but the constant value + * argument for the command is an unrecognized string. As a result, the + * lexer command will be translated as a custom lexer action, preventing the + * command from executing in some interpreted modes. The output of the lexer + * interpreter may not match the output of the generated lexer.</p> + * + * <p>The following rule produces this warning.</p> + * + * <pre> + * @members { + * public static final int CUSTOM = HIDDEN + 1; + * } + * + * X : 'foo' -> channel(HIDDEN); // ok + * Y : 'bar' -> channel(CUSTOM); // warning 155 + * </pre> + * + * @since 4.2 + */ + UNKNOWN_LEXER_CONSTANT(155, "rule <arg> contains a lexer command with an unrecognized constant value; lexer interpreters may produce incorrect output", ErrorSeverity.WARNING), + /** + * Compiler Error 156. + * + * <p>invalid escape sequence</p> + * + * <p>The grammar contains a string literal with an invalid escape sequence.</p> + * + * <p>The following rule produces this error.</p> + * + * <pre> + * x : 'x'; // ok + * y : '\u005Cu'; // error 156 + * </pre> + * + * @since 4.2.1 + */ + INVALID_ESCAPE_SEQUENCE(156, "invalid escape sequence", ErrorSeverity.ERROR), + /** + * Compiler Warning 157. + * + * <p>rule <em>rule</em> contains an assoc element option in an + * unrecognized location</p> + * + * <p> + * In ANTLR 4.2, the position of the {@code assoc} element option was moved + * from the operator terminal(s) to the alternative itself. This warning is + * reported when an {@code assoc} element option is specified on a grammar + * element that is not recognized by the current version of ANTLR, and as a + * result will simply be ignored. + * </p> + * + * <p>The following rule produces this warning.</p> + * + * <pre> + * x : 'x' + * | x '+'<assoc=right> x // warning 157 + * |<assoc=right> x * x // ok + * ; + * </pre> + * + * @since 4.2.1 + */ + UNRECOGNIZED_ASSOC_OPTION(157, "rule <arg> contains an assoc terminal option in an unrecognized location", ErrorSeverity.WARNING), + /** + * Compiler Warning 158. + * + * <p>fragment rule <em>rule</em> contains an action or command which can + * never be executed</p> + * + * <p>A lexer rule which is marked with the {@code fragment} modifier + * contains an embedded action or lexer command. ANTLR lexers only execute + * commands and embedded actions located in the top-level matched rule. + * Since fragment rules can never be the top-level rule matched by a lexer, + * actions or commands placed in these rules can never be executed during + * the lexing process.</p> + * + * <p>The following rule produces this warning.</p> + * + * <pre> + * X1 : 'x' -> more // ok + * ; + * Y1 : 'x' {more();} // ok + * ; + * fragment + * X2 : 'x' -> more // warning 158 + * ; + * fragment + * Y2 : 'x' {more();} // warning 158 + * ; + * </pre> + * + * @since 4.2.1 + */ + FRAGMENT_ACTION_IGNORED(158, "fragment rule <arg> contains an action or command which can never be executed", ErrorSeverity.WARNING), + /** + * Compiler Error 159. + * + * <p>cannot declare a rule with reserved name <em>rule</em></p> + * + * <p>A rule was declared with a reserved name.</p> + * + * <p>The following rule produces this error.</p> + * + * <pre> + * EOF : ' ' // error 159 (EOF is a reserved name) + * ; + * </pre> + * + * @since 4.2.1 + */ + RESERVED_RULE_NAME(159, "cannot declare a rule with reserved name <arg>", ErrorSeverity.ERROR), + /** + * Compiler Error 160. + * + * <p>reference to parser rule <em>rule</em> in lexer rule <em>name</em></p> + * + * @see #UNDEFINED_RULE_REF + */ + PARSER_RULE_REF_IN_LEXER_RULE(160, "reference to parser rule <arg> in lexer rule <arg2>", ErrorSeverity.ERROR), + /** + * Compiler Error 161. + * + * <p>channel <em>name</em> conflicts with token with same name</p> + */ + CHANNEL_CONFLICTS_WITH_TOKEN(161, "channel <arg> conflicts with token with same name", ErrorSeverity.ERROR), + /** + * Compiler Error 162. + * + * <p>channel <em>name</em> conflicts with mode with same name</p> + */ + CHANNEL_CONFLICTS_WITH_MODE(162, "channel <arg> conflicts with mode with same name", ErrorSeverity.ERROR), + /** + * Compiler Error 163. + * + * <p>custom channels are not supported in parser grammars</p> + */ + CHANNELS_BLOCK_IN_PARSER_GRAMMAR(163, "custom channels are not supported in parser grammars", ErrorSeverity.ERROR), + /** + * Compiler Error 164. + * + * <p>custom channels are not supported in combined grammars</p> + */ + CHANNELS_BLOCK_IN_COMBINED_GRAMMAR(164, "custom channels are not supported in combined grammars", ErrorSeverity.ERROR), + + NONCONFORMING_LR_RULE(169, "rule <arg> is left recursive but doesn't conform to a pattern ANTLR can handle", ErrorSeverity.ERROR), + + /* + * Backward incompatibility errors + */ + + /** + * Compiler Error 200. + * + * <p>tree grammars are not supported in ANTLR 4</p> + * + * <p> + * This error message is provided as a compatibility notice for users + * migrating from ANTLR 3. ANTLR 4 does not support tree grammars, but + * instead offers automatically generated parse tree listeners and visitors + * as a more maintainable alternative.</p> + */ + V3_TREE_GRAMMAR(200, "tree grammars are not supported in ANTLR 4", ErrorSeverity.ERROR), + /** + * Compiler Warning 201. + * + * <p> + * labels in lexer rules are not supported in ANTLR 4; actions cannot + * reference elements of lexical rules but you can use + * {@link Lexer#getText()} to get the entire text matched for the rule</p> + * + * <p> + * ANTLR 4 uses a DFA for recognition of entire tokens, resulting in faster + * and smaller lexers than ANTLR 3 produced. As a result, sub-rules + * referenced within lexer rules are not tracked independently, and cannot + * be assigned to labels.</p> + */ + V3_LEXER_LABEL(201, "labels in lexer rules are not supported in ANTLR 4; " + + "actions cannot reference elements of lexical rules but you can use " + + "getText() to get the entire text matched for the rule", ErrorSeverity.WARNING), + /** + * Compiler Warning 202. + * + * <p> + * {@code tokens {A; B;}} syntax is now {@code tokens {A, B}} in ANTLR + * 4</p> + * + * <p> + * ANTLR 4 uses comma-separated token declarations in the {@code tokens{}} + * block. This warning appears when the tokens block is written using the + * ANTLR 3 syntax of semicolon-terminated token declarations.</p> + * + * <p> + * <strong>NOTE:</strong> ANTLR 4 does not allow a trailing comma to appear following the + * last token declared in the {@code tokens{}} block.</p> + */ + V3_TOKENS_SYNTAX(202, "tokens {A; B;} syntax is now tokens {A, B} in ANTLR 4", ErrorSeverity.WARNING), + /** + * Compiler Error 203. + * + * <p> + * assignments in {@code tokens{}} are not supported in ANTLR 4; use lexical + * rule <em>TokenName</em> : <em>LiteralValue</em>; instead</p> + * + * <p> + * ANTLR 3 allowed literal tokens to be declared and assigned a value within + * the {@code tokens{}} block. ANTLR 4 no longer offers this syntax. When + * migrating a grammar from ANTLR 3 to ANTLR 4, any tokens with a literal + * value declared in the {@code tokens{}} block should be converted to + * standard lexer rules.</p> + */ + V3_ASSIGN_IN_TOKENS(203, "assignments in tokens{} are not supported in ANTLR 4; use lexical rule <arg> : <arg2>; instead", ErrorSeverity.ERROR), + /** + * Compiler Warning 204. + * + * <p> + * {@code {...}?=>} explicitly gated semantic predicates are deprecated in + * ANTLR 4; use {@code {...}?} instead</p> + * + * <p> + * ANTLR 4 treats semantic predicates consistently in a manner similar to + * gated semantic predicates in ANTLR 3. When migrating a grammar from ANTLR + * 3 to ANTLR 4, all uses of the gated semantic predicate syntax can be + * safely converted to the standard semantic predicated syntax, which is the + * only form used by ANTLR 4.</p> + */ + V3_GATED_SEMPRED(204, "{...}?=> explicitly gated semantic predicates are deprecated in ANTLR 4; use {...}? instead", ErrorSeverity.WARNING), + /** + * Compiler Error 205. + * + * <p>{@code (...)=>} syntactic predicates are not supported in ANTLR 4</p> + * + * <p> + * ANTLR 4's improved lookahead algorithms do not require the use of + * syntactic predicates to disambiguate long lookahead sequences. The + * syntactic predicates should be removed when migrating a grammar from + * ANTLR 3 to ANTLR 4.</p> + */ + V3_SYNPRED(205, "(...)=> syntactic predicates are not supported in ANTLR 4", ErrorSeverity.ERROR), + + // Dependency sorting errors + + /** t1.g4 -> t2.g4 -> t3.g4 ->t1.g4 */ + //CIRCULAR_DEPENDENCY(200, "your grammars contain a circular dependency and cannot be sorted into a valid build order", ErrorSeverity.ERROR), + ; + + /** + * The error or warning message, in StringTemplate 4 format using {@code <} + * and {@code >} as the delimiters. Arguments for the message may be + * referenced using the following names: + * + * <ul> + * <li>{@code arg}: The first template argument</li> + * <li>{@code arg2}: The second template argument</li> + * <li>{@code arg3}: The third template argument</li> + * <li>{@code verbose}: {@code true} if verbose messages were requested; otherwise, {@code false}</li> + * <li>{@code exception}: The exception which resulted in the error, if any.</li> + * <li>{@code stackTrace}: The stack trace for the exception, when available.</li> + * </ul> + */ + public final String msg; + /** + * The error or warning number. + * + * <p>The code should be unique, and following its + * use in a release should not be altered or reassigned.</p> + */ + public final int code; + /** + * The error severity. + */ + public final ErrorSeverity severity; + + /** + * Constructs a new {@link ErrorType} with the specified code, message, and + * severity. + * + * @param code The unique error number. + * @param msg The error message template. + * @param severity The error severity. + */ + ErrorType(int code, String msg, ErrorSeverity severity) { + this.code = code; + this.msg = msg; + this.severity = severity; + } +} diff --git a/tool/src/org/antlr/v4/tool/Grammar.java b/tool/src/org/antlr/v4/tool/Grammar.java new file mode 100644 index 0000000..5632ab4 --- /dev/null +++ b/tool/src/org/antlr/v4/tool/Grammar.java @@ -0,0 +1,1343 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.analysis.LeftRecursiveRuleTransformer; +import org.antlr.v4.automata.ParserATNFactory; +import org.antlr.v4.misc.CharSupport; +import org.antlr.v4.misc.OrderedHashMap; +import org.antlr.v4.misc.Utils; +import org.antlr.v4.parse.ANTLRParser; +import org.antlr.v4.parse.GrammarASTAdaptor; +import org.antlr.v4.parse.GrammarTreeVisitor; +import org.antlr.v4.parse.TokenVocabParser; +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.Lexer; +import org.antlr.v4.runtime.LexerInterpreter; +import org.antlr.v4.runtime.ParserInterpreter; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.TokenStream; +import org.antlr.v4.runtime.Vocabulary; +import org.antlr.v4.runtime.VocabularyImpl; +import org.antlr.v4.runtime.atn.ATN; +import org.antlr.v4.runtime.atn.ATNDeserializer; +import org.antlr.v4.runtime.atn.ATNSerializer; +import org.antlr.v4.runtime.atn.SemanticContext; +import org.antlr.v4.runtime.dfa.DFA; +import org.antlr.v4.runtime.misc.IntSet; +import org.antlr.v4.runtime.misc.Interval; +import org.antlr.v4.runtime.misc.IntervalSet; +import org.antlr.v4.runtime.misc.Pair; +import org.antlr.v4.tool.ast.ActionAST; +import org.antlr.v4.tool.ast.GrammarAST; +import org.antlr.v4.tool.ast.GrammarASTWithOptions; +import org.antlr.v4.tool.ast.GrammarRootAST; +import org.antlr.v4.tool.ast.PredAST; +import org.antlr.v4.tool.ast.RuleAST; +import org.antlr.v4.tool.ast.TerminalAST; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class Grammar implements AttributeResolver { + public static final String GRAMMAR_FROM_STRING_NAME = "<string>"; + /** + * This value is used in the following situations to indicate that a token + * type does not have an associated name which can be directly referenced in + * a grammar. + * + * <ul> + * <li>This value is the name and display name for the token with type + * {@link Token#INVALID_TYPE}.</li> + * <li>This value is the name for tokens with a type not represented by a + * named token. The display name for these tokens is simply the string + * representation of the token type as an integer.</li> + * </ul> + */ + public static final String INVALID_TOKEN_NAME = "<INVALID>"; + /** + * This value is used as the name for elements in the array returned by + * {@link #getRuleNames} for indexes not associated with a rule. + */ + public static final String INVALID_RULE_NAME = "<invalid>"; + + public static final Set<String> parserOptions = new HashSet<String>(); + static { + parserOptions.add("superClass"); + parserOptions.add("TokenLabelType"); + parserOptions.add("tokenVocab"); + parserOptions.add("language"); + } + + public static final Set<String> lexerOptions = parserOptions; + + public static final Set<String> ruleOptions = new HashSet<String>(); + + public static final Set<String> ParserBlockOptions = new HashSet<String>(); + + public static final Set<String> LexerBlockOptions = new HashSet<String>(); + + /** Legal options for rule refs like id<key=value> */ + public static final Set<String> ruleRefOptions = new HashSet<String>(); + static { + ruleRefOptions.add(LeftRecursiveRuleTransformer.PRECEDENCE_OPTION_NAME); + ruleRefOptions.add(LeftRecursiveRuleTransformer.TOKENINDEX_OPTION_NAME); + } + + /** Legal options for terminal refs like ID<assoc=right> */ + public static final Set<String> tokenOptions = new HashSet<String>(); + static { + tokenOptions.add("assoc"); + tokenOptions.add(LeftRecursiveRuleTransformer.TOKENINDEX_OPTION_NAME); + } + + public static final Set<String> actionOptions = new HashSet<String>(); + + public static final Set<String> semPredOptions = new HashSet<String>(); + static { + semPredOptions.add(LeftRecursiveRuleTransformer.PRECEDENCE_OPTION_NAME); + semPredOptions.add("fail"); + } + + public static final Set<String> doNotCopyOptionsToLexer = new HashSet<String>(); + static { + doNotCopyOptionsToLexer.add("superClass"); + doNotCopyOptionsToLexer.add("TokenLabelType"); + doNotCopyOptionsToLexer.add("tokenVocab"); + } + + public static final Map<String, AttributeDict> grammarAndLabelRefTypeToScope = + new HashMap<String, AttributeDict>(); + static { + grammarAndLabelRefTypeToScope.put("parser:RULE_LABEL", Rule.predefinedRulePropertiesDict); + grammarAndLabelRefTypeToScope.put("parser:TOKEN_LABEL", AttributeDict.predefinedTokenDict); + grammarAndLabelRefTypeToScope.put("combined:RULE_LABEL", Rule.predefinedRulePropertiesDict); + grammarAndLabelRefTypeToScope.put("combined:TOKEN_LABEL", AttributeDict.predefinedTokenDict); + } + + public String name; + public GrammarRootAST ast; + + /** Track token stream used to create this grammar */ + + public final org.antlr.runtime.TokenStream tokenStream; + + /** If we transform grammar, track original unaltered token stream. + * This is set to the same value as tokenStream when tokenStream is + * initially set. + * + * If this field differs from tokenStream, then we have transformed + * the grammar. + */ + + public org.antlr.runtime.TokenStream originalTokenStream; + + public String text; // testing only + public String fileName; + + /** Was this parser grammar created from a COMBINED grammar? If so, + * this is what we extracted. + */ + public LexerGrammar implicitLexer; + + /** If this is an extracted/implicit lexer, we point at original grammar */ + public Grammar originalGrammar; + + /** If we're imported, who imported us? If null, implies grammar is root */ + public Grammar parent; + public List<Grammar> importedGrammars; + + /** All rules defined in this specific grammar, not imported. Also does + * not include lexical rules if combined. + */ + public OrderedHashMap<String, Rule> rules = new OrderedHashMap<String, Rule>(); + public List<Rule> indexToRule = new ArrayList<Rule>(); + + int ruleNumber = 0; // used to get rule indexes (0..n-1) + int stringLiteralRuleNumber = 0; // used to invent rule names for 'keyword', ';', ... (0..n-1) + + /** The ATN that represents the grammar with edges labelled with tokens + * or epsilon. It is more suitable to analysis than an AST representation. + */ + public ATN atn; + + public Map<Integer, Interval> stateToGrammarRegionMap; + + public Map<Integer, DFA> decisionDFAs = new HashMap<Integer, DFA>(); + + public List<IntervalSet[]> decisionLOOK; + + public final Tool tool; + + /** Token names and literal tokens like "void" are uniquely indexed. + * with -1 implying EOF. Characters are different; they go from + * -1 (EOF) to \uFFFE. For example, 0 could be a binary byte you + * want to lexer. Labels of DFA/ATN transitions can be both tokens + * and characters. I use negative numbers for bookkeeping labels + * like EPSILON. Char/String literals and token types overlap in the same + * space, however. + */ + int maxTokenType = Token.MIN_USER_TOKEN_TYPE -1; + + /** + * Map token like {@code ID} (but not literals like {@code 'while'}) to its + * token type. + */ + public final Map<String, Integer> tokenNameToTypeMap = new LinkedHashMap<String, Integer>(); + + /** + * Map token literals like {@code 'while'} to its token type. It may be that + * {@code WHILE="while"=35}, in which case both {@link #tokenNameToTypeMap} + * and this field will have entries both mapped to 35. + */ + public final Map<String, Integer> stringLiteralToTypeMap = new LinkedHashMap<String, Integer>(); + + /** + * Reverse index for {@link #stringLiteralToTypeMap}. Indexed with raw token + * type. 0 is invalid. + */ + public final List<String> typeToStringLiteralList = new ArrayList<String>(); + + /** + * Map a token type to its token name. Indexed with raw token type. 0 is + * invalid. + */ + public final List<String> typeToTokenList = new ArrayList<String>(); + + /** + * The maximum channel value which is assigned by this grammar. Values below + * {@link Token#MIN_USER_CHANNEL_VALUE} are assumed to be predefined. + */ + int maxChannelType = Token.MIN_USER_CHANNEL_VALUE - 1; + + /** + * Map channel like {@code COMMENTS_CHANNEL} to its constant channel value. + * Only user-defined channels are defined in this map. + */ + public final Map<String, Integer> channelNameToValueMap = new LinkedHashMap<String, Integer>(); + + /** + * Map a constant channel value to its name. Indexed with raw channel value. + * The predefined channels {@link Token#DEFAULT_CHANNEL} and + * {@link Token#HIDDEN_CHANNEL} are not stored in this list, so the values + * at the corresponding indexes is {@code null}. + */ + public final List<String> channelValueToNameList = new ArrayList<String>(); + + /** Map a name to an action. + * The code generator will use this to fill holes in the output files. + * I track the AST node for the action in case I need the line number + * for errors. + */ + public Map<String,ActionAST> namedActions = new HashMap<String,ActionAST>(); + + /** Tracks all user lexer actions in all alternatives of all rules. + * Doesn't track sempreds. maps tree node to action index (alt number 1..n). + */ + public LinkedHashMap<ActionAST, Integer> lexerActions = new LinkedHashMap<ActionAST, Integer>(); + + /** All sempreds found in grammar; maps tree node to sempred index; + * sempred index is 0..n-1 + */ + public LinkedHashMap<PredAST, Integer> sempreds = new LinkedHashMap<PredAST, Integer>(); + /** Map the other direction upon demand */ + public LinkedHashMap<Integer, PredAST> indexToPredMap; + + public static final String AUTO_GENERATED_TOKEN_NAME_PREFIX = "T__"; + + public Grammar(Tool tool, GrammarRootAST ast) { + if ( ast==null ) { + throw new NullPointerException("ast"); + } + + if (ast.tokenStream == null) { + throw new IllegalArgumentException("ast must have a token stream"); + } + + this.tool = tool; + this.ast = ast; + this.name = (ast.getChild(0)).getText(); + this.tokenStream = ast.tokenStream; + this.originalTokenStream = this.tokenStream; + + initTokenSymbolTables(); + } + + /** For testing */ + public Grammar(String grammarText) throws org.antlr.runtime.RecognitionException { + this(GRAMMAR_FROM_STRING_NAME, grammarText, null); + } + + public Grammar(String grammarText, LexerGrammar tokenVocabSource) throws org.antlr.runtime.RecognitionException { + this(GRAMMAR_FROM_STRING_NAME, grammarText, tokenVocabSource, null); + } + + /** For testing */ + public Grammar(String grammarText, ANTLRToolListener listener) + throws org.antlr.runtime.RecognitionException + { + this(GRAMMAR_FROM_STRING_NAME, grammarText, listener); + } + + /** For testing; builds trees, does sem anal */ + public Grammar(String fileName, String grammarText) + throws org.antlr.runtime.RecognitionException + { + this(fileName, grammarText, null); + } + + /** For testing; builds trees, does sem anal */ + public Grammar(String fileName, String grammarText, ANTLRToolListener listener) + throws org.antlr.runtime.RecognitionException + { + this(fileName, grammarText, null, listener); + } + + /** For testing; builds trees, does sem anal */ + public Grammar(String fileName, String grammarText, Grammar tokenVocabSource, ANTLRToolListener listener) + throws org.antlr.runtime.RecognitionException + { + this.text = grammarText; + this.fileName = fileName; + this.tool = new Tool(); + this.tool.addListener(listener); + org.antlr.runtime.ANTLRStringStream in = new org.antlr.runtime.ANTLRStringStream(grammarText); + in.name = fileName; + + this.ast = tool.parse(fileName, in); + if ( ast==null ) { + throw new UnsupportedOperationException(); + } + + if (ast.tokenStream == null) { + throw new IllegalStateException("expected ast to have a token stream"); + } + + this.tokenStream = ast.tokenStream; + this.originalTokenStream = this.tokenStream; + + // ensure each node has pointer to surrounding grammar + final Grammar thiz = this; + org.antlr.runtime.tree.TreeVisitor v = new org.antlr.runtime.tree.TreeVisitor(new GrammarASTAdaptor()); + v.visit(ast, new org.antlr.runtime.tree.TreeVisitorAction() { + @Override + public Object pre(Object t) { ((GrammarAST)t).g = thiz; return t; } + @Override + public Object post(Object t) { return t; } + }); + initTokenSymbolTables(); + + if (tokenVocabSource != null) { + importVocab(tokenVocabSource); + } + + tool.process(this, false); + } + + protected void initTokenSymbolTables() { + tokenNameToTypeMap.put("EOF", Token.EOF); + + // reserve a spot for the INVALID token + typeToTokenList.add(null); + } + + public void loadImportedGrammars() { + if ( ast==null ) return; + GrammarAST i = (GrammarAST)ast.getFirstChildWithType(ANTLRParser.IMPORT); + if ( i==null ) return; + importedGrammars = new ArrayList<Grammar>(); + for (Object c : i.getChildren()) { + GrammarAST t = (GrammarAST)c; + String importedGrammarName = null; + if ( t.getType()==ANTLRParser.ASSIGN ) { + t = (GrammarAST)t.getChild(1); + importedGrammarName = t.getText(); + } + else if ( t.getType()==ANTLRParser.ID ) { + importedGrammarName = t.getText(); + } + Grammar g; + try { + g = tool.loadImportedGrammar(this, t); + } + catch (IOException ioe) { + tool.errMgr.grammarError(ErrorType.ERROR_READING_IMPORTED_GRAMMAR, + importedGrammarName, + t.getToken(), + importedGrammarName, + name); + continue; + } + // did it come back as error node or missing? + if ( g == null ) continue; + g.parent = this; + importedGrammars.add(g); + g.loadImportedGrammars(); // recursively pursue any imports in this import + } + } + + public void defineAction(GrammarAST atAST) { + if ( atAST.getChildCount()==2 ) { + String name = atAST.getChild(0).getText(); + namedActions.put(name, (ActionAST)atAST.getChild(1)); + } + else { + String scope = atAST.getChild(0).getText(); + String gtype = getTypeString(); + if ( scope.equals(gtype) || (scope.equals("parser")&>ype.equals("combined")) ) { + String name = atAST.getChild(1).getText(); + namedActions.put(name, (ActionAST)atAST.getChild(2)); + } + } + } + + /** + * Define the specified rule in the grammar. This method assigns the rule's + * {@link Rule#index} according to the {@link #ruleNumber} field, and adds + * the {@link Rule} instance to {@link #rules} and {@link #indexToRule}. + * + * @param r The rule to define in the grammar. + * @return {@code true} if the rule was added to the {@link Grammar} + * instance; otherwise, {@code false} if a rule with this name already + * existed in the grammar instance. + */ + public boolean defineRule(Rule r) { + if ( rules.get(r.name)!=null ) { + return false; + } + + rules.put(r.name, r); + r.index = ruleNumber++; + indexToRule.add(r); + return true; + } + + /** + * Undefine the specified rule from this {@link Grammar} instance. The + * instance {@code r} is removed from {@link #rules} and + * {@link #indexToRule}. This method updates the {@link Rule#index} field + * for all rules defined after {@code r}, and decrements {@link #ruleNumber} + * in preparation for adding new rules. + * <p> + * This method does nothing if the current {@link Grammar} does not contain + * the instance {@code r} at index {@code r.index} in {@link #indexToRule}. + * </p> + * + * @param r + * @return {@code true} if the rule was removed from the {@link Grammar} + * instance; otherwise, {@code false} if the specified rule was not defined + * in the grammar. + */ + public boolean undefineRule(Rule r) { + if (r.index < 0 || r.index >= indexToRule.size() || indexToRule.get(r.index) != r) { + return false; + } + + assert rules.get(r.name) == r; + + rules.remove(r.name); + indexToRule.remove(r.index); + for (int i = r.index; i < indexToRule.size(); i++) { + assert indexToRule.get(i).index == i + 1; + indexToRule.get(i).index--; + } + + ruleNumber--; + return true; + } + +// public int getNumRules() { +// int n = rules.size(); +// List<Grammar> imports = getAllImportedGrammars(); +// if ( imports!=null ) { +// for (Grammar g : imports) n += g.getNumRules(); +// } +// return n; +// } + + public Rule getRule(String name) { + Rule r = rules.get(name); + if ( r!=null ) return r; + return null; + /* + List<Grammar> imports = getAllImportedGrammars(); + if ( imports==null ) return null; + for (Grammar g : imports) { + r = g.getRule(name); // recursively walk up hierarchy + if ( r!=null ) return r; + } + return null; + */ + } + + public ATN getATN() { + if ( atn==null ) { + ParserATNFactory factory = new ParserATNFactory(this); + atn = factory.createATN(); + } + return atn; + } + + public Rule getRule(int index) { return indexToRule.get(index); } + + public Rule getRule(String grammarName, String ruleName) { + if ( grammarName!=null ) { // scope override + Grammar g = getImportedGrammar(grammarName); + if ( g ==null ) { + return null; + } + return g.rules.get(ruleName); + } + return getRule(ruleName); + } + + /** Get list of all imports from all grammars in the delegate subtree of g. + * The grammars are in import tree preorder. Don't include ourselves + * in list as we're not a delegate of ourselves. + */ + public List<Grammar> getAllImportedGrammars() { + if (importedGrammars == null) { + return null; + } + + LinkedHashMap<String, Grammar> delegates = new LinkedHashMap<String, Grammar>(); + for (Grammar d : importedGrammars) { + delegates.put(d.fileName, d); + List<Grammar> ds = d.getAllImportedGrammars(); + if (ds != null) { + for (Grammar imported : ds) { + delegates.put(imported.fileName, imported); + } + } + } + + return new ArrayList<Grammar>(delegates.values()); + } + + public List<Grammar> getImportedGrammars() { return importedGrammars; } + + /** Get delegates below direct delegates of g + public List<Grammar> getIndirectDelegates(Grammar g) { + List<Grammar> direct = getDirectDelegates(g); + List<Grammar> delegates = getDelegates(g); + delegates.removeAll(direct); + return delegates; + } +*/ + + public LexerGrammar getImplicitLexer() { + return implicitLexer; + } + + /** convenience method for Tool.loadGrammar() */ + public static Grammar load(String fileName) { + Tool antlr = new Tool(); + return antlr.loadGrammar(fileName); + } + + /** Return list of imported grammars from root down to our parent. + * Order is [root, ..., this.parent]. (us not included). + */ + public List<Grammar> getGrammarAncestors() { + Grammar root = getOutermostGrammar(); + if ( this==root ) return null; + List<Grammar> grammars = new ArrayList<Grammar>(); + // walk backwards to root, collecting grammars + Grammar p = this.parent; + while ( p!=null ) { + grammars.add(0, p); // add to head so in order later + p = p.parent; + } + return grammars; + } + + /** Return the grammar that imported us and our parents. Return this + * if we're root. + */ + public Grammar getOutermostGrammar() { + if ( parent==null ) return this; + return parent.getOutermostGrammar(); + } + + /** Get the name of the generated recognizer; may or may not be same + * as grammar name. + * Recognizer is TParser and TLexer from T if combined, else + * just use T regardless of grammar type. + */ + public String getRecognizerName() { + String suffix = ""; + List<Grammar> grammarsFromRootToMe = getOutermostGrammar().getGrammarAncestors(); + String qualifiedName = name; + if ( grammarsFromRootToMe!=null ) { + StringBuilder buf = new StringBuilder(); + for (Grammar g : grammarsFromRootToMe) { + buf.append(g.name); + buf.append('_'); + } + buf.append(name); + qualifiedName = buf.toString(); + } + + if ( isCombined() || (isLexer() && implicitLexer!=null) ) + { + suffix = Grammar.getGrammarTypeToFileNameSuffix(getType()); + } + return qualifiedName+suffix; + } + + public String getStringLiteralLexerRuleName(String lit) { + return AUTO_GENERATED_TOKEN_NAME_PREFIX + stringLiteralRuleNumber++; + } + + /** Return grammar directly imported by this grammar */ + public Grammar getImportedGrammar(String name) { + for (Grammar g : importedGrammars) { + if ( g.name.equals(name) ) return g; + } + return null; + } + + public int getTokenType(String token) { + Integer I; + if ( token.charAt(0)=='\'') { + I = stringLiteralToTypeMap.get(token); + } + else { // must be a label like ID + I = tokenNameToTypeMap.get(token); + } + int i = (I!=null)? I : Token.INVALID_TYPE; + //tool.log("grammar", "grammar type "+type+" "+tokenName+"->"+i); + return i; + } + + /** Given a token type, get a meaningful name for it such as the ID + * or string literal. If this is a lexer and the ttype is in the + * char vocabulary, compute an ANTLR-valid (possibly escaped) char literal. + */ + public String getTokenDisplayName(int ttype) { + // inside any target's char range and is lexer grammar? + if ( isLexer() && + ttype >= Lexer.MIN_CHAR_VALUE && ttype <= Lexer.MAX_CHAR_VALUE ) + { + return CharSupport.getANTLRCharLiteralForChar(ttype); + } + + if ( ttype==Token.EOF ) { + return "EOF"; + } + + if ( ttype==Token.INVALID_TYPE ) { + return INVALID_TOKEN_NAME; + } + + if (ttype >= 0 && ttype < typeToStringLiteralList.size() && typeToStringLiteralList.get(ttype) != null) { + return typeToStringLiteralList.get(ttype); + } + + if (ttype >= 0 && ttype < typeToTokenList.size() && typeToTokenList.get(ttype) != null) { + return typeToTokenList.get(ttype); + } + + return String.valueOf(ttype); + } + + /** + * Gets the name by which a token can be referenced in the generated code. + * For tokens defined in a {@code tokens{}} block or via a lexer rule, this + * is the declared name of the token. For token types generated by the use + * of a string literal within a parser rule of a combined grammar, this is + * the automatically generated token type which includes the + * {@link #AUTO_GENERATED_TOKEN_NAME_PREFIX} prefix. For types which are not + * associated with a defined token, this method returns + * {@link #INVALID_TOKEN_NAME}. + * + * @param ttype The token type. + * @return The name of the token with the specified type. + */ + + public String getTokenName(int ttype) { + // inside any target's char range and is lexer grammar? + if ( isLexer() && + ttype >= Lexer.MIN_CHAR_VALUE && ttype <= Lexer.MAX_CHAR_VALUE ) + { + return CharSupport.getANTLRCharLiteralForChar(ttype); + } + + if ( ttype==Token.EOF ) { + return "EOF"; + } + + if (ttype >= 0 && ttype < typeToTokenList.size() && typeToTokenList.get(ttype) != null) { + return typeToTokenList.get(ttype); + } + + return INVALID_TOKEN_NAME; + } + + /** + * Gets the constant channel value for a user-defined channel. + * + * <p> + * This method only returns channel values for user-defined channels. All + * other channels, including the predefined channels + * {@link Token#DEFAULT_CHANNEL} and {@link Token#HIDDEN_CHANNEL} along with + * any channel defined in code (e.g. in a {@code @members{}} block), are + * ignored.</p> + * + * @param channel The channel name. + * @return The channel value, if {@code channel} is the name of a known + * user-defined token channel; otherwise, -1. + */ + public int getChannelValue(String channel) { + Integer I = channelNameToValueMap.get(channel); + int i = (I != null) ? I : -1; + return i; + } + + /** + * Gets an array of rule names for rules defined or imported by the + * grammar. The array index is the rule index, and the value is the name of + * the rule with the corresponding {@link Rule#index}. + * + * <p>If no rule is defined with an index for an element of the resulting + * array, the value of that element is {@link #INVALID_RULE_NAME}.</p> + * + * @return The names of all rules defined in the grammar. + */ + public String[] getRuleNames() { + String[] result = new String[rules.size()]; + Arrays.fill(result, INVALID_RULE_NAME); + for (Rule rule : rules.values()) { + result[rule.index] = rule.name; + } + + return result; + } + + /** + * Gets an array of token names for tokens defined or imported by the + * grammar. The array index is the token type, and the value is the result + * of {@link #getTokenName} for the corresponding token type. + * + * @see #getTokenName + * @return The token names of all tokens defined in the grammar. + */ + public String[] getTokenNames() { + int numTokens = getMaxTokenType(); + String[] tokenNames = new String[numTokens+1]; + for (int i = 0; i < tokenNames.length; i++) { + tokenNames[i] = getTokenName(i); + } + + return tokenNames; + } + + /** + * Gets an array of display names for tokens defined or imported by the + * grammar. The array index is the token type, and the value is the result + * of {@link #getTokenDisplayName} for the corresponding token type. + * + * @see #getTokenDisplayName + * @return The display names of all tokens defined in the grammar. + */ + public String[] getTokenDisplayNames() { + int numTokens = getMaxTokenType(); + String[] tokenNames = new String[numTokens+1]; + for (int i = 0; i < tokenNames.length; i++) { + tokenNames[i] = getTokenDisplayName(i); + } + + return tokenNames; + } + + /** + * Gets the literal names assigned to tokens in the grammar. + */ + + public String[] getTokenLiteralNames() { + int numTokens = getMaxTokenType(); + String[] literalNames = new String[numTokens+1]; + for (int i = 0; i < Math.min(literalNames.length, typeToStringLiteralList.size()); i++) { + literalNames[i] = typeToStringLiteralList.get(i); + } + + for (Map.Entry<String, Integer> entry : stringLiteralToTypeMap.entrySet()) { + if (entry.getValue() >= 0 && entry.getValue() < literalNames.length && literalNames[entry.getValue()] == null) { + literalNames[entry.getValue()] = entry.getKey(); + } + } + + return literalNames; + } + + /** + * Gets the symbolic names assigned to tokens in the grammar. + */ + + public String[] getTokenSymbolicNames() { + int numTokens = getMaxTokenType(); + String[] symbolicNames = new String[numTokens+1]; + for (int i = 0; i < Math.min(symbolicNames.length, typeToTokenList.size()); i++) { + if (typeToTokenList.get(i) == null || typeToTokenList.get(i).startsWith(AUTO_GENERATED_TOKEN_NAME_PREFIX)) { + continue; + } + + symbolicNames[i] = typeToTokenList.get(i); + } + + return symbolicNames; + } + + /** + * Gets a {@link Vocabulary} instance describing the vocabulary used by the + * grammar. + */ + + public Vocabulary getVocabulary() { + return new VocabularyImpl(getTokenLiteralNames(), getTokenSymbolicNames()); + } + + /** Given an arbitrarily complex SemanticContext, walk the "tree" and get display string. + * Pull predicates from grammar text. + */ + public String getSemanticContextDisplayString(SemanticContext semctx) { + if ( semctx instanceof SemanticContext.Predicate ) { + return getPredicateDisplayString((SemanticContext.Predicate)semctx); + } + if ( semctx instanceof SemanticContext.AND ) { + SemanticContext.AND and = (SemanticContext.AND)semctx; + return joinPredicateOperands(and, " and "); + } + if ( semctx instanceof SemanticContext.OR ) { + SemanticContext.OR or = (SemanticContext.OR)semctx; + return joinPredicateOperands(or, " or "); + } + return semctx.toString(); + } + + public String joinPredicateOperands(SemanticContext.Operator op, String separator) { + StringBuilder buf = new StringBuilder(); + for (SemanticContext operand : op.getOperands()) { + if (buf.length() > 0) { + buf.append(separator); + } + + buf.append(getSemanticContextDisplayString(operand)); + } + + return buf.toString(); + } + + public LinkedHashMap<Integer, PredAST> getIndexToPredicateMap() { + LinkedHashMap<Integer, PredAST> indexToPredMap = new LinkedHashMap<Integer, PredAST>(); + for (Rule r : rules.values()) { + for (ActionAST a : r.actions) { + if (a instanceof PredAST) { + PredAST p = (PredAST) a; + indexToPredMap.put(sempreds.get(p), p); + } + } + } + return indexToPredMap; + } + + public String getPredicateDisplayString(SemanticContext.Predicate pred) { + if ( indexToPredMap==null ) { + indexToPredMap = getIndexToPredicateMap(); + } + ActionAST actionAST = indexToPredMap.get(pred.predIndex); + return actionAST.getText(); + } + + /** What is the max char value possible for this grammar's target? Use + * unicode max if no target defined. + */ + public int getMaxCharValue() { + return org.antlr.v4.runtime.Lexer.MAX_CHAR_VALUE; +// if ( generator!=null ) { +// return generator.target.getMaxCharValue(generator); +// } +// else { +// return Label.MAX_CHAR_VALUE; +// } + } + + /** Return a set of all possible token or char types for this grammar */ + public IntSet getTokenTypes() { + if ( isLexer() ) { + return getAllCharValues(); + } + return IntervalSet.of(Token.MIN_USER_TOKEN_TYPE, getMaxTokenType()); + } + + /** Return min to max char as defined by the target. + * If no target, use max unicode char value. + */ + public IntSet getAllCharValues() { + return IntervalSet.of(Lexer.MIN_CHAR_VALUE, getMaxCharValue()); + } + + /** How many token types have been allocated so far? */ + public int getMaxTokenType() { + return typeToTokenList.size() - 1; // don't count 0 (invalid) + } + + /** Return a new unique integer in the token type space */ + public int getNewTokenType() { + maxTokenType++; + return maxTokenType; + } + + /** Return a new unique integer in the channel value space. */ + public int getNewChannelNumber() { + maxChannelType++; + return maxChannelType; + } + + public void importTokensFromTokensFile() { + String vocab = getOptionString("tokenVocab"); + if ( vocab!=null ) { + TokenVocabParser vparser = new TokenVocabParser(this); + Map<String,Integer> tokens = vparser.load(); + tool.log("grammar", "tokens=" + tokens); + for (String t : tokens.keySet()) { + if ( t.charAt(0)=='\'' ) defineStringLiteral(t, tokens.get(t)); + else defineTokenName(t, tokens.get(t)); + } + } + } + + public void importVocab(Grammar importG) { + for (String tokenName: importG.tokenNameToTypeMap.keySet()) { + defineTokenName(tokenName, importG.tokenNameToTypeMap.get(tokenName)); + } + for (String tokenName: importG.stringLiteralToTypeMap.keySet()) { + defineStringLiteral(tokenName, importG.stringLiteralToTypeMap.get(tokenName)); + } + for (Map.Entry<String, Integer> channel : importG.channelNameToValueMap.entrySet()) { + defineChannelName(channel.getKey(), channel.getValue()); + } +// this.tokenNameToTypeMap.putAll( importG.tokenNameToTypeMap ); +// this.stringLiteralToTypeMap.putAll( importG.stringLiteralToTypeMap ); + int max = Math.max(this.typeToTokenList.size(), importG.typeToTokenList.size()); + Utils.setSize(typeToTokenList, max); + for (int ttype=0; ttype<importG.typeToTokenList.size(); ttype++) { + maxTokenType = Math.max(maxTokenType, ttype); + this.typeToTokenList.set(ttype, importG.typeToTokenList.get(ttype)); + } + + max = Math.max(this.channelValueToNameList.size(), importG.channelValueToNameList.size()); + Utils.setSize(channelValueToNameList, max); + for (int channelValue = 0; channelValue < importG.channelValueToNameList.size(); channelValue++) { + maxChannelType = Math.max(maxChannelType, channelValue); + this.channelValueToNameList.set(channelValue, importG.channelValueToNameList.get(channelValue)); + } + } + + public int defineTokenName(String name) { + Integer prev = tokenNameToTypeMap.get(name); + if ( prev==null ) return defineTokenName(name, getNewTokenType()); + return prev; + } + + public int defineTokenName(String name, int ttype) { + Integer prev = tokenNameToTypeMap.get(name); + if ( prev!=null ) return prev; + tokenNameToTypeMap.put(name, ttype); + setTokenForType(ttype, name); + maxTokenType = Math.max(maxTokenType, ttype); + return ttype; + } + + public int defineStringLiteral(String lit) { + if ( stringLiteralToTypeMap.containsKey(lit) ) { + return stringLiteralToTypeMap.get(lit); + } + return defineStringLiteral(lit, getNewTokenType()); + + } + + public int defineStringLiteral(String lit, int ttype) { + if ( !stringLiteralToTypeMap.containsKey(lit) ) { + stringLiteralToTypeMap.put(lit, ttype); + // track in reverse index too + if ( ttype>=typeToStringLiteralList.size() ) { + Utils.setSize(typeToStringLiteralList, ttype+1); + } + typeToStringLiteralList.set(ttype, lit); + + setTokenForType(ttype, lit); + return ttype; + } + return Token.INVALID_TYPE; + } + + public int defineTokenAlias(String name, String lit) { + int ttype = defineTokenName(name); + stringLiteralToTypeMap.put(lit, ttype); + setTokenForType(ttype, name); + return ttype; + } + + public void setTokenForType(int ttype, String text) { + if (ttype == Token.EOF) { + // ignore EOF, it will be reported as an error separately + return; + } + + if ( ttype>=typeToTokenList.size() ) { + Utils.setSize(typeToTokenList, ttype+1); + } + String prevToken = typeToTokenList.get(ttype); + if ( prevToken==null || prevToken.charAt(0)=='\'' ) { + // only record if nothing there before or if thing before was a literal + typeToTokenList.set(ttype, text); + } + } + + /** + * Define a token channel with a specified name. + * + * <p> + * If a channel with the specified name already exists, the previously + * assigned channel value is returned.</p> + * + * @param name The channel name. + * @return The constant channel value assigned to the channel. + */ + public int defineChannelName(String name) { + Integer prev = channelNameToValueMap.get(name); + if (prev == null) { + return defineChannelName(name, getNewChannelNumber()); + } + + return prev; + } + + /** + * Define a token channel with a specified name. + * + * <p> + * If a channel with the specified name already exists, the previously + * assigned channel value is not altered.</p> + * + * @param name The channel name. + * @return The constant channel value assigned to the channel. + */ + public int defineChannelName(String name, int value) { + Integer prev = channelNameToValueMap.get(name); + if (prev != null) { + return prev; + } + + channelNameToValueMap.put(name, value); + setChannelNameForValue(value, name); + maxChannelType = Math.max(maxChannelType, value); + return value; + } + + /** + * Sets the channel name associated with a particular channel value. + * + * <p> + * If a name has already been assigned to the channel with constant value + * {@code channelValue}, this method does nothing.</p> + * + * @param channelValue The constant value for the channel. + * @param name The channel name. + */ + public void setChannelNameForValue(int channelValue, String name) { + if (channelValue >= channelValueToNameList.size()) { + Utils.setSize(channelValueToNameList, channelValue + 1); + } + + String prevChannel = channelValueToNameList.get(channelValue); + if (prevChannel == null) { + channelValueToNameList.set(channelValue, name); + } + } + + // no isolated attr at grammar action level + @Override + public Attribute resolveToAttribute(String x, ActionAST node) { + return null; + } + + // no $x.y makes sense here + @Override + public Attribute resolveToAttribute(String x, String y, ActionAST node) { + return null; + } + + @Override + public boolean resolvesToLabel(String x, ActionAST node) { return false; } + + @Override + public boolean resolvesToListLabel(String x, ActionAST node) { return false; } + + @Override + public boolean resolvesToToken(String x, ActionAST node) { return false; } + + @Override + public boolean resolvesToAttributeDict(String x, ActionAST node) { + return false; + } + + /** Given a grammar type, what should be the default action scope? + * If I say @members in a COMBINED grammar, for example, the + * default scope should be "parser". + */ + public String getDefaultActionScope() { + switch ( getType() ) { + case ANTLRParser.LEXER : + return "lexer"; + case ANTLRParser.PARSER : + case ANTLRParser.COMBINED : + return "parser"; + } + return null; + } + + public int getType() { + if ( ast!=null ) return ast.grammarType; + return 0; + } + + public org.antlr.runtime.TokenStream getTokenStream() { + if ( ast!=null ) return ast.tokenStream; + return null; + } + + public boolean isLexer() { return getType()==ANTLRParser.LEXER; } + public boolean isParser() { return getType()==ANTLRParser.PARSER; } + public boolean isCombined() { return getType()==ANTLRParser.COMBINED; } + + /** Is id a valid token name? Does id start with an uppercase letter? */ + public static boolean isTokenName(String id) { + return Character.isUpperCase(id.charAt(0)); + } + + public String getTypeString() { + if ( ast==null ) return null; + return ANTLRParser.tokenNames[getType()].toLowerCase(); + } + + public static String getGrammarTypeToFileNameSuffix(int type) { + switch ( type ) { + case ANTLRParser.LEXER : return "Lexer"; + case ANTLRParser.PARSER : return "Parser"; + // if combined grammar, gen Parser and Lexer will be done later + // TODO: we are separate now right? + case ANTLRParser.COMBINED : return "Parser"; + default : + return "<invalid>"; + } + } + + public String getOptionString(String key) { return ast.getOptionString(key); } + + /** Given ^(TOKEN_REF ^(OPTIONS ^(ELEMENT_OPTIONS (= assoc right)))) + * set option assoc=right in TOKEN_REF. + */ + public static void setNodeOptions(GrammarAST node, GrammarAST options) { + if ( options==null ) return; + GrammarASTWithOptions t = (GrammarASTWithOptions)node; + if ( t.getChildCount()==0 || options.getChildCount()==0 ) return; + for (Object o : options.getChildren()) { + GrammarAST c = (GrammarAST)o; + if ( c.getType()==ANTLRParser.ASSIGN ) { + t.setOption(c.getChild(0).getText(), (GrammarAST)c.getChild(1)); + } + else { + t.setOption(c.getText(), null); // no arg such as ID<VarNodeType> + } + } + } + + /** Return list of (TOKEN_NAME node, 'literal' node) pairs */ + public static List<Pair<GrammarAST,GrammarAST>> getStringLiteralAliasesFromLexerRules(GrammarRootAST ast) { + String[] patterns = { + "(RULE %name:TOKEN_REF (BLOCK (ALT %lit:STRING_LITERAL)))", + "(RULE %name:TOKEN_REF (BLOCK (ALT %lit:STRING_LITERAL ACTION)))", + "(RULE %name:TOKEN_REF (BLOCK (ALT %lit:STRING_LITERAL SEMPRED)))", + "(RULE %name:TOKEN_REF (BLOCK (LEXER_ALT_ACTION (ALT %lit:STRING_LITERAL) .)))", + "(RULE %name:TOKEN_REF (BLOCK (LEXER_ALT_ACTION (ALT %lit:STRING_LITERAL) . .)))", + "(RULE %name:TOKEN_REF (BLOCK (LEXER_ALT_ACTION (ALT %lit:STRING_LITERAL) (LEXER_ACTION_CALL . .))))", + "(RULE %name:TOKEN_REF (BLOCK (LEXER_ALT_ACTION (ALT %lit:STRING_LITERAL) . (LEXER_ACTION_CALL . .))))", + "(RULE %name:TOKEN_REF (BLOCK (LEXER_ALT_ACTION (ALT %lit:STRING_LITERAL) (LEXER_ACTION_CALL . .) .)))", + // TODO: allow doc comment in there + }; + GrammarASTAdaptor adaptor = new GrammarASTAdaptor(ast.token.getInputStream()); + org.antlr.runtime.tree.TreeWizard wiz = new org.antlr.runtime.tree.TreeWizard(adaptor,ANTLRParser.tokenNames); + List<Pair<GrammarAST,GrammarAST>> lexerRuleToStringLiteral = + new ArrayList<Pair<GrammarAST,GrammarAST>>(); + + List<GrammarAST> ruleNodes = ast.getNodesWithType(ANTLRParser.RULE); + if ( ruleNodes==null || ruleNodes.isEmpty() ) return null; + + for (GrammarAST r : ruleNodes) { + //tool.log("grammar", r.toStringTree()); +// System.out.println("chk: "+r.toStringTree()); + org.antlr.runtime.tree.Tree name = r.getChild(0); + if ( name.getType()==ANTLRParser.TOKEN_REF ) { + // check rule against patterns + boolean isLitRule; + for (String pattern : patterns) { + isLitRule = + defAlias(r, pattern, wiz, lexerRuleToStringLiteral); + if ( isLitRule ) break; + } +// if ( !isLitRule ) System.out.println("no pattern matched"); + } + } + return lexerRuleToStringLiteral; + } + + protected static boolean defAlias(GrammarAST r, String pattern, + org.antlr.runtime.tree.TreeWizard wiz, + List<Pair<GrammarAST,GrammarAST>> lexerRuleToStringLiteral) + { + HashMap<String, Object> nodes = new HashMap<String, Object>(); + if ( wiz.parse(r, pattern, nodes) ) { + GrammarAST litNode = (GrammarAST)nodes.get("lit"); + GrammarAST nameNode = (GrammarAST)nodes.get("name"); + Pair<GrammarAST, GrammarAST> pair = + new Pair<GrammarAST, GrammarAST>(nameNode, litNode); + lexerRuleToStringLiteral.add(pair); + return true; + } + return false; + } + + public Set<String> getStringLiterals() { + final Set<String> strings = new LinkedHashSet<String>(); + GrammarTreeVisitor collector = new GrammarTreeVisitor() { + @Override + public void stringRef(TerminalAST ref) { + strings.add(ref.getText()); + } + @Override + public ErrorManager getErrorManager() { return tool.errMgr; } + }; + collector.visitGrammar(ast); + return strings; + } + + public void setLookaheadDFA(int decision, DFA lookaheadDFA) { + decisionDFAs.put(decision, lookaheadDFA); + } + + public static Map<Integer, Interval> getStateToGrammarRegionMap(GrammarRootAST ast, IntervalSet grammarTokenTypes) { + Map<Integer, Interval> stateToGrammarRegionMap = new HashMap<Integer, Interval>(); + if ( ast==null ) return stateToGrammarRegionMap; + + List<GrammarAST> nodes = ast.getNodesWithType(grammarTokenTypes); + for (GrammarAST n : nodes) { + if (n.atnState != null) { + Interval tokenRegion = Interval.of(n.getTokenStartIndex(), n.getTokenStopIndex()); + org.antlr.runtime.tree.Tree ruleNode = null; + // RULEs, BLOCKs of transformed recursive rules point to original token interval + switch ( n.getType() ) { + case ANTLRParser.RULE : + ruleNode = n; + break; + case ANTLRParser.BLOCK : + case ANTLRParser.CLOSURE : + ruleNode = n.getAncestor(ANTLRParser.RULE); + break; + } + if ( ruleNode instanceof RuleAST ) { + String ruleName = ((RuleAST) ruleNode).getRuleName(); + Rule r = ast.g.getRule(ruleName); + if ( r instanceof LeftRecursiveRule ) { + RuleAST originalAST = ((LeftRecursiveRule) r).getOriginalAST(); + tokenRegion = Interval.of(originalAST.getTokenStartIndex(), originalAST.getTokenStopIndex()); + } + } + stateToGrammarRegionMap.put(n.atnState.stateNumber, tokenRegion); + } + } + return stateToGrammarRegionMap; + } + + /** Given an ATN state number, return the token index range within the grammar from which that ATN state was derived. */ + public Interval getStateToGrammarRegion(int atnStateNumber) { + if ( stateToGrammarRegionMap==null ) { + stateToGrammarRegionMap = getStateToGrammarRegionMap(ast, null); // map all nodes with non-null atn state ptr + } + if ( stateToGrammarRegionMap==null ) return Interval.INVALID; + + return stateToGrammarRegionMap.get(atnStateNumber); + } + + public LexerInterpreter createLexerInterpreter(CharStream input) { + if (this.isParser()) { + throw new IllegalStateException("A lexer interpreter can only be created for a lexer or combined grammar."); + } + + if (this.isCombined()) { + return implicitLexer.createLexerInterpreter(input); + } + + char[] serializedAtn = ATNSerializer.getSerializedAsChars(atn); + ATN deserialized = new ATNDeserializer().deserialize(serializedAtn); + return new LexerInterpreter(fileName, getVocabulary(), Arrays.asList(getRuleNames()), ((LexerGrammar)this).modes.keySet(), deserialized, input); + } + + /** @since 4.5.1 */ + public GrammarParserInterpreter createGrammarParserInterpreter(TokenStream tokenStream) { + if (this.isLexer()) { + throw new IllegalStateException("A parser interpreter can only be created for a parser or combined grammar."); + } + char[] serializedAtn = ATNSerializer.getSerializedAsChars(atn); + ATN deserialized = new ATNDeserializer().deserialize(serializedAtn); + return new GrammarParserInterpreter(this, deserialized, tokenStream); + } + + public ParserInterpreter createParserInterpreter(TokenStream tokenStream) { + if (this.isLexer()) { + throw new IllegalStateException("A parser interpreter can only be created for a parser or combined grammar."); + } + + char[] serializedAtn = ATNSerializer.getSerializedAsChars(atn); + ATN deserialized = new ATNDeserializer().deserialize(serializedAtn); + return new ParserInterpreter(fileName, getVocabulary(), Arrays.asList(getRuleNames()), deserialized, tokenStream); + } +} diff --git a/tool/src/org/antlr/v4/tool/GrammarInterpreterRuleContext.java b/tool/src/org/antlr/v4/tool/GrammarInterpreterRuleContext.java new file mode 100644 index 0000000..8457a6a --- /dev/null +++ b/tool/src/org/antlr/v4/tool/GrammarInterpreterRuleContext.java @@ -0,0 +1,57 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.runtime.InterpreterRuleContext; +import org.antlr.v4.runtime.ParserRuleContext; + +/** An {@link InterpreterRuleContext} that knows which alternative + * for a rule was matched. + * + * @see GrammarParserInterpreter + * @since 4.5.1 + */ +public class GrammarInterpreterRuleContext extends InterpreterRuleContext { + protected int outerAltNum = 1; + + public GrammarInterpreterRuleContext(ParserRuleContext parent, int invokingStateNumber, int ruleIndex) { + super(parent, invokingStateNumber, ruleIndex); + } + + /** The predicted outermost alternative for the rule associated + * with this context object. If this node left recursive, the true original + * outermost alternative is returned. + */ + public int getOuterAltNum() { return outerAltNum; } + + public void setOuterAltNum(int outerAltNum) { + this.outerAltNum = outerAltNum; + } +} diff --git a/tool/src/org/antlr/v4/tool/GrammarParserInterpreter.java b/tool/src/org/antlr/v4/tool/GrammarParserInterpreter.java new file mode 100644 index 0000000..9361b40 --- /dev/null +++ b/tool/src/org/antlr/v4/tool/GrammarParserInterpreter.java @@ -0,0 +1,474 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.runtime.BailErrorStrategy; +import org.antlr.v4.runtime.DefaultErrorStrategy; +import org.antlr.v4.runtime.InputMismatchException; +import org.antlr.v4.runtime.InterpreterRuleContext; +import org.antlr.v4.runtime.Parser; +import org.antlr.v4.runtime.ParserInterpreter; +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.RecognitionException; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.TokenStream; +import org.antlr.v4.runtime.Vocabulary; +import org.antlr.v4.runtime.atn.ATN; +import org.antlr.v4.runtime.atn.ATNDeserializer; +import org.antlr.v4.runtime.atn.ATNSerializer; +import org.antlr.v4.runtime.atn.ATNState; +import org.antlr.v4.runtime.atn.DecisionState; +import org.antlr.v4.runtime.atn.PredictionMode; +import org.antlr.v4.runtime.atn.RuleStartState; +import org.antlr.v4.runtime.atn.StarLoopEntryState; +import org.antlr.v4.runtime.tree.Trees; + +import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.BitSet; +import java.util.Collection; +import java.util.List; + +/** A heavier weight {@link ParserInterpreter} that creates parse trees + * that track alternative numbers for subtree roots. + * + * @since 4.5.1 + * + */ +public class GrammarParserInterpreter extends ParserInterpreter { + /** The grammar associated with this interpreter. Unlike the + * {@link ParserInterpreter} from the standard distribution, + * this can reference Grammar, which is in the tools area not + * purely runtime. + */ + protected final Grammar g; + + protected BitSet decisionStatesThatSetOuterAltNumInContext; + + /** Cache {@link LeftRecursiveRule#getPrimaryAlts()} and + * {@link LeftRecursiveRule#getRecursiveOpAlts()} for states in + * {@link #decisionStatesThatSetOuterAltNumInContext}. It only + * caches decisions in left-recursive rules. + */ + protected int[][] stateToAltsMap; + + public GrammarParserInterpreter(Grammar g, + String grammarFileName, + Vocabulary vocabulary, + Collection<String> ruleNames, + ATN atn, + TokenStream input) { + super(grammarFileName, vocabulary, ruleNames, atn, input); + this.g = g; + } + + public GrammarParserInterpreter(Grammar g, ATN atn, TokenStream input) { + super(g.fileName, g.getVocabulary(), + Arrays.asList(g.getRuleNames()), + atn, // must run ATN through serializer to set some state flags + input); + this.g = g; + decisionStatesThatSetOuterAltNumInContext = findOuterMostDecisionStates(); + stateToAltsMap = new int[g.atn.states.size()][]; + } + + @Override + protected InterpreterRuleContext createInterpreterRuleContext(ParserRuleContext parent, + int invokingStateNumber, + int ruleIndex) + { + return new GrammarInterpreterRuleContext(parent, invokingStateNumber, ruleIndex); + } + + @Override + public void reset() { + super.reset(); + overrideDecisionRoot = null; + } + + /** identify the ATN states where we need to set the outer alt number. + * For regular rules, that's the block at the target to rule start state. + * For left-recursive rules, we track the primary block, which looks just + * like a regular rule's outer block, and the star loop block (always + * there even if 1 alt). + */ + public BitSet findOuterMostDecisionStates() { + BitSet track = new BitSet(atn.states.size()); + int numberOfDecisions = atn.getNumberOfDecisions(); + for (int i = 0; i < numberOfDecisions; i++) { + DecisionState decisionState = atn.getDecisionState(i); + RuleStartState startState = atn.ruleToStartState[decisionState.ruleIndex]; + // Look for StarLoopEntryState that is in any left recursive rule + if ( decisionState instanceof StarLoopEntryState) { + StarLoopEntryState loopEntry = (StarLoopEntryState)decisionState; + if ( loopEntry.isPrecedenceDecision ) { + // Recursive alts always result in a (...)* in the transformed + // left recursive rule and that always has a BasicBlockStartState + // even if just 1 recursive alt exists. + ATNState blockStart = loopEntry.transition(0).target; + // track the StarBlockStartState associated with the recursive alternatives + track.set(blockStart.stateNumber); + } + } + else if ( startState.transition(0).target == decisionState ) { + // always track outermost block for any rule if it exists + track.set(decisionState.stateNumber); + } + } + return track; + } + + /** Override this method so that we can record which alternative + * was taken at each decision point. For non-left recursive rules, + * it's simple. Set decisionStatesThatSetOuterAltNumInContext + * indicates which decision states should set the outer alternative number. + * + * Left recursive rules are much more complicated to deal with: + * there is typically a decision for the primary alternatives and a + * decision to choose between the recursive operator alternatives. + * For example, the following left recursive rule has two primary and 2 + * recursive alternatives.</p> + * + e : e '*' e + | '-' INT + | e '+' e + | ID + ; + + * <p>ANTLR rewrites that rule to be</p> + + e[int precedence] + : ('-' INT | ID) + ( {...}? '*' e[5] + | {...}? '+' e[3] + )* + ; + + * + * <p>So, there are two decisions associated with picking the outermost alt. + * This complicates our tracking significantly. The outermost alternative number + * is a function of the decision (ATN state) within a left recursive rule and the + * predicted alternative coming back from adaptivePredict(). + * + * We use stateToAltsMap as a cache to avoid expensive calls to + * getRecursiveOpAlts(). + */ + @Override + protected int visitDecisionState(DecisionState p) { + int predictedAlt = super.visitDecisionState(p); + if( p.getNumberOfTransitions() > 1) { +// System.out.println("decision "+p.decision+": "+predictedAlt); + if( p.decision == this.overrideDecision && + this._input.index() == this.overrideDecisionInputIndex ) + { + overrideDecisionRoot = (GrammarInterpreterRuleContext)getContext(); + } + } + + GrammarInterpreterRuleContext ctx = (GrammarInterpreterRuleContext)_ctx; + if ( decisionStatesThatSetOuterAltNumInContext.get(p.stateNumber) ) { + ctx.outerAltNum = predictedAlt; + Rule r = g.getRule(p.ruleIndex); + if ( atn.ruleToStartState[r.index].isLeftRecursiveRule ) { + int[] alts = stateToAltsMap[p.stateNumber]; + LeftRecursiveRule lr = (LeftRecursiveRule) g.getRule(p.ruleIndex); + if (p.getStateType() == ATNState.BLOCK_START) { + if ( alts==null ) { + alts = lr.getPrimaryAlts(); + stateToAltsMap[p.stateNumber] = alts; // cache it + } + } + else if ( p.getStateType() == ATNState.STAR_BLOCK_START ) { + if ( alts==null ) { + alts = lr.getRecursiveOpAlts(); + stateToAltsMap[p.stateNumber] = alts; // cache it + } + } + ctx.outerAltNum = alts[predictedAlt]; + } + } + + return predictedAlt; + } + + /** Given an ambiguous parse information, return the list of ambiguous parse trees. + * An ambiguity occurs when a specific token sequence can be recognized + * in more than one way by the grammar. These ambiguities are detected only + * at decision points. + * + * The list of trees includes the actual interpretation (that for + * the minimum alternative number) and all ambiguous alternatives. + * The actual interpretation is always first. + * + * This method reuses the same physical input token stream used to + * detect the ambiguity by the original parser in the first place. + * This method resets/seeks within but does not alter originalParser. + * + * The trees are rooted at the node whose start..stop token indices + * include the start and stop indices of this ambiguity event. That is, + * the trees returned will always include the complete ambiguous subphrase + * identified by the ambiguity event. The subtrees returned will + * also always contain the node associated with the overridden decision. + * + * Be aware that this method does NOT notify error or parse listeners as + * it would trigger duplicate or otherwise unwanted events. + * + * This uses a temporary ParserATNSimulator and a ParserInterpreter + * so we don't mess up any statistics, event lists, etc... + * The parse tree constructed while identifying/making ambiguityInfo is + * not affected by this method as it creates a new parser interp to + * get the ambiguous interpretations. + * + * Nodes in the returned ambig trees are independent of the original parse + * tree (constructed while identifying/creating ambiguityInfo). + * + * @since 4.5.1 + * + * @param g From which grammar should we drive alternative + * numbers and alternative labels. + * + * @param originalParser The parser used to create ambiguityInfo; it + * is not modified by this routine and can be either + * a generated or interpreted parser. It's token + * stream *is* reset/seek()'d. + * @param tokens A stream of tokens to use with the temporary parser. + * This will often be just the token stream within the + * original parser but here it is for flexibility. + * + * @param decision Which decision to try different alternatives for. + * + * @param alts The set of alternatives to try while re-parsing. + * + * @param startIndex The index of the first token of the ambiguous + * input or other input of interest. + * + * @param stopIndex The index of the last token of the ambiguous input. + * The start and stop indexes are used primarily to + * identify how much of the resulting parse tree + * to return. + * + * @param startRuleIndex The start rule for the entire grammar, not + * the ambiguous decision. We re-parse the entire input + * and so we need the original start rule. + * + * @return The list of all possible interpretations of + * the input for the decision in ambiguityInfo. + * The actual interpretation chosen by the parser + * is always given first because this method + * retests the input in alternative order and + * ANTLR always resolves ambiguities by choosing + * the first alternative that matches the input. + * The subtree returned + * + * @throws RecognitionException Throws upon syntax error while matching + * ambig input. + */ + public static List<ParserRuleContext> getAllPossibleParseTrees(Grammar g, + Parser originalParser, + TokenStream tokens, + int decision, + BitSet alts, + int startIndex, + int stopIndex, + int startRuleIndex) + throws RecognitionException + { + List<ParserRuleContext> trees = new ArrayList<ParserRuleContext>(); + // Create a new parser interpreter to parse the ambiguous subphrase + ParserInterpreter parser = deriveTempParserInterpreter(g, originalParser, tokens); + + // get ambig trees + int alt = alts.nextSetBit(0); + while (alt >= 0) { + // re-parse entire input for all ambiguous alternatives + // (don't have to do first as it's been parsed, but do again for simplicity + // using this temp parser.) + parser.reset(); + parser.addDecisionOverride(decision, startIndex, alt); + ParserRuleContext t = parser.parse(startRuleIndex); + GrammarInterpreterRuleContext ambigSubTree = + (GrammarInterpreterRuleContext) Trees.getRootOfSubtreeEnclosingRegion(t, startIndex, stopIndex); + // Use higher of overridden decision tree or tree enclosing all tokens + if ( Trees.isAncestorOf(parser.getOverrideDecisionRoot(), ambigSubTree) ) { + ambigSubTree = (GrammarInterpreterRuleContext)parser.getOverrideDecisionRoot(); + } + trees.add(ambigSubTree); + alt = alts.nextSetBit(alt + 1); + } + + return trees; + } + + + /** Return a list of parse trees, one for each alternative in a decision + * given the same input. + * + * Very similar to {@link #getAllPossibleParseTrees} except + * that it re-parses the input for every alternative in a decision, + * not just the ambiguous ones (there is no alts parameter here). + * This method also tries to reduce the size of the parse trees + * by stripping away children of the tree that are completely out of range + * of startIndex..stopIndex. Also, because errors are expected, we + * use a specialized error handler that more or less bails out + * but that also consumes the first erroneous token at least. This + * ensures that an error node will be in the parse tree for display. + * + * NOTES: + // we must parse the entire input now with decision overrides + // we cannot parse a subset because it could be that a decision + // above our decision of interest needs to read way past + // lookaheadInfo.stopIndex. It seems like there is no escaping + // the use of a full and complete token stream if we are + // resetting to token index 0 and re-parsing from the start symbol. + // It's not easy to restart parsing somewhere in the middle like a + // continuation because our call stack does not match the + // tree stack because of left recursive rule rewriting. grrrr! + * + * @since 4.5.1 + */ + public static List<ParserRuleContext> getLookaheadParseTrees(Grammar g, + ParserInterpreter originalParser, + TokenStream tokens, + int startRuleIndex, + int decision, + int startIndex, + int stopIndex) + { + List<ParserRuleContext> trees = new ArrayList<ParserRuleContext>(); + // Create a new parser interpreter to parse the ambiguous subphrase + ParserInterpreter parser = deriveTempParserInterpreter(g, originalParser, tokens); + BailButConsumeErrorStrategy errorHandler = new BailButConsumeErrorStrategy(); + parser.setErrorHandler(errorHandler); + + DecisionState decisionState = originalParser.getATN().decisionToState.get(decision); + + for (int alt=1; alt<=decisionState.getTransitions().length; alt++) { + // re-parse entire input for all ambiguous alternatives + // (don't have to do first as it's been parsed, but do again for simplicity + // using this temp parser.) + parser.reset(); + parser.addDecisionOverride(decision, startIndex, alt); + ParserRuleContext tt = parser.parse(startRuleIndex); + int stopTreeAt = stopIndex; + if ( errorHandler.firstErrorTokenIndex>=0 ) { + stopTreeAt = errorHandler.firstErrorTokenIndex; // cut off rest at first error + } + ParserRuleContext subtree = + Trees.getRootOfSubtreeEnclosingRegion(tt, + startIndex, + stopTreeAt); + // Use higher of overridden decision tree or tree enclosing all tokens + if ( Trees.isAncestorOf(parser.getOverrideDecisionRoot(), subtree) ) { + subtree = parser.getOverrideDecisionRoot(); + } + Trees.stripChildrenOutOfRange(subtree, parser.getOverrideDecisionRoot(), startIndex, stopTreeAt); + trees.add(subtree); + } + + return trees; + } + + /** Derive a new parser from an old one that has knowledge of the grammar. + * The Grammar object is used to correctly compute outer alternative + * numbers for parse tree nodes. A parser of the same type is created + * for subclasses of {@link ParserInterpreter}. + */ + public static ParserInterpreter deriveTempParserInterpreter(Grammar g, Parser originalParser, TokenStream tokens) { + ParserInterpreter parser; + if (originalParser instanceof ParserInterpreter) { + Class<? extends ParserInterpreter> c = originalParser.getClass().asSubclass(ParserInterpreter.class); + try { + Constructor<? extends ParserInterpreter> ctor = c.getConstructor(Grammar.class, ATN.class, TokenStream.class); + parser = ctor.newInstance(g, originalParser.getATN(), originalParser.getTokenStream()); + } + catch (Exception e) { + throw new IllegalArgumentException("can't create parser to match incoming "+originalParser.getClass().getSimpleName(), e); + } + } + else { // must've been a generated parser + char[] serializedAtn = ATNSerializer.getSerializedAsChars(originalParser.getATN()); + ATN deserialized = new ATNDeserializer().deserialize(serializedAtn); + parser = new ParserInterpreter(originalParser.getGrammarFileName(), + originalParser.getVocabulary(), + Arrays.asList(originalParser.getRuleNames()), + deserialized, + tokens); + } + + parser.setInputStream(tokens); + + // Make sure that we don't get any error messages from using this temporary parser + parser.setErrorHandler(new BailErrorStrategy()); + parser.removeErrorListeners(); + parser.removeParseListeners(); + parser.getInterpreter().setPredictionMode(PredictionMode.LL_EXACT_AMBIG_DETECTION); + return parser; + } + + /** We want to stop and track the first error but we cannot bail out like + * {@link BailErrorStrategy} as consume() constructs trees. We make sure + * to create an error node during recovery with this strategy. We + * consume() 1 token during the "bail out of rule" mechanism in recover() + * and let it fall out of the rule to finish constructing trees. For + * recovery in line, we throw InputMismatchException to engage recover(). + */ + public static class BailButConsumeErrorStrategy extends DefaultErrorStrategy { + public int firstErrorTokenIndex = -1; + @Override + public void recover(Parser recognizer, RecognitionException e) { + int errIndex = recognizer.getInputStream().index(); + if ( firstErrorTokenIndex == -1 ) { + firstErrorTokenIndex = errIndex; // latch + } +// System.err.println("recover: error at " + errIndex); + TokenStream input = recognizer.getInputStream(); + if ( input.index()<input.size()-1 ) { // don't consume() eof + recognizer.consume(); // just kill this bad token and let it continue. + } + } + + @Override + public Token recoverInline(Parser recognizer) throws RecognitionException { + int errIndex = recognizer.getInputStream().index(); + if ( firstErrorTokenIndex == -1 ) { + firstErrorTokenIndex = errIndex; // latch + } +// System.err.println("recoverInline: error at " + errIndex); + InputMismatchException e = new InputMismatchException(recognizer); +// TokenStream input = recognizer.getInputStream(); // seek EOF +// input.seek(input.size() - 1); + throw e; + } + + @Override + public void sync(Parser recognizer) { } // don't consume anything; let it fail later + } +} diff --git a/tool/src/org/antlr/v4/tool/GrammarSemanticsMessage.java b/tool/src/org/antlr/v4/tool/GrammarSemanticsMessage.java new file mode 100644 index 0000000..72e7501 --- /dev/null +++ b/tool/src/org/antlr/v4/tool/GrammarSemanticsMessage.java @@ -0,0 +1,52 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.runtime.Token; + +/** A problem with the symbols and/or meaning of a grammar such as rule + * redefinition. Any msg where we can point to a location in the grammar. + */ +public class GrammarSemanticsMessage extends ANTLRMessage { + public GrammarSemanticsMessage(ErrorType etype, + String fileName, + Token offendingToken, + Object... args) + { + super(etype,offendingToken,args); + this.fileName = fileName; + if ( offendingToken!=null ) { + line = offendingToken.getLine(); + charPosition = offendingToken.getCharPositionInLine(); + } + } +} + diff --git a/tool/src/org/antlr/v4/tool/GrammarSyntaxMessage.java b/tool/src/org/antlr/v4/tool/GrammarSyntaxMessage.java new file mode 100644 index 0000000..4648a84 --- /dev/null +++ b/tool/src/org/antlr/v4/tool/GrammarSyntaxMessage.java @@ -0,0 +1,60 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.runtime.RecognitionException; +import org.antlr.runtime.Token; + +/** A problem with the syntax of your antlr grammar such as + * "The '{' came as a complete surprise to me at this point in your program" + */ +public class GrammarSyntaxMessage extends ANTLRMessage { + public GrammarSyntaxMessage(ErrorType etype, + String fileName, + Token offendingToken, + RecognitionException antlrException, + Object... args) + { + super(etype, antlrException, offendingToken, args); + this.fileName = fileName; + this.offendingToken = offendingToken; + if ( offendingToken!=null ) { + line = offendingToken.getLine(); + charPosition = offendingToken.getCharPositionInLine(); + } + } + + @SuppressWarnings({"ThrowableResultOfMethodCallIgnored"}) + @Override + public RecognitionException getCause() { + return (RecognitionException)super.getCause(); + } +} diff --git a/tool/src/org/antlr/v4/tool/GrammarTransformPipeline.java b/tool/src/org/antlr/v4/tool/GrammarTransformPipeline.java new file mode 100644 index 0000000..6759e20 --- /dev/null +++ b/tool/src/org/antlr/v4/tool/GrammarTransformPipeline.java @@ -0,0 +1,456 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.runtime.CommonToken; +import org.antlr.runtime.tree.CommonTree; +import org.antlr.runtime.tree.CommonTreeNodeStream; +import org.antlr.runtime.tree.Tree; +import org.antlr.runtime.tree.TreeVisitor; +import org.antlr.runtime.tree.TreeVisitorAction; +import org.antlr.v4.Tool; +import org.antlr.v4.analysis.LeftRecursiveRuleTransformer; +import org.antlr.v4.parse.ANTLRParser; +import org.antlr.v4.parse.BlockSetTransformer; +import org.antlr.v4.parse.GrammarASTAdaptor; +import org.antlr.v4.parse.GrammarToken; +import org.antlr.v4.runtime.misc.DoubleKeyMap; +import org.antlr.v4.runtime.misc.Pair; +import org.antlr.v4.tool.ast.AltAST; +import org.antlr.v4.tool.ast.BlockAST; +import org.antlr.v4.tool.ast.GrammarAST; +import org.antlr.v4.tool.ast.GrammarASTWithOptions; +import org.antlr.v4.tool.ast.GrammarRootAST; +import org.antlr.v4.tool.ast.RuleAST; +import org.antlr.v4.tool.ast.TerminalAST; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** Handle left-recursion and block-set transforms */ +public class GrammarTransformPipeline { + public Grammar g; + public Tool tool; + + public GrammarTransformPipeline(Grammar g, Tool tool) { + this.g = g; + this.tool = tool; + } + + public void process() { + GrammarRootAST root = g.ast; + if ( root==null ) return; + tool.log("grammar", "before: "+root.toStringTree()); + + integrateImportedGrammars(g); + reduceBlocksToSets(root); + expandParameterizedLoops(root); + + tool.log("grammar", "after: "+root.toStringTree()); + } + + public void reduceBlocksToSets(GrammarAST root) { + CommonTreeNodeStream nodes = new CommonTreeNodeStream(new GrammarASTAdaptor(), root); + GrammarASTAdaptor adaptor = new GrammarASTAdaptor(); + BlockSetTransformer transformer = new BlockSetTransformer(nodes, g); + transformer.setTreeAdaptor(adaptor); + transformer.downup(root); + } + + /** Find and replace + * ID*[','] with ID (',' ID)* + * ID+[','] with ID (',' ID)+ + * (x {action} y)+[','] with x {action} y (',' x {action} y)+ + * + * Parameter must be a token. + * todo: do we want? + */ + public void expandParameterizedLoops(GrammarAST root) { + TreeVisitor v = new TreeVisitor(new GrammarASTAdaptor()); + v.visit(root, new TreeVisitorAction() { + @Override + public Object pre(Object t) { + if ( ((GrammarAST)t).getType() == 3 ) { + return expandParameterizedLoop((GrammarAST)t); + } + return t; + } + @Override + public Object post(Object t) { return t; } + }); + } + + public GrammarAST expandParameterizedLoop(GrammarAST t) { + // todo: update grammar, alter AST + return t; + } + + /** Utility visitor that sets grammar ptr in each node */ + public static void setGrammarPtr(final Grammar g, GrammarAST tree) { + if ( tree==null ) return; + // ensure each node has pointer to surrounding grammar + TreeVisitor v = new TreeVisitor(new GrammarASTAdaptor()); + v.visit(tree, new TreeVisitorAction() { + @Override + public Object pre(Object t) { ((GrammarAST)t).g = g; return t; } + @Override + public Object post(Object t) { return t; } + }); + } + + public static void augmentTokensWithOriginalPosition(final Grammar g, GrammarAST tree) { + if ( tree==null ) return; + + List<GrammarAST> optionsSubTrees = tree.getNodesWithType(ANTLRParser.ELEMENT_OPTIONS); + for (int i = 0; i < optionsSubTrees.size(); i++) { + GrammarAST t = optionsSubTrees.get(i); + CommonTree elWithOpt = t.parent; + if ( elWithOpt instanceof GrammarASTWithOptions ) { + Map<String, GrammarAST> options = ((GrammarASTWithOptions) elWithOpt).getOptions(); + if ( options.containsKey(LeftRecursiveRuleTransformer.TOKENINDEX_OPTION_NAME) ) { + GrammarToken newTok = new GrammarToken(g, elWithOpt.getToken()); + newTok.originalTokenIndex = Integer.valueOf(options.get(LeftRecursiveRuleTransformer.TOKENINDEX_OPTION_NAME).getText()); + elWithOpt.token = newTok; + + GrammarAST originalNode = g.ast.getNodeWithTokenIndex(newTok.getTokenIndex()); + if (originalNode != null) { + // update the AST node start/stop index to match the values + // of the corresponding node in the original parse tree. + elWithOpt.setTokenStartIndex(originalNode.getTokenStartIndex()); + elWithOpt.setTokenStopIndex(originalNode.getTokenStopIndex()); + } + else { + // the original AST node could not be located by index; + // make sure to assign valid values for the start/stop + // index so toTokenString will not throw exceptions. + elWithOpt.setTokenStartIndex(newTok.getTokenIndex()); + elWithOpt.setTokenStopIndex(newTok.getTokenIndex()); + } + } + } + } + } + + /** Merge all the rules, token definitions, and named actions from + imported grammars into the root grammar tree. Perform: + + (tokens { X (= Y 'y')) + (tokens { Z ) -> (tokens { X (= Y 'y') Z) + + (@ members {foo}) + (@ members {bar}) -> (@ members {foobar}) + + (RULES (RULE x y)) + (RULES (RULE z)) -> (RULES (RULE x y z)) + + Rules in root prevent same rule from being appended to RULES node. + + The goal is a complete combined grammar so we can ignore subordinate + grammars. + */ + public void integrateImportedGrammars(Grammar rootGrammar) { + List<Grammar> imports = rootGrammar.getAllImportedGrammars(); + if ( imports==null ) return; + + GrammarAST root = rootGrammar.ast; + GrammarAST id = (GrammarAST) root.getChild(0); + GrammarASTAdaptor adaptor = new GrammarASTAdaptor(id.token.getInputStream()); + + GrammarAST tokensRoot = (GrammarAST)root.getFirstChildWithType(ANTLRParser.TOKENS_SPEC); + + List<GrammarAST> actionRoots = root.getNodesWithType(ANTLRParser.AT); + + // Compute list of rules in root grammar and ensure we have a RULES node + GrammarAST RULES = (GrammarAST)root.getFirstChildWithType(ANTLRParser.RULES); + Set<String> rootRuleNames = new HashSet<String>(); + // make list of rules we have in root grammar + List<GrammarAST> rootRules = RULES.getNodesWithType(ANTLRParser.RULE); + for (GrammarAST r : rootRules) rootRuleNames.add(r.getChild(0).getText()); + + for (Grammar imp : imports) { + // COPY TOKENS + GrammarAST imp_tokensRoot = (GrammarAST)imp.ast.getFirstChildWithType(ANTLRParser.TOKENS_SPEC); + if ( imp_tokensRoot!=null ) { + rootGrammar.tool.log("grammar", "imported tokens: "+imp_tokensRoot.getChildren()); + if ( tokensRoot==null ) { + tokensRoot = (GrammarAST)adaptor.create(ANTLRParser.TOKENS_SPEC, "TOKENS"); + tokensRoot.g = rootGrammar; + root.insertChild(1, tokensRoot); // ^(GRAMMAR ID TOKENS...) + } + tokensRoot.addChildren(Arrays.asList(imp_tokensRoot.getChildren().toArray(new Tree[0]))); + } + + List<GrammarAST> all_actionRoots = new ArrayList<GrammarAST>(); + List<GrammarAST> imp_actionRoots = imp.ast.getAllChildrenWithType(ANTLRParser.AT); + if ( actionRoots!=null ) all_actionRoots.addAll(actionRoots); + all_actionRoots.addAll(imp_actionRoots); + + // COPY ACTIONS + if ( imp_actionRoots!=null ) { + DoubleKeyMap<String, String, GrammarAST> namedActions = + new DoubleKeyMap<String, String, GrammarAST>(); + + rootGrammar.tool.log("grammar", "imported actions: "+imp_actionRoots); + for (GrammarAST at : all_actionRoots) { + String scopeName = rootGrammar.getDefaultActionScope(); + GrammarAST scope, name, action; + if ( at.getChildCount()>2 ) { // must have a scope + scope = (GrammarAST)at.getChild(0); + scopeName = scope.getText(); + name = (GrammarAST)at.getChild(1); + action = (GrammarAST)at.getChild(2); + } + else { + name = (GrammarAST)at.getChild(0); + action = (GrammarAST)at.getChild(1); + } + GrammarAST prevAction = namedActions.get(scopeName, name.getText()); + if ( prevAction==null ) { + namedActions.put(scopeName, name.getText(), action); + } + else { + if ( prevAction.g == at.g ) { + rootGrammar.tool.errMgr.grammarError(ErrorType.ACTION_REDEFINITION, + at.g.fileName, name.token, name.getText()); + } + else { + String s1 = prevAction.getText(); + s1 = s1.substring(1, s1.length()-1); + String s2 = action.getText(); + s2 = s2.substring(1, s2.length()-1); + String combinedAction = "{"+s1 + '\n'+ s2+"}"; + prevAction.token.setText(combinedAction); + } + } + } + // at this point, we have complete list of combined actions, + // some of which are already living in root grammar. + // Merge in any actions not in root grammar into root's tree. + for (String scopeName : namedActions.keySet()) { + for (String name : namedActions.keySet(scopeName)) { + GrammarAST action = namedActions.get(scopeName, name); + rootGrammar.tool.log("grammar", action.g.name+" "+scopeName+":"+name+"="+action.getText()); + if ( action.g != rootGrammar ) { + root.insertChild(1, action.getParent()); + } + } + } + } + + // COPY RULES + List<GrammarAST> rules = imp.ast.getNodesWithType(ANTLRParser.RULE); + if ( rules!=null ) { + for (GrammarAST r : rules) { + rootGrammar.tool.log("grammar", "imported rule: "+r.toStringTree()); + String name = r.getChild(0).getText(); + boolean rootAlreadyHasRule = rootRuleNames.contains(name); + if ( !rootAlreadyHasRule ) { + RULES.addChild(r); // merge in if not overridden + rootRuleNames.add(name); + } + } + } + + GrammarAST optionsRoot = (GrammarAST)imp.ast.getFirstChildWithType(ANTLRParser.OPTIONS); + if ( optionsRoot!=null ) { + // suppress the warning if the options match the options specified + // in the root grammar + // https://github.com/antlr/antlr4/issues/707 + + boolean hasNewOption = false; + for (Map.Entry<String, GrammarAST> option : imp.ast.getOptions().entrySet()) { + String importOption = imp.ast.getOptionString(option.getKey()); + if (importOption == null) { + continue; + } + + String rootOption = rootGrammar.ast.getOptionString(option.getKey()); + if (!importOption.equals(rootOption)) { + hasNewOption = true; + break; + } + } + + if (hasNewOption) { + rootGrammar.tool.errMgr.grammarError(ErrorType.OPTIONS_IN_DELEGATE, + optionsRoot.g.fileName, optionsRoot.token, imp.name); + } + } + } + rootGrammar.tool.log("grammar", "Grammar: "+rootGrammar.ast.toStringTree()); + } + + /** Build lexer grammar from combined grammar that looks like: + * + * (COMBINED_GRAMMAR A + * (tokens { X (= Y 'y')) + * (OPTIONS (= x 'y')) + * (@ members {foo}) + * (@ lexer header {package jj;}) + * (RULES (RULE .+))) + * + * Move rules and actions to new tree, don't dup. Split AST apart. + * We'll have this Grammar share token symbols later; don't generate + * tokenVocab or tokens{} section. Copy over named actions. + * + * Side-effects: it removes children from GRAMMAR & RULES nodes + * in combined AST. Anything cut out is dup'd before + * adding to lexer to avoid "who's ur daddy" issues + */ + public GrammarRootAST extractImplicitLexer(Grammar combinedGrammar) { + GrammarRootAST combinedAST = combinedGrammar.ast; + //tool.log("grammar", "before="+combinedAST.toStringTree()); + GrammarASTAdaptor adaptor = new GrammarASTAdaptor(combinedAST.token.getInputStream()); + GrammarAST[] elements = combinedAST.getChildren().toArray(new GrammarAST[0]); + + // MAKE A GRAMMAR ROOT and ID + String lexerName = combinedAST.getChild(0).getText()+"Lexer"; + GrammarRootAST lexerAST = + new GrammarRootAST(new CommonToken(ANTLRParser.GRAMMAR, "LEXER_GRAMMAR"), combinedGrammar.ast.tokenStream); + lexerAST.grammarType = ANTLRParser.LEXER; + lexerAST.token.setInputStream(combinedAST.token.getInputStream()); + lexerAST.addChild((GrammarAST)adaptor.create(ANTLRParser.ID, lexerName)); + + // COPY OPTIONS + GrammarAST optionsRoot = + (GrammarAST)combinedAST.getFirstChildWithType(ANTLRParser.OPTIONS); + if ( optionsRoot!=null && optionsRoot.getChildCount()!=0 ) { + GrammarAST lexerOptionsRoot = (GrammarAST)adaptor.dupNode(optionsRoot); + lexerAST.addChild(lexerOptionsRoot); + GrammarAST[] options = optionsRoot.getChildren().toArray(new GrammarAST[0]); + for (GrammarAST o : options) { + String optionName = o.getChild(0).getText(); + if ( Grammar.lexerOptions.contains(optionName) && + !Grammar.doNotCopyOptionsToLexer.contains(optionName) ) + { + GrammarAST optionTree = (GrammarAST)adaptor.dupTree(o); + lexerOptionsRoot.addChild(optionTree); + lexerAST.setOption(optionName, (GrammarAST)optionTree.getChild(1)); + } + } + } + + // COPY all named actions, but only move those with lexer:: scope + List<GrammarAST> actionsWeMoved = new ArrayList<GrammarAST>(); + for (GrammarAST e : elements) { + if ( e.getType()==ANTLRParser.AT ) { + lexerAST.addChild((Tree)adaptor.dupTree(e)); + if ( e.getChild(0).getText().equals("lexer") ) { + actionsWeMoved.add(e); + } + } + } + + for (GrammarAST r : actionsWeMoved) { + combinedAST.deleteChild( r ); + } + + GrammarAST combinedRulesRoot = + (GrammarAST)combinedAST.getFirstChildWithType(ANTLRParser.RULES); + if ( combinedRulesRoot==null ) return lexerAST; + + // MOVE lexer rules + + GrammarAST lexerRulesRoot = + (GrammarAST)adaptor.create(ANTLRParser.RULES, "RULES"); + lexerAST.addChild(lexerRulesRoot); + List<GrammarAST> rulesWeMoved = new ArrayList<GrammarAST>(); + GrammarASTWithOptions[] rules; + if (combinedRulesRoot.getChildCount() > 0) { + rules = combinedRulesRoot.getChildren().toArray(new GrammarASTWithOptions[0]); + } + else { + rules = new GrammarASTWithOptions[0]; + } + + for (GrammarASTWithOptions r : rules) { + String ruleName = r.getChild(0).getText(); + if (Grammar.isTokenName(ruleName)) { + lexerRulesRoot.addChild((Tree)adaptor.dupTree(r)); + rulesWeMoved.add(r); + } + } + for (GrammarAST r : rulesWeMoved) { + combinedRulesRoot.deleteChild( r ); + } + + // Will track 'if' from IF : 'if' ; rules to avoid defining new token for 'if' + List<Pair<GrammarAST,GrammarAST>> litAliases = + Grammar.getStringLiteralAliasesFromLexerRules(lexerAST); + + Set<String> stringLiterals = combinedGrammar.getStringLiterals(); + // add strings from combined grammar (and imported grammars) into lexer + // put them first as they are keywords; must resolve ambigs to these rules +// tool.log("grammar", "strings from parser: "+stringLiterals); + int insertIndex = 0; + nextLit: + for (String lit : stringLiterals) { + // if lexer already has a rule for literal, continue + if ( litAliases!=null ) { + for (Pair<GrammarAST,GrammarAST> pair : litAliases) { + GrammarAST litAST = pair.b; + if ( lit.equals(litAST.getText()) ) continue nextLit; + } + } + // create for each literal: (RULE <uniquename> (BLOCK (ALT <lit>)) + String rname = combinedGrammar.getStringLiteralLexerRuleName(lit); + // can't use wizard; need special node types + GrammarAST litRule = new RuleAST(ANTLRParser.RULE); + BlockAST blk = new BlockAST(ANTLRParser.BLOCK); + AltAST alt = new AltAST(ANTLRParser.ALT); + TerminalAST slit = new TerminalAST(new CommonToken(ANTLRParser.STRING_LITERAL, lit)); + alt.addChild(slit); + blk.addChild(alt); + CommonToken idToken = new CommonToken(ANTLRParser.TOKEN_REF, rname); + litRule.addChild(new TerminalAST(idToken)); + litRule.addChild(blk); + lexerRulesRoot.insertChild(insertIndex, litRule); +// lexerRulesRoot.getChildren().add(0, litRule); + lexerRulesRoot.freshenParentAndChildIndexes(); // reset indexes and set litRule parent + + // next literal will be added after the one just added + insertIndex++; + } + + // TODO: take out after stable if slow + lexerAST.sanityCheckParentAndChildIndexes(); + combinedAST.sanityCheckParentAndChildIndexes(); +// tool.log("grammar", combinedAST.toTokenString()); + + combinedGrammar.tool.log("grammar", "after extract implicit lexer ="+combinedAST.toStringTree()); + combinedGrammar.tool.log("grammar", "lexer ="+lexerAST.toStringTree()); + + if ( lexerRulesRoot.getChildCount()==0 ) return null; + return lexerAST; + } + +} diff --git a/tool/src/org/antlr/v4/tool/LabelElementPair.java b/tool/src/org/antlr/v4/tool/LabelElementPair.java new file mode 100644 index 0000000..835bd5f --- /dev/null +++ b/tool/src/org/antlr/v4/tool/LabelElementPair.java @@ -0,0 +1,74 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.runtime.BitSet; +import org.antlr.v4.parse.ANTLRParser; +import org.antlr.v4.tool.ast.GrammarAST; + +public class LabelElementPair { + public static final BitSet tokenTypeForTokens = new BitSet(); + static { + tokenTypeForTokens.add(ANTLRParser.TOKEN_REF); + tokenTypeForTokens.add(ANTLRParser.STRING_LITERAL); + tokenTypeForTokens.add(ANTLRParser.WILDCARD); + } + + public GrammarAST label; + public GrammarAST element; + public LabelType type; + + public LabelElementPair(Grammar g, GrammarAST label, GrammarAST element, int labelOp) { + this.label = label; + this.element = element; + // compute general case for label type + if ( element.getFirstDescendantWithType(tokenTypeForTokens)!=null ) { + if ( labelOp==ANTLRParser.ASSIGN ) type = LabelType.TOKEN_LABEL; + else type = LabelType.TOKEN_LIST_LABEL; + } + else if ( element.getFirstDescendantWithType(ANTLRParser.RULE_REF)!=null ) { + if ( labelOp==ANTLRParser.ASSIGN ) type = LabelType.RULE_LABEL; + else type = LabelType.RULE_LIST_LABEL; + } + + // now reset if lexer and string + if ( g.isLexer() ) { + if ( element.getFirstDescendantWithType(ANTLRParser.STRING_LITERAL)!=null ) { + if ( labelOp==ANTLRParser.ASSIGN ) type = LabelType.LEXER_STRING_LABEL; + } + } + } + + @Override + public String toString() { + return label.getText()+" "+type+" "+element.toString(); + } +} diff --git a/tool/src/org/antlr/v4/tool/LabelType.java b/tool/src/org/antlr/v4/tool/LabelType.java new file mode 100644 index 0000000..df92ff9 --- /dev/null +++ b/tool/src/org/antlr/v4/tool/LabelType.java @@ -0,0 +1,45 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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; + +/** the various kinds of labels. t=type, id=ID, types+=type ids+=ID */ +public enum LabelType { + RULE_LABEL, + TOKEN_LABEL, + RULE_LIST_LABEL, + TOKEN_LIST_LABEL, + LEXER_STRING_LABEL, // used in lexer for x='a' + SUBRULE_LABEL, // x=(...) + SUBRULE_LIST_LABEL, // x+=(...) + WILDCARD_TREE_LABEL, // Used in tree grammar x=. + WILDCARD_TREE_LIST_LABEL // Used in tree grammar x+=. + ; +} diff --git a/tool/src/org/antlr/v4/tool/LeftRecursionCyclesMessage.java b/tool/src/org/antlr/v4/tool/LeftRecursionCyclesMessage.java new file mode 100644 index 0000000..3e0740b --- /dev/null +++ b/tool/src/org/antlr/v4/tool/LeftRecursionCyclesMessage.java @@ -0,0 +1,61 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.runtime.Token; + +import java.util.Collection; + +public class LeftRecursionCyclesMessage extends ANTLRMessage { + public LeftRecursionCyclesMessage(String fileName, Collection<? extends Collection<Rule>> cycles) { + super(ErrorType.LEFT_RECURSION_CYCLES, getStartTokenOfFirstRule(cycles), cycles); + this.fileName = fileName; + } + + protected static Token getStartTokenOfFirstRule(Collection<? extends Collection<Rule>> cycles) { + if (cycles == null) { + return null; + } + + for (Collection<Rule> collection : cycles) { + if (collection == null) { + return null; + } + + for (Rule rule : collection) { + if (rule.ast != null) { + return rule.ast.getToken(); + } + } + } + return null; + } +} diff --git a/tool/src/org/antlr/v4/tool/LeftRecursiveRule.java b/tool/src/org/antlr/v4/tool/LeftRecursiveRule.java new file mode 100644 index 0000000..1d3b990 --- /dev/null +++ b/tool/src/org/antlr/v4/tool/LeftRecursiveRule.java @@ -0,0 +1,175 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.analysis.LeftRecursiveRuleAltInfo; +import org.antlr.v4.misc.OrderedHashMap; +import org.antlr.v4.runtime.misc.Pair; +import org.antlr.v4.tool.ast.AltAST; +import org.antlr.v4.tool.ast.GrammarAST; +import org.antlr.v4.tool.ast.RuleAST; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class LeftRecursiveRule extends Rule { + public List<LeftRecursiveRuleAltInfo> recPrimaryAlts; + public OrderedHashMap<Integer, LeftRecursiveRuleAltInfo> recOpAlts; + public RuleAST originalAST; + + /** Did we delete any labels on direct left-recur refs? Points at ID of ^(= ID el) */ + public List<Pair<GrammarAST,String>> leftRecursiveRuleRefLabels = + new ArrayList<Pair<GrammarAST,String>>(); + + public LeftRecursiveRule(Grammar g, String name, RuleAST ast) { + super(g, name, ast, 1); + originalAST = ast; + alt = new Alternative[numberOfAlts+1]; // always just one + for (int i=1; i<=numberOfAlts; i++) alt[i] = new Alternative(this, i); + } + + @Override + public boolean hasAltSpecificContexts() { + return super.hasAltSpecificContexts() || getAltLabels()!=null; + } + + @Override + public int getOriginalNumberOfAlts() { + int n = 0; + if ( recPrimaryAlts!=null ) n += recPrimaryAlts.size(); + if ( recOpAlts!=null ) n += recOpAlts.size(); + return n; + } + + public RuleAST getOriginalAST() { + return originalAST; + } + + @Override + public List<AltAST> getUnlabeledAltASTs() { + List<AltAST> alts = new ArrayList<AltAST>(); + for (LeftRecursiveRuleAltInfo altInfo : recPrimaryAlts) { + if (altInfo.altLabel == null) alts.add(altInfo.originalAltAST); + } + for (int i = 0; i < recOpAlts.size(); i++) { + LeftRecursiveRuleAltInfo altInfo = recOpAlts.getElement(i); + if ( altInfo.altLabel==null ) alts.add(altInfo.originalAltAST); + } + if ( alts.isEmpty() ) return null; + return alts; + } + + /** Return an array that maps predicted alt from primary decision + * to original alt of rule. For following rule, return [0, 2, 4] + * + e : e '*' e + | INT + | e '+' e + | ID + ; + + * That maps predicted alt 1 to original alt 2 and predicted 2 to alt 4. + * + * @since 4.5.1 + */ + public int[] getPrimaryAlts() { + if ( recPrimaryAlts.size()==0 ) return null; + int[] alts = new int[recPrimaryAlts.size()+1]; + for (int i = 0; i < recPrimaryAlts.size(); i++) { // recPrimaryAlts is a List not Map like recOpAlts + LeftRecursiveRuleAltInfo altInfo = recPrimaryAlts.get(i); + alts[i+1] = altInfo.altNum; + } + return alts; + } + + /** Return an array that maps predicted alt from recursive op decision + * to original alt of rule. For following rule, return [0, 1, 3] + * + e : e '*' e + | INT + | e '+' e + | ID + ; + + * That maps predicted alt 1 to original alt 1 and predicted 2 to alt 3. + * + * @since 4.5.1 + */ + public int[] getRecursiveOpAlts() { + if ( recOpAlts.size()==0 ) return null; + int[] alts = new int[recOpAlts.size()+1]; + int alt = 1; + for (LeftRecursiveRuleAltInfo altInfo : recOpAlts.values()) { + alts[alt] = altInfo.altNum; + alt++; // recOpAlts has alts possibly with gaps + } + return alts; + } + + /** Get -> labels from those alts we deleted for left-recursive rules. */ + @Override + public Map<String, List<Pair<Integer, AltAST>>> getAltLabels() { + Map<String, List<Pair<Integer, AltAST>>> labels = new HashMap<String, List<Pair<Integer, AltAST>>>(); + Map<String, List<Pair<Integer, AltAST>>> normalAltLabels = super.getAltLabels(); + if ( normalAltLabels!=null ) labels.putAll(normalAltLabels); + if ( recPrimaryAlts!=null ) { + for (LeftRecursiveRuleAltInfo altInfo : recPrimaryAlts) { + if (altInfo.altLabel != null) { + List<Pair<Integer, AltAST>> pairs = labels.get(altInfo.altLabel); + if (pairs == null) { + pairs = new ArrayList<Pair<Integer, AltAST>>(); + labels.put(altInfo.altLabel, pairs); + } + + pairs.add(new Pair<Integer, AltAST>(altInfo.altNum, altInfo.originalAltAST)); + } + } + } + if ( recOpAlts!=null ) { + for (int i = 0; i < recOpAlts.size(); i++) { + LeftRecursiveRuleAltInfo altInfo = recOpAlts.getElement(i); + if ( altInfo.altLabel!=null ) { + List<Pair<Integer, AltAST>> pairs = labels.get(altInfo.altLabel); + if (pairs == null) { + pairs = new ArrayList<Pair<Integer, AltAST>>(); + labels.put(altInfo.altLabel, pairs); + } + + pairs.add(new Pair<Integer, AltAST>(altInfo.altNum, altInfo.originalAltAST)); + } + } + } + if ( labels.isEmpty() ) return null; + return labels; + } +} diff --git a/tool/src/org/antlr/v4/tool/LexerGrammar.java b/tool/src/org/antlr/v4/tool/LexerGrammar.java new file mode 100644 index 0000000..5b92a80 --- /dev/null +++ b/tool/src/org/antlr/v4/tool/LexerGrammar.java @@ -0,0 +1,85 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.runtime.RecognitionException; +import org.antlr.v4.Tool; +import org.antlr.v4.runtime.misc.MultiMap; +import org.antlr.v4.tool.ast.GrammarRootAST; + +/** */ +public class LexerGrammar extends Grammar { + public static final String DEFAULT_MODE_NAME = "DEFAULT_MODE"; + + /** The grammar from which this lexer grammar was derived (if implicit) */ + public Grammar implicitLexerOwner; + + /** DEFAULT_MODE rules are added first due to grammar syntax order */ + public MultiMap<String, Rule> modes; + + public LexerGrammar(Tool tool, GrammarRootAST ast) { + super(tool, ast); + } + + public LexerGrammar(String grammarText) throws RecognitionException { + super(grammarText); + } + + public LexerGrammar(String grammarText, ANTLRToolListener listener) throws RecognitionException { + super(grammarText, listener); + } + + public LexerGrammar(String fileName, String grammarText, ANTLRToolListener listener) throws RecognitionException { + super(fileName, grammarText, listener); + } + + @Override + public boolean defineRule(Rule r) { + if (!super.defineRule(r)) { + return false; + } + + if ( modes==null ) modes = new MultiMap<String, Rule>(); + modes.map(r.mode, r); + return true; + } + + @Override + public boolean undefineRule(Rule r) { + if (!super.undefineRule(r)) { + return false; + } + + boolean removed = modes.get(r.mode).remove(r); + assert removed; + return true; + } +} diff --git a/tool/src/org/antlr/v4/tool/Rule.java b/tool/src/org/antlr/v4/tool/Rule.java new file mode 100644 index 0000000..38f5c88 --- /dev/null +++ b/tool/src/org/antlr/v4/tool/Rule.java @@ -0,0 +1,364 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.runtime.misc.Pair; +import org.antlr.v4.tool.ast.ActionAST; +import org.antlr.v4.tool.ast.AltAST; +import org.antlr.v4.tool.ast.GrammarAST; +import org.antlr.v4.tool.ast.PredAST; +import org.antlr.v4.tool.ast.RuleAST; +import org.stringtemplate.v4.misc.MultiMap; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class Rule implements AttributeResolver { + /** Rule refs have a predefined set of attributes as well as + * the return values and args. + * + * These must be consistent with ActionTranslator.rulePropToModelMap, ... + */ + public static final AttributeDict predefinedRulePropertiesDict = + new AttributeDict(AttributeDict.DictType.PREDEFINED_RULE); + static { + predefinedRulePropertiesDict.add(new Attribute("parser")); + predefinedRulePropertiesDict.add(new Attribute("text")); + predefinedRulePropertiesDict.add(new Attribute("start")); + predefinedRulePropertiesDict.add(new Attribute("stop")); + predefinedRulePropertiesDict.add(new Attribute("ctx")); + } + + public static final Set<String> validLexerCommands = new HashSet<String>(); + static { + // CALLS + validLexerCommands.add("mode"); + validLexerCommands.add("pushMode"); + validLexerCommands.add("type"); + validLexerCommands.add("channel"); + + // ACTIONS + validLexerCommands.add("popMode"); + validLexerCommands.add("skip"); + validLexerCommands.add("more"); + } + + public String name; + public List<GrammarAST> modifiers; + + public RuleAST ast; + public AttributeDict args; + public AttributeDict retvals; + public AttributeDict locals; + + /** In which grammar does this rule live? */ + public Grammar g; + + /** If we're in a lexer grammar, we might be in a mode */ + public String mode; + + /** Map a name to an action for this rule like @init {...}. + * The code generator will use this to fill holes in the rule template. + * I track the AST node for the action in case I need the line number + * for errors. + */ + public Map<String, ActionAST> namedActions = + new HashMap<String, ActionAST>(); + + /** Track exception handlers; points at "catch" node of (catch exception action) + * don't track finally action + */ + public List<GrammarAST> exceptions = new ArrayList<GrammarAST>(); + + /** Track all executable actions other than named actions like @init + * and catch/finally (not in an alt). Also tracks predicates, rewrite actions. + * We need to examine these actions before code generation so + * that we can detect refs to $rule.attr etc... + * + * This tracks per rule; Alternative objs also track per alt. + */ + public List<ActionAST> actions = new ArrayList<ActionAST>(); + + public ActionAST finallyAction; + + public int numberOfAlts; + + public boolean isStartRule = true; // nobody calls us + + /** 1..n alts */ + public Alternative[] alt; + + /** All rules have unique index 0..n-1 */ + public int index; + + public int actionIndex = -1; // if lexer; 0..n-1 for n actions in a rule + + public Rule(Grammar g, String name, RuleAST ast, int numberOfAlts) { + this.g = g; + this.name = name; + this.ast = ast; + this.numberOfAlts = numberOfAlts; + alt = new Alternative[numberOfAlts+1]; // 1..n + for (int i=1; i<=numberOfAlts; i++) alt[i] = new Alternative(this, i); + } + + public void defineActionInAlt(int currentAlt, ActionAST actionAST) { + actions.add(actionAST); + alt[currentAlt].actions.add(actionAST); + if ( g.isLexer() ) { + defineLexerAction(actionAST); + } + } + + /** Lexer actions are numbered across rules 0..n-1 */ + public void defineLexerAction(ActionAST actionAST) { + actionIndex = g.lexerActions.size(); + if ( g.lexerActions.get(actionAST)==null ) { + g.lexerActions.put(actionAST, actionIndex); + } + } + + public void definePredicateInAlt(int currentAlt, PredAST predAST) { + actions.add(predAST); + alt[currentAlt].actions.add(predAST); + if ( g.sempreds.get(predAST)==null ) { + g.sempreds.put(predAST, g.sempreds.size()); + } + } + + public Attribute resolveRetvalOrProperty(String y) { + if ( retvals!=null ) { + Attribute a = retvals.get(y); + if ( a!=null ) return a; + } + AttributeDict d = getPredefinedScope(LabelType.RULE_LABEL); + return d.get(y); + } + + public Set<String> getTokenRefs() { + Set<String> refs = new HashSet<String>(); + for (int i=1; i<=numberOfAlts; i++) { + refs.addAll(alt[i].tokenRefs.keySet()); + } + return refs; + } + + public Set<String> getElementLabelNames() { + Set<String> refs = new HashSet<String>(); + for (int i=1; i<=numberOfAlts; i++) { + refs.addAll(alt[i].labelDefs.keySet()); + } + if ( refs.isEmpty() ) return null; + return refs; + } + + public MultiMap<String, LabelElementPair> getElementLabelDefs() { + MultiMap<String, LabelElementPair> defs = + new MultiMap<String, LabelElementPair>(); + for (int i=1; i<=numberOfAlts; i++) { + for (List<LabelElementPair> pairs : alt[i].labelDefs.values()) { + for (LabelElementPair p : pairs) { + defs.map(p.label.getText(), p); + } + } + } + return defs; + } + + public boolean hasAltSpecificContexts() { + return getAltLabels()!=null; + } + + /** Used for recursive rules (subclass), which have 1 alt, but many original alts */ + public int getOriginalNumberOfAlts() { + return numberOfAlts; + } + + /** + * Get {@code #} labels. The keys of the map are the labels applied to outer + * alternatives of a lexer rule, and the values are collections of pairs + * (alternative number and {@link AltAST}) identifying the alternatives with + * this label. Unlabeled alternatives are not included in the result. + */ + public Map<String, List<Pair<Integer, AltAST>>> getAltLabels() { + Map<String, List<Pair<Integer, AltAST>>> labels = new LinkedHashMap<String, List<Pair<Integer, AltAST>>>(); + for (int i=1; i<=numberOfAlts; i++) { + GrammarAST altLabel = alt[i].ast.altLabel; + if ( altLabel!=null ) { + List<Pair<Integer, AltAST>> list = labels.get(altLabel.getText()); + if (list == null) { + list = new ArrayList<Pair<Integer, AltAST>>(); + labels.put(altLabel.getText(), list); + } + + list.add(new Pair<Integer, AltAST>(i, alt[i].ast)); + } + } + if ( labels.isEmpty() ) return null; + return labels; + } + + public List<AltAST> getUnlabeledAltASTs() { + List<AltAST> alts = new ArrayList<AltAST>(); + for (int i=1; i<=numberOfAlts; i++) { + GrammarAST altLabel = alt[i].ast.altLabel; + if ( altLabel==null ) alts.add(alt[i].ast); + } + if ( alts.isEmpty() ) return null; + return alts; + } + + /** $x Attribute: rule arguments, return values, predefined rule prop. + */ + @Override + public Attribute resolveToAttribute(String x, ActionAST node) { + if ( args!=null ) { + Attribute a = args.get(x); if ( a!=null ) return a; + } + if ( retvals!=null ) { + Attribute a = retvals.get(x); if ( a!=null ) return a; + } + if ( locals!=null ) { + Attribute a = locals.get(x); if ( a!=null ) return a; + } + AttributeDict properties = getPredefinedScope(LabelType.RULE_LABEL); + return properties.get(x); + } + + /** $x.y Attribute: x is surrounding rule, label ref (in any alts) */ + @Override + public Attribute resolveToAttribute(String x, String y, ActionAST node) { + LabelElementPair anyLabelDef = getAnyLabelDef(x); + if ( anyLabelDef!=null ) { + if ( anyLabelDef.type==LabelType.RULE_LABEL ) { + return g.getRule(anyLabelDef.element.getText()).resolveRetvalOrProperty(y); + } + else { + AttributeDict scope = getPredefinedScope(anyLabelDef.type); + if (scope == null) { + return null; + } + + return scope.get(y); + } + } + return null; + + } + + @Override + public boolean resolvesToLabel(String x, ActionAST node) { + LabelElementPair anyLabelDef = getAnyLabelDef(x); + return anyLabelDef!=null && + (anyLabelDef.type==LabelType.RULE_LABEL || + anyLabelDef.type==LabelType.TOKEN_LABEL); + } + + @Override + public boolean resolvesToListLabel(String x, ActionAST node) { + LabelElementPair anyLabelDef = getAnyLabelDef(x); + return anyLabelDef!=null && + (anyLabelDef.type==LabelType.RULE_LIST_LABEL || + anyLabelDef.type==LabelType.TOKEN_LIST_LABEL); + } + + @Override + public boolean resolvesToToken(String x, ActionAST node) { + LabelElementPair anyLabelDef = getAnyLabelDef(x); + if ( anyLabelDef!=null && anyLabelDef.type==LabelType.TOKEN_LABEL ) return true; + return false; + } + + @Override + public boolean resolvesToAttributeDict(String x, ActionAST node) { + if ( resolvesToToken(x, node) ) return true; + return false; + } + + public Rule resolveToRule(String x) { + if ( x.equals(this.name) ) return this; + LabelElementPair anyLabelDef = getAnyLabelDef(x); + if ( anyLabelDef!=null && anyLabelDef.type==LabelType.RULE_LABEL ) { + return g.getRule(anyLabelDef.element.getText()); + } + return g.getRule(x); + } + + public LabelElementPair getAnyLabelDef(String x) { + List<LabelElementPair> labels = getElementLabelDefs().get(x); + if ( labels!=null ) return labels.get(0); + return null; + } + + public AttributeDict getPredefinedScope(LabelType ltype) { + String grammarLabelKey = g.getTypeString() + ":" + ltype; + return Grammar.grammarAndLabelRefTypeToScope.get(grammarLabelKey); + } + + public boolean isFragment() { + if ( modifiers==null ) return false; + for (GrammarAST a : modifiers) { + if ( a.getText().equals("fragment") ) return true; + } + return false; + } + + @Override + public int hashCode() { return name.hashCode(); } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (!(obj instanceof Rule)) { + return false; + } + + return name.equals(((Rule)obj).name); + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append("Rule{name=").append(name); + if ( args!=null ) buf.append(", args=").append(args); + if ( retvals!=null ) buf.append(", retvals=").append(retvals); + buf.append("}"); + return buf.toString(); + } +} diff --git a/tool/src/org/antlr/v4/tool/ToolMessage.java b/tool/src/org/antlr/v4/tool/ToolMessage.java new file mode 100644 index 0000000..422eda2 --- /dev/null +++ b/tool/src/org/antlr/v4/tool/ToolMessage.java @@ -0,0 +1,53 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.runtime.Token; + +/** A generic message from the tool such as "file not found" type errors; there + * is no reason to create a special object for each error unlike the grammar + * errors, which may be rather complex. + * + * Sometimes you need to pass in a filename or something to say it is "bad". + * Allow a generic object to be passed in and the string template can deal + * with just printing it or pulling a property out of it. + */ +public class ToolMessage extends ANTLRMessage { + public ToolMessage(ErrorType errorType) { + super(errorType); + } + public ToolMessage(ErrorType errorType, Object... args) { + super(errorType, null, Token.INVALID_TOKEN, args); + } + public ToolMessage(ErrorType errorType, Throwable e, Object... args) { + super(errorType, e, Token.INVALID_TOKEN, args); + } +} diff --git a/tool/src/org/antlr/v4/tool/ast/ActionAST.java b/tool/src/org/antlr/v4/tool/ast/ActionAST.java new file mode 100644 index 0000000..8daf349 --- /dev/null +++ b/tool/src/org/antlr/v4/tool/ast/ActionAST.java @@ -0,0 +1,58 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.ast; + +import org.antlr.runtime.Token; +import org.antlr.v4.tool.AttributeResolver; + +import java.util.List; + +public class ActionAST extends GrammarASTWithOptions implements RuleElementAST { + // Alt, rule, grammar space + public AttributeResolver resolver; + public List<Token> chunks; // useful for ANTLR IDE developers + + public ActionAST(ActionAST node) { + super(node); + this.resolver = node.resolver; + this.chunks = node.chunks; + } + + public ActionAST(Token t) { super(t); } + public ActionAST(int type) { super(type); } + public ActionAST(int type, Token t) { super(type, t); } + + @Override + public ActionAST dupNode() { return new ActionAST(this); } + + @Override + public Object visit(GrammarASTVisitor v) { return v.visit(this); } +} diff --git a/tool/src/org/antlr/v4/tool/ast/AltAST.java b/tool/src/org/antlr/v4/tool/ast/AltAST.java new file mode 100644 index 0000000..77e2106 --- /dev/null +++ b/tool/src/org/antlr/v4/tool/ast/AltAST.java @@ -0,0 +1,66 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.ast; + +import org.antlr.runtime.Token; +import org.antlr.v4.analysis.LeftRecursiveRuleAltInfo; +import org.antlr.v4.tool.Alternative; + +/** Any ALT (which can be child of ALT_REWRITE node) */ +public class AltAST extends GrammarASTWithOptions { + public Alternative alt; + + /** If we transformed this alt from a left-recursive one, need info on it */ + public LeftRecursiveRuleAltInfo leftRecursiveAltInfo; + + /** If someone specified an outermost alternative label with #foo. + * Token type will be ID. + */ + public GrammarAST altLabel; + + public AltAST(AltAST node) { + super(node); + this.alt = node.alt; + this.altLabel = node.altLabel; + this.leftRecursiveAltInfo = node.leftRecursiveAltInfo; + } + + public AltAST(Token t) { super(t); } + public AltAST(int type) { super(type); } + public AltAST(int type, Token t) { super(type, t); } + public AltAST(int type, Token t, String text) { super(type,t,text); } + + @Override + public AltAST dupNode() { return new AltAST(this); } + + @Override + public Object visit(GrammarASTVisitor v) { return v.visit(this); } +} diff --git a/tool/src/org/antlr/v4/tool/ast/BlockAST.java b/tool/src/org/antlr/v4/tool/ast/BlockAST.java new file mode 100644 index 0000000..6e45e96 --- /dev/null +++ b/tool/src/org/antlr/v4/tool/ast/BlockAST.java @@ -0,0 +1,61 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.ast; + +import org.antlr.runtime.Token; + +import java.util.HashMap; +import java.util.Map; + +public class BlockAST extends GrammarASTWithOptions implements RuleElementAST { + // TODO: maybe I need a Subrule object like Rule so these options mov to that? + /** What are the default options for a subrule? */ + public static final Map<String, String> defaultBlockOptions = + new HashMap<String, String>(); + + public static final Map<String, String> defaultLexerBlockOptions = + new HashMap<String, String>(); + + public BlockAST(BlockAST node) { + super(node); + } + + public BlockAST(Token t) { super(t); } + public BlockAST(int type) { super(type); } + public BlockAST(int type, Token t) { super(type, t); } + public BlockAST(int type, Token t, String text) { super(type,t,text); } + + @Override + public BlockAST dupNode() { return new BlockAST(this); } + + @Override + public Object visit(GrammarASTVisitor v) { return v.visit(this); } +} diff --git a/tool/src/org/antlr/v4/tool/ast/GrammarAST.java b/tool/src/org/antlr/v4/tool/ast/GrammarAST.java new file mode 100644 index 0000000..a794b88 --- /dev/null +++ b/tool/src/org/antlr/v4/tool/ast/GrammarAST.java @@ -0,0 +1,264 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.ast; + +import org.antlr.runtime.CharStream; +import org.antlr.runtime.CommonToken; +import org.antlr.runtime.Token; +import org.antlr.runtime.tree.CommonTree; +import org.antlr.runtime.tree.CommonTreeNodeStream; +import org.antlr.runtime.tree.Tree; +import org.antlr.v4.parse.ANTLRParser; +import org.antlr.v4.parse.GrammarASTAdaptor; +import org.antlr.v4.runtime.atn.ATNState; +import org.antlr.v4.runtime.misc.IntervalSet; +import org.antlr.v4.tool.Grammar; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +public class GrammarAST extends CommonTree { + /** For error msgs, nice to know which grammar this AST lives in */ + // TODO: try to remove + public Grammar g; + + /** If we build an ATN, we make AST node point at left edge of ATN construct */ + public ATNState atnState; + + public String textOverride; + + public GrammarAST() {} + public GrammarAST(Token t) { super(t); } + public GrammarAST(GrammarAST node) { + super(node); + this.g = node.g; + this.atnState = node.atnState; + this.textOverride = node.textOverride; + } + public GrammarAST(int type) { super(new CommonToken(type, ANTLRParser.tokenNames[type])); } + public GrammarAST(int type, Token t) { + this(new CommonToken(t)); + token.setType(type); + } + public GrammarAST(int type, Token t, String text) { + this(new CommonToken(t)); + token.setType(type); + token.setText(text); + } + + public GrammarAST[] getChildrenAsArray() { + return children.toArray(new GrammarAST[children.size()]); + } + + public List<GrammarAST> getNodesWithType(int ttype) { + return getNodesWithType(IntervalSet.of(ttype)); + } + + public List<GrammarAST> getAllChildrenWithType(int type) { + List<GrammarAST> nodes = new ArrayList<GrammarAST>(); + for (int i = 0; children!=null && i < children.size(); i++) { + Tree t = (Tree) children.get(i); + if ( t.getType()==type ) { + nodes.add((GrammarAST)t); + } + } + return nodes; + } + + public List<GrammarAST> getNodesWithType(IntervalSet types) { + List<GrammarAST> nodes = new ArrayList<GrammarAST>(); + List<GrammarAST> work = new LinkedList<GrammarAST>(); + work.add(this); + GrammarAST t; + while ( !work.isEmpty() ) { + t = work.remove(0); + if ( types==null || types.contains(t.getType()) ) nodes.add(t); + if ( t.children!=null ) { + work.addAll(Arrays.asList(t.getChildrenAsArray())); + } + } + return nodes; + } + + public List<GrammarAST> getNodesWithTypePreorderDFS(IntervalSet types) { + ArrayList<GrammarAST> nodes = new ArrayList<GrammarAST>(); + getNodesWithTypePreorderDFS_(nodes, types); + return nodes; + } + + public void getNodesWithTypePreorderDFS_(List<GrammarAST> nodes, IntervalSet types) { + if ( types.contains(this.getType()) ) nodes.add(this); + // walk all children of root. + for (int i= 0; i < getChildCount(); i++) { + GrammarAST child = (GrammarAST)getChild(i); + child.getNodesWithTypePreorderDFS_(nodes, types); + } + } + + public GrammarAST getNodeWithTokenIndex(int index) { + if ( this.getToken()!=null && this.getToken().getTokenIndex()==index ) { + return this; + } + // walk all children of root. + for (int i= 0; i < getChildCount(); i++) { + GrammarAST child = (GrammarAST)getChild(i); + GrammarAST result = child.getNodeWithTokenIndex(index); + if ( result!=null ) { + return result; + } + } + return null; + } + + public AltAST getOutermostAltNode() { + if ( this instanceof AltAST && parent.parent instanceof RuleAST ) { + return (AltAST)this; + } + if ( parent!=null ) return ((GrammarAST)parent).getOutermostAltNode(); + return null; + } + + /** Walk ancestors of this node until we find ALT with + * alt!=null or leftRecursiveAltInfo!=null. Then grab label if any. + * If not a rule element, just returns null. + */ + public String getAltLabel() { + List<? extends Tree> ancestors = this.getAncestors(); + if ( ancestors==null ) return null; + for (int i=ancestors.size()-1; i>=0; i--) { + GrammarAST p = (GrammarAST)ancestors.get(i); + if ( p.getType()== ANTLRParser.ALT ) { + AltAST a = (AltAST)p; + if ( a.altLabel!=null ) return a.altLabel.getText(); + if ( a.leftRecursiveAltInfo!=null ) { + return a.leftRecursiveAltInfo.altLabel; + } + } + } + return null; + } + + public boolean deleteChild(org.antlr.runtime.tree.Tree t) { + for (int i=0; i<children.size(); i++) { + Object c = children.get(i); + if ( c == t ) { + deleteChild(t.getChildIndex()); + return true; + } + } + return false; + } + + // TODO: move to basetree when i settle on how runtime works + // TODO: don't include this node!! + // TODO: reuse other method + public CommonTree getFirstDescendantWithType(int type) { + if ( getType()==type ) return this; + if ( children==null ) return null; + for (Object c : children) { + GrammarAST t = (GrammarAST)c; + if ( t.getType()==type ) return t; + CommonTree d = t.getFirstDescendantWithType(type); + if ( d!=null ) return d; + } + return null; + } + + // TODO: don't include this node!! + public CommonTree getFirstDescendantWithType(org.antlr.runtime.BitSet types) { + if ( types.member(getType()) ) return this; + if ( children==null ) return null; + for (Object c : children) { + GrammarAST t = (GrammarAST)c; + if ( types.member(t.getType()) ) return t; + CommonTree d = t.getFirstDescendantWithType(types); + if ( d!=null ) return d; + } + return null; + } + + public void setType(int type) { + token.setType(type); + } +// +// @Override +// public String getText() { +// if ( textOverride!=null ) return textOverride; +// if ( token!=null ) { +// return token.getText(); +// } +// return ""; +// } + + public void setText(String text) { +// textOverride = text; // don't alt tokens as others might see + token.setText(text); // we delete surrounding tree, so ok to alter + } + +// @Override +// public boolean equals(Object obj) { +// return super.equals(obj); +// } + + @Override + public GrammarAST dupNode() { + return new GrammarAST(this); + } + + public GrammarAST dupTree() { + GrammarAST t = this; + CharStream input = this.token.getInputStream(); + GrammarASTAdaptor adaptor = new GrammarASTAdaptor(input); + return (GrammarAST)adaptor.dupTree(t); + } + + public String toTokenString() { + CharStream input = this.token.getInputStream(); + GrammarASTAdaptor adaptor = new GrammarASTAdaptor(input); + CommonTreeNodeStream nodes = + new CommonTreeNodeStream(adaptor, this); + StringBuilder buf = new StringBuilder(); + GrammarAST o = (GrammarAST)nodes.LT(1); + int type = adaptor.getType(o); + while ( type!=Token.EOF ) { + buf.append(" "); + buf.append(o.getText()); + nodes.consume(); + o = (GrammarAST)nodes.LT(1); + type = adaptor.getType(o); + } + return buf.toString(); + } + + public Object visit(GrammarASTVisitor v) { return v.visit(this); } +} diff --git a/tool/src/org/antlr/v4/tool/ast/GrammarASTErrorNode.java b/tool/src/org/antlr/v4/tool/ast/GrammarASTErrorNode.java new file mode 100644 index 0000000..3b2094b --- /dev/null +++ b/tool/src/org/antlr/v4/tool/ast/GrammarASTErrorNode.java @@ -0,0 +1,56 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.ast; + +import org.antlr.runtime.Token; +import org.antlr.runtime.TokenStream; +import org.antlr.runtime.tree.CommonErrorNode; + +/** A node representing erroneous token range in token stream */ +public class GrammarASTErrorNode extends GrammarAST { + CommonErrorNode delegate; + public GrammarASTErrorNode(TokenStream input, Token start, Token stop, + org.antlr.runtime.RecognitionException e) + { + delegate = new CommonErrorNode(input,start,stop,e); + } + + @Override + public boolean isNil() { return delegate.isNil(); } + + @Override + public int getType() { return delegate.getType(); } + + @Override + public String getText() { return delegate.getText(); } + @Override + public String toString() { return delegate.toString(); } +} diff --git a/tool/src/org/antlr/v4/tool/ast/GrammarASTVisitor.java b/tool/src/org/antlr/v4/tool/ast/GrammarASTVisitor.java new file mode 100644 index 0000000..a11deb1 --- /dev/null +++ b/tool/src/org/antlr/v4/tool/ast/GrammarASTVisitor.java @@ -0,0 +1,68 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.ast; + +/** A simple visitor, based upon the classic double dispatch method, + * for walking GrammarAST trees resulting from parsing ANTLR grammars. + * There is also the GrammarTreeVisitor.g tree grammar that looks for + * subtree patterns and fires off high-level events as opposed to + * "found node" events like this visitor does. Also, like all + * visitors, the users of this interface are required to implement + * the node visitation of the children. The GrammarTreeVisitor mechanism + * fires events and the user is not required to do any walking code. + * + * GrammarAST t = ...; + * GrammarASTVisitor v = new ...; + * t.visit(v); + */ +public interface GrammarASTVisitor { + /** This is the generic visitor method that will be invoked + * for any other kind of AST node not covered by the other visit methods. + */ + Object visit(GrammarAST node); + + Object visit(GrammarRootAST node); + Object visit(RuleAST node); + + Object visit(BlockAST node); + Object visit(OptionalBlockAST node); + Object visit(PlusBlockAST node); + Object visit(StarBlockAST node); + + Object visit(AltAST node); + + Object visit(NotAST node); + Object visit(PredAST node); + Object visit(RangeAST node); + Object visit(SetAST node); + Object visit(RuleRefAST node); + Object visit(TerminalAST node); +} diff --git a/tool/src/org/antlr/v4/tool/ast/GrammarASTWithOptions.java b/tool/src/org/antlr/v4/tool/ast/GrammarASTWithOptions.java new file mode 100644 index 0000000..b492c93 --- /dev/null +++ b/tool/src/org/antlr/v4/tool/ast/GrammarASTWithOptions.java @@ -0,0 +1,96 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.ast; + +import org.antlr.runtime.Token; +import org.antlr.v4.misc.CharSupport; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public abstract class GrammarASTWithOptions extends GrammarAST { + protected Map<String, GrammarAST> options; + + public GrammarASTWithOptions(GrammarASTWithOptions node) { + super(node); + this.options = node.options; + } + + public GrammarASTWithOptions(Token t) { super(t); } + public GrammarASTWithOptions(int type) { super(type); } + public GrammarASTWithOptions(int type, Token t) { super(type, t); } + public GrammarASTWithOptions(int type, Token t, String text) { super(type,t,text); } + + public void setOption(String key, GrammarAST node) { + if ( options==null ) options = new HashMap<String, GrammarAST>(); + options.put(key, node); + } + + public String getOptionString(String key) { + GrammarAST value = getOptionAST(key); + if ( value == null ) return null; + if ( value instanceof ActionAST ) { + return value.getText(); + } + else { + String v = value.getText(); + if ( v.startsWith("'") || v.startsWith("\"") ) { + v = CharSupport.getStringFromGrammarStringLiteral(v); + } + return v; + } + } + + /** Gets AST node holding value for option key; ignores default options + * and command-line forced options. + */ + public GrammarAST getOptionAST(String key) { + if ( options==null ) return null; + return options.get(key); + } + + public int getNumberOfOptions() { + return options==null ? 0 : options.size(); + } + + @Override + public abstract GrammarASTWithOptions dupNode(); + + + public Map<String, GrammarAST> getOptions() { + if (options == null) { + return Collections.emptyMap(); + } + + return options; + } +} diff --git a/tool/src/org/antlr/v4/tool/ast/GrammarRootAST.java b/tool/src/org/antlr/v4/tool/ast/GrammarRootAST.java new file mode 100644 index 0000000..bf4ef96 --- /dev/null +++ b/tool/src/org/antlr/v4/tool/ast/GrammarRootAST.java @@ -0,0 +1,111 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.ast; + +import org.antlr.runtime.Token; +import org.antlr.runtime.TokenStream; +import org.antlr.runtime.tree.Tree; + +import java.util.HashMap; +import java.util.Map; + +public class GrammarRootAST extends GrammarASTWithOptions { + public static final Map<String, String> defaultOptions = new HashMap<String, String>(); + static { + defaultOptions.put("language","Java"); + } + + public int grammarType; // LEXER, PARSER, GRAMMAR (combined) + public boolean hasErrors; + /** Track stream used to create this tree */ + + public final TokenStream tokenStream; + public Map<String, String> cmdLineOptions; // -DsuperClass=T on command line + public String fileName; + + public GrammarRootAST(GrammarRootAST node) { + super(node); + this.grammarType = node.grammarType; + this.hasErrors = node.hasErrors; + this.tokenStream = node.tokenStream; + } + + public GrammarRootAST(Token t, TokenStream tokenStream) { + super(t); + if (tokenStream == null) { + throw new NullPointerException("tokenStream"); + } + + this.tokenStream = tokenStream; + } + + public GrammarRootAST(int type, Token t, TokenStream tokenStream) { + super(type, t); + if (tokenStream == null) { + throw new NullPointerException("tokenStream"); + } + + this.tokenStream = tokenStream; + } + + public GrammarRootAST(int type, Token t, String text, TokenStream tokenStream) { + super(type,t,text); + if (tokenStream == null) { + throw new NullPointerException("tokenStream"); + } + + this.tokenStream = tokenStream; + } + + public String getGrammarName() { + Tree t = getChild(0); + if ( t!=null ) return t.getText(); + return null; + } + + @Override + public String getOptionString(String key) { + if ( cmdLineOptions!=null && cmdLineOptions.containsKey(key) ) { + return cmdLineOptions.get(key); + } + String value = super.getOptionString(key); + if ( value==null ) { + value = defaultOptions.get(key); + } + return value; + } + + @Override + public Object visit(GrammarASTVisitor v) { return v.visit(this); } + + @Override + public GrammarRootAST dupNode() { return new GrammarRootAST(this); } +} diff --git a/tool/src/org/antlr/v4/tool/ast/NotAST.java b/tool/src/org/antlr/v4/tool/ast/NotAST.java new file mode 100644 index 0000000..0d943e6 --- /dev/null +++ b/tool/src/org/antlr/v4/tool/ast/NotAST.java @@ -0,0 +1,50 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.ast; + +import org.antlr.runtime.Token; + +public class NotAST extends GrammarAST implements RuleElementAST { + + public NotAST(NotAST node) { + super(node); + } + + public NotAST(int type, Token t) { super(type, t); } + + @Override + public NotAST dupNode() { + return new NotAST(this); + } + + @Override + public Object visit(GrammarASTVisitor v) { return v.visit(this); } +} diff --git a/tool/src/org/antlr/v4/tool/ast/OptionalBlockAST.java b/tool/src/org/antlr/v4/tool/ast/OptionalBlockAST.java new file mode 100644 index 0000000..fafdfc3 --- /dev/null +++ b/tool/src/org/antlr/v4/tool/ast/OptionalBlockAST.java @@ -0,0 +1,59 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.ast; + +import org.antlr.runtime.Token; + +public class OptionalBlockAST extends GrammarAST implements RuleElementAST, QuantifierAST { + private final boolean _greedy; + + public OptionalBlockAST(OptionalBlockAST node) { + super(node); + _greedy = node._greedy; + } + + public OptionalBlockAST(int type, Token t, Token nongreedy) { + super(type, t); + _greedy = nongreedy == null; + } + + @Override + public boolean isGreedy() { + return _greedy; + } + + @Override + public OptionalBlockAST dupNode() { return new OptionalBlockAST(this); } + + @Override + public Object visit(GrammarASTVisitor v) { return v.visit(this); } + +} diff --git a/tool/src/org/antlr/v4/tool/ast/PlusBlockAST.java b/tool/src/org/antlr/v4/tool/ast/PlusBlockAST.java new file mode 100644 index 0000000..2bd97be --- /dev/null +++ b/tool/src/org/antlr/v4/tool/ast/PlusBlockAST.java @@ -0,0 +1,58 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.ast; + +import org.antlr.runtime.Token; + +public class PlusBlockAST extends GrammarAST implements RuleElementAST, QuantifierAST { + private final boolean _greedy; + + public PlusBlockAST(PlusBlockAST node) { + super(node); + _greedy = node._greedy; + } + + public PlusBlockAST(int type, Token t, Token nongreedy) { + super(type, t); + _greedy = nongreedy == null; + } + + @Override + public boolean isGreedy() { + return _greedy; + } + + @Override + public PlusBlockAST dupNode() { return new PlusBlockAST(this); } + + @Override + public Object visit(GrammarASTVisitor v) { return v.visit(this); } +} diff --git a/tool/src/org/antlr/v4/tool/ast/PredAST.java b/tool/src/org/antlr/v4/tool/ast/PredAST.java new file mode 100644 index 0000000..c7d0706 --- /dev/null +++ b/tool/src/org/antlr/v4/tool/ast/PredAST.java @@ -0,0 +1,49 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.ast; + +import org.antlr.runtime.Token; + +public class PredAST extends ActionAST { + public PredAST(PredAST node) { + super(node); + } + + public PredAST(Token t) { super(t); } + public PredAST(int type) { super(type); } + public PredAST(int type, Token t) { super(type, t); } + + @Override + public PredAST dupNode() { return new PredAST(this); } + + @Override + public Object visit(GrammarASTVisitor v) { return v.visit(this); } +} diff --git a/tool/src/org/antlr/v4/tool/ast/QuantifierAST.java b/tool/src/org/antlr/v4/tool/ast/QuantifierAST.java new file mode 100644 index 0000000..0869ca7 --- /dev/null +++ b/tool/src/org/antlr/v4/tool/ast/QuantifierAST.java @@ -0,0 +1,41 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.ast; + +/** + * + * @author Sam Harwell + */ +public interface QuantifierAST { + + boolean isGreedy(); + +} diff --git a/tool/src/org/antlr/v4/tool/ast/RangeAST.java b/tool/src/org/antlr/v4/tool/ast/RangeAST.java new file mode 100644 index 0000000..54ce96a --- /dev/null +++ b/tool/src/org/antlr/v4/tool/ast/RangeAST.java @@ -0,0 +1,50 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.ast; + +import org.antlr.runtime.Token; + +public class RangeAST extends GrammarAST implements RuleElementAST { + + public RangeAST(RangeAST node) { + super(node); + } + + public RangeAST(Token t) { super(t); } + + @Override + public RangeAST dupNode() { + return new RangeAST(this); + } + + @Override + public Object visit(GrammarASTVisitor v) { return v.visit(this); } +} diff --git a/tool/src/org/antlr/v4/tool/ast/RuleAST.java b/tool/src/org/antlr/v4/tool/ast/RuleAST.java new file mode 100644 index 0000000..0db780a --- /dev/null +++ b/tool/src/org/antlr/v4/tool/ast/RuleAST.java @@ -0,0 +1,74 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.ast; + +import org.antlr.runtime.Token; +import org.antlr.runtime.tree.Tree; +import org.antlr.v4.parse.ANTLRParser; +import org.antlr.v4.tool.Grammar; + +public class RuleAST extends GrammarASTWithOptions { + public RuleAST(RuleAST node) { + super(node); + } + + public RuleAST(Token t) { super(t); } + public RuleAST(int type) { super(type); } + + public boolean isLexerRule() { + String name = getRuleName(); + return name!=null && Grammar.isTokenName(name); + } + + public String getRuleName() { + GrammarAST nameNode = (GrammarAST)getChild(0); + if ( nameNode!=null ) return nameNode.getText(); + return null; + } + + @Override + public RuleAST dupNode() { return new RuleAST(this); } + + public ActionAST getLexerAction() { + Tree blk = getFirstChildWithType(ANTLRParser.BLOCK); + if ( blk.getChildCount()==1 ) { + Tree onlyAlt = blk.getChild(0); + Tree lastChild = onlyAlt.getChild(onlyAlt.getChildCount()-1); + if ( lastChild.getType()==ANTLRParser.ACTION ) { + return (ActionAST)lastChild; + } + } + return null; + } + + @Override + public Object visit(GrammarASTVisitor v) { return v.visit(this); } +} diff --git a/tool/src/org/antlr/v4/tool/ast/RuleElementAST.java b/tool/src/org/antlr/v4/tool/ast/RuleElementAST.java new file mode 100644 index 0000000..e5118fa --- /dev/null +++ b/tool/src/org/antlr/v4/tool/ast/RuleElementAST.java @@ -0,0 +1,35 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.ast; + +/** Tag indicated AST node is a rule element like token or rule ref. */ +public interface RuleElementAST { +} diff --git a/tool/src/org/antlr/v4/tool/ast/RuleRefAST.java b/tool/src/org/antlr/v4/tool/ast/RuleRefAST.java new file mode 100644 index 0000000..822c142 --- /dev/null +++ b/tool/src/org/antlr/v4/tool/ast/RuleRefAST.java @@ -0,0 +1,60 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.ast; + +import org.antlr.runtime.CommonToken; +import org.antlr.runtime.Token; + +public class RuleRefAST extends GrammarASTWithOptions implements RuleElementAST { + public RuleRefAST(RuleRefAST node) { + super(node); + } + + public RuleRefAST(Token t) { super(t); } + public RuleRefAST(int type) { super(type); } + public RuleRefAST(int type, Token t) { super(type, t); } + + /** Dup token too since we overwrite during LR rule transform */ + @Override + public RuleRefAST dupNode() { + RuleRefAST r = new RuleRefAST(this); + // In LR transform, we alter original token stream to make e -> e[n] + // Since we will be altering the dup, we need dup to have the + // original token. We can set this tree (the original) to have + // a new token. + r.token = this.token; + this.token = new CommonToken(r.token); + return r; + } + + @Override + public Object visit(GrammarASTVisitor v) { return v.visit(this); } +} diff --git a/tool/src/org/antlr/v4/tool/ast/SetAST.java b/tool/src/org/antlr/v4/tool/ast/SetAST.java new file mode 100644 index 0000000..9c06b49 --- /dev/null +++ b/tool/src/org/antlr/v4/tool/ast/SetAST.java @@ -0,0 +1,50 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.ast; + +import org.antlr.runtime.Token; + +public class SetAST extends GrammarAST implements RuleElementAST { + + public SetAST(SetAST node) { + super(node); + } + + public SetAST(int type, Token t, String text) { super(type,t,text); } + + @Override + public SetAST dupNode() { + return new SetAST(this); + } + + @Override + public Object visit(GrammarASTVisitor v) { return v.visit(this); } +} diff --git a/tool/src/org/antlr/v4/tool/ast/StarBlockAST.java b/tool/src/org/antlr/v4/tool/ast/StarBlockAST.java new file mode 100644 index 0000000..58c7b45 --- /dev/null +++ b/tool/src/org/antlr/v4/tool/ast/StarBlockAST.java @@ -0,0 +1,58 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.ast; + +import org.antlr.runtime.Token; + +public class StarBlockAST extends GrammarAST implements RuleElementAST, QuantifierAST { + private final boolean _greedy; + + public StarBlockAST(StarBlockAST node) { + super(node); + _greedy = node._greedy; + } + + public StarBlockAST(int type, Token t, Token nongreedy) { + super(type, t); + _greedy = nongreedy == null; + } + + @Override + public boolean isGreedy() { + return _greedy; + } + + @Override + public StarBlockAST dupNode() { return new StarBlockAST(this); } + + @Override + public Object visit(GrammarASTVisitor v) { return v.visit(this); } +} diff --git a/tool/src/org/antlr/v4/tool/ast/TerminalAST.java b/tool/src/org/antlr/v4/tool/ast/TerminalAST.java new file mode 100644 index 0000000..26114f3 --- /dev/null +++ b/tool/src/org/antlr/v4/tool/ast/TerminalAST.java @@ -0,0 +1,50 @@ +/* + * [The "BSD license"] + * Copyright (c) 2012 Terence Parr + * Copyright (c) 2012 Sam Harwell + * 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.ast; + +import org.antlr.runtime.Token; + +public class TerminalAST extends GrammarASTWithOptions implements RuleElementAST { + + public TerminalAST(TerminalAST node) { + super(node); + } + + public TerminalAST(Token t) { super(t); } + public TerminalAST(int type) { super(type); } + public TerminalAST(int type, Token t) { super(type, t); } + + @Override + public TerminalAST dupNode() { return new TerminalAST(this); } + + @Override + public Object visit(GrammarASTVisitor v) { return v.visit(this); } +} |