Added visitor gen mechanism, runtime support

This commit is contained in:
Terence Parr 2012-02-16 17:49:57 -08:00
parent 66e7e0fad2
commit 725b105135
22 changed files with 400 additions and 94 deletions

View File

@ -28,16 +28,11 @@
*/ */
package org.antlr.v4.runtime; package org.antlr.v4.runtime;
import org.antlr.v4.runtime.atn.ATN; import org.antlr.v4.runtime.atn.*;
import org.antlr.v4.runtime.atn.ATNState; import org.antlr.v4.runtime.misc.*;
import org.antlr.v4.runtime.misc.NotNull; import org.antlr.v4.runtime.tree.*;
import org.antlr.v4.runtime.misc.Nullable;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.ParseTreeListener;
import java.util.ArrayList; import java.util.*;
import java.util.Collections;
import java.util.List;
/** A rule invocation record for parsing and tree parsing. /** A rule invocation record for parsing and tree parsing.
* *
@ -131,10 +126,11 @@ public class ParserRuleContext<Symbol> extends RuleContext {
this(parent, parent!=null ? parent.s : -1 /* invoking state */, stateNumber); this(parent, parent!=null ? parent.s : -1 /* invoking state */, stateNumber);
} }
// Double dispatch methods // Double dispatch methods for listeners and visitors
public void enterRule(ParseTreeListener<Symbol> listener) { } public void enterRule(ParseTreeListener<Symbol> listener) { }
public void exitRule(ParseTreeListener<Symbol> listener) { } public void exitRule(ParseTreeListener<Symbol> listener) { }
public <T> T accept(ParseTreeVisitor<? extends T> visitor) { visitor.visitChildren(this); return null; }
/** Does not set parent link; other add methods do */ /** Does not set parent link; other add methods do */
public void addChild(TerminalNode<Symbol> t) { public void addChild(TerminalNode<Symbol> t) {

View File

@ -0,0 +1,26 @@
package org.antlr.v4.runtime.tree;
import org.antlr.v4.runtime.ParserRuleContext;
/** T is return type of visit methods. Use T=Void for no return type. */
public class ParseTreeVisitor<T> {
public T visit(ParserRuleContext<?> ctx) {
return ctx.accept(this);
}
/** Visit all rule, nonleaf children. Not useful if you are using T as
* non-Void. This returns nothing, losing all computations from below.
* But handy if you are just walking the tree with a visitor and only
* care about some nodes. The ParserRuleContext.accept() method
* walks all children by default; i.e., calls this method.
*/
public <Symbol> void visitChildren(ParserRuleContext<Symbol> ctx) {
for (ParseTree c : ctx.children) {
if ( c instanceof ParseTree.RuleNode) {
ParseTree.RuleNode r = (ParseTree.RuleNode)c;
ParserRuleContext<Symbol> rctx = (ParserRuleContext<Symbol>)r.getRuleContext();
visit(rctx);
}
}
}
}

View File

@ -0,0 +1,10 @@
import org.antlr.v4.runtime.tree.*;
import org.antlr.v4.runtime.Token;
public interface AVisitor<T> {
T visit(AParser.MultContext ctx);
T visit(AParser.ParensContext ctx);
T visit(AParser.sContext ctx);
T visit(AParser.AddContext ctx);
T visit(AParser.IntContext ctx);
}

View File

@ -3,7 +3,7 @@ import org.antlr.v4.runtime.*;
public class TestE { public class TestE {
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
CharStream input = new ANTLRFileStream(args[0]); CharStream input = new ANTLRFileStream(args[0]);
E lex = new E(input); ELexer lex = new ELexer(input);
CommonTokenStream tokens = new CommonTokenStream(lex); CommonTokenStream tokens = new CommonTokenStream(lex);
tokens.fill(); tokens.fill();
for (Object t : tokens.getTokens()) System.out.println(t); for (Object t : tokens.getTokens()) System.out.println(t);

View File

@ -0,0 +1,76 @@
/*
[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.
*/
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.ParseTreeVisitor;
public class TestVisitor {
public static class MyVisitor extends ABaseVisitor<Integer> implements AVisitor<Integer> {
@Override
public Integer visit(AParser.AddContext ctx) {
return ctx.e(0).accept(this) + ctx.e(1).accept(this);
}
@Override
public Integer visit(AParser.IntContext ctx) {
return Integer.valueOf(ctx.INT().getText());
}
@Override
public Integer visit(AParser.MultContext ctx) {
// return ctx.e(0).accept(this) * ctx.e(1).accept(this);
return visit(ctx.e(0)) * visit(ctx.e(1));
}
@Override
public Integer visit(AParser.ParensContext ctx) {
return ctx.e().accept(this);
}
@Override
public Integer visit(AParser.sContext ctx) {
return visit(ctx.e());
//return ctx.e().accept(this);
}
}
public static void main(String[] args) throws Exception {
ALexer lexer = new ALexer(new ANTLRFileStream(args[0]));
CommonTokenStream tokens = new CommonTokenStream(lexer);
AParser p = new AParser(tokens);
p.setBuildParseTree(true);
ParserRuleContext<Token> t = p.s();
System.out.println("tree = "+t.toStringTree(p));
MyVisitor visitor = new MyVisitor();
Integer result = visitor.visit(t);
// Integer result = t.accept(visitor);
System.out.println("result from tree walk = " + result);
}
}

View File

@ -56,6 +56,28 @@ public class <file.grammarName>BaseListener implements <file.grammarName>Listene
} }
>> >>
VisitorFile(file, header) ::= <<
<header>
import org.antlr.v4.runtime.tree.*;
import org.antlr.v4.runtime.Token;
public interface <file.grammarName>Visitor\<T> {
<file.visitorNames:{lname |
T visit(<file.parserName>.<lname>Context ctx);}; separator="\n">
}
>>
BaseVisitorFile(file, header) ::= <<
<header>
import org.antlr.v4.runtime.tree.*;
import org.antlr.v4.runtime.Token;
public class <file.grammarName>BaseVisitor\<T> extends ParseTreeVisitor\<T> implements <file.grammarName>Visitor\<T> {
<file.visitorNames:{lname |
public T visit(<file.parserName>.<lname>Context ctx) { visitChildren(ctx); return null; \}}; separator="\n">
}
>>
Parser(parser, funcs, atn, sempredFuncs, superclass) ::= << Parser(parser, funcs, atn, sempredFuncs, superclass) ::= <<
<Parser_(ctor="parser_ctor", ...)> <Parser_(ctor="parser_ctor", ...)>
>> >>
@ -533,7 +555,7 @@ ListLabelName(label) ::= "<label>"
CaptureNextToken(d) ::= "<d.varName> = _input.LT(1);" CaptureNextToken(d) ::= "<d.varName> = _input.LT(1);"
CaptureNextTokenType(d) ::= "<d.varName> = _input.LA(1);" CaptureNextTokenType(d) ::= "<d.varName> = _input.LA(1);"
StructDecl(s,attrs,getters,visitorDispatchMethods,interfaces,extensionMembers, StructDecl(s,attrs,getters,dispatchMethods,interfaces,extensionMembers,
superClass={ParserRuleContext\<<InputSymbolType()>>}) ::= << superClass={ParserRuleContext\<<InputSymbolType()>>}) ::= <<
public static class <s.name> extends <superClass><if(interfaces)> implements <interfaces; separator=", "><endif> { public static class <s.name> extends <superClass><if(interfaces)> implements <interfaces; separator=", "><endif> {
<attrs:{a | <a>}; separator="\n"> <attrs:{a | <a>}; separator="\n">
@ -550,24 +572,32 @@ public static class <s.name> extends <superClass><if(interfaces)> implements <in
<s.attrs:{a | this.<a.name> = ctx.<a.name>;}; separator="\n"> <s.attrs:{a | this.<a.name> = ctx.<a.name>;}; separator="\n">
} }
<endif> <endif>
<visitorDispatchMethods; separator="\n"> <dispatchMethods; separator="\n">
<extensionMembers; separator="\n"> <extensionMembers; separator="\n">
} }
>> >>
AltLabelStructDecl(s,attrs,getters,visitorDispatchMethods) ::= << AltLabelStructDecl(s,attrs,getters,dispatchMethods) ::= <<
public static class <s.name> extends <currentRule.name>Context { public static class <s.name> extends <currentRule.name>Context {
<attrs:{a | <a>}; separator="\n"> <attrs:{a | <a>}; separator="\n">
<getters:{g | <g>}; separator="\n"> <getters:{g | <g>}; separator="\n">
public <s.name>(<currentRule.name>Context ctx) { copyFrom(ctx); } public <s.name>(<currentRule.name>Context ctx) { copyFrom(ctx); }
<visitorDispatchMethods; separator="\n"> <dispatchMethods; separator="\n">
}
>>
ListenerDispatchMethod(method) ::= <<
@Override
public void <if(method.isEnter)>enter<else>exit<endif>Rule(ParseTreeListener\<<InputSymbolType()>\> listener) {
if ( listener instanceof <parser.grammarName>Listener ) ((<parser.grammarName>Listener)listener).<if(method.isEnter)>enter<else>exit<endif>(this);
} }
>> >>
VisitorDispatchMethod(method) ::= << VisitorDispatchMethod(method) ::= <<
@Override @Override
public void <if(method.isEnter)>enter<else>exit<endif>Rule(ParseTreeListener\<<InputSymbolType()>\> listener) { public \<T> T accept(ParseTreeVisitor\<? extends T> visitor) {
if ( listener instanceof <parser.grammarName>Listener ) ((<parser.grammarName>Listener)listener).<if(method.isEnter)>enter<else>exit<endif>(this); if ( visitor instanceof <parser.grammarName>Visitor ) return ((<parser.grammarName>Visitor\<T>)visitor).visit(this);
else return null;
} }
>> >>

View File

@ -186,8 +186,10 @@ public class Tool {
grammarFiles.add(arg); grammarFiles.add(arg);
continue; continue;
} }
boolean found = false;
for (Option o : optionDefs) { for (Option o : optionDefs) {
if ( arg.equals(o.name) ) { if ( arg.equals(o.name) ) {
found = true;
String argValue = null; String argValue = null;
if ( o.argType==OptionArgType.STRING ) { if ( o.argType==OptionArgType.STRING ) {
argValue = args[i]; argValue = args[i];
@ -198,7 +200,7 @@ public class Tool {
try { try {
Field f = c.getField(o.fieldName); Field f = c.getField(o.fieldName);
if ( argValue==null ) { if ( argValue==null ) {
if ( o.fieldName.startsWith("-no-") ) f.setBoolean(this, false); if ( arg.startsWith("-no-") ) f.setBoolean(this, false);
else f.setBoolean(this, true); else f.setBoolean(this, true);
} }
else f.set(this, argValue); else f.set(this, argValue);
@ -208,6 +210,9 @@ public class Tool {
} }
} }
} }
if ( !found ) {
errMgr.toolError(ErrorType.INVALID_CMDLINE_ARG, arg);
}
} }
if ( outputDirectory!=null ) { if ( outputDirectory!=null ) {
if (outputDirectory.endsWith("/") || if (outputDirectory.endsWith("/") ||

View File

@ -46,9 +46,13 @@ public class CodeGenPipeline {
} }
else { else {
gen.writeRecognizer(gen.generateParser()); gen.writeRecognizer(gen.generateParser());
if ( g.tool.gen_listener) { if ( g.tool.gen_listener ) {
gen.writeListener(gen.generateListener()); gen.writeListener(gen.generateListener());
gen.writeBlankListener(gen.generateBlankListener()); gen.writeBaseListener(gen.generateBaseListener());
}
if ( g.tool.gen_visitor ) {
gen.writeVisitor(gen.generateVisitor());
gen.writeBaseVisitor(gen.generateBaseVisitor());
} }
gen.writeHeaderFile(); gen.writeHeaderFile();
} }

View File

@ -33,16 +33,12 @@ import org.antlr.v4.Tool;
import org.antlr.v4.codegen.model.OutputModelObject; import org.antlr.v4.codegen.model.OutputModelObject;
import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.misc.NotNull; import org.antlr.v4.runtime.misc.NotNull;
import org.antlr.v4.tool.ErrorType; import org.antlr.v4.tool.*;
import org.antlr.v4.tool.Grammar;
import org.stringtemplate.v4.*; import org.stringtemplate.v4.*;
import java.io.IOException; import java.io.*;
import java.io.Writer; import java.lang.reflect.*;
import java.lang.reflect.Constructor; import java.util.*;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
/** General controller for code gen. Can instantiate sub generator(s). /** General controller for code gen. Can instantiate sub generator(s).
*/ */
@ -173,16 +169,42 @@ public class CodeGenerator {
return st; return st;
} }
public ST generateBlankListener() { public ST generateVisitor() {
OutputModelFactory factory = new ParserFactory(this); OutputModelFactory factory = new ParserFactory(this);
OutputModelController controller = new OutputModelController(factory); OutputModelController controller = new OutputModelController(factory);
factory.setController(controller); factory.setController(controller);
OutputModelObject blankModel = controller.buildBlankListenerOutputModel(); OutputModelObject visitorModel = controller.buildVisitorOutputModel();
OutputModelWalker walker = new OutputModelWalker(tool, templates); OutputModelWalker walker = new OutputModelWalker(tool, templates);
ST st = walker.walk(blankModel); ST st = walker.walk(visitorModel);
return st;
}
public ST generateBaseListener() {
OutputModelFactory factory = new ParserFactory(this);
OutputModelController controller = new OutputModelController(factory);
factory.setController(controller);
OutputModelObject baseModel = controller.buildBaseListenerOutputModel();
OutputModelWalker walker = new OutputModelWalker(tool, templates);
ST st = walker.walk(baseModel);
return st;
}
public ST generateBaseVisitor() {
OutputModelFactory factory = new ParserFactory(this);
OutputModelController controller = new OutputModelController(factory);
factory.setController(controller);
OutputModelObject baseModel = controller.buildBaseVisitorOutputModel();
OutputModelWalker walker = new OutputModelWalker(tool, templates);
ST st = walker.walk(baseModel);
return st; return st;
} }
@ -226,8 +248,16 @@ public class CodeGenerator {
target.genFile(g,outputFileST, getListenerFileName()); target.genFile(g,outputFileST, getListenerFileName());
} }
public void writeBlankListener(ST outputFileST) { public void writeBaseListener(ST outputFileST) {
target.genFile(g,outputFileST, getBlankListenerFileName()); target.genFile(g,outputFileST, getBaseListenerFileName());
}
public void writeVisitor(ST outputFileST) {
target.genFile(g,outputFileST, getVisitorFileName());
}
public void writeBaseVisitor(ST outputFileST) {
target.genFile(g,outputFileST, getBaseVisitorFileName());
} }
public void writeHeaderFile() { public void writeHeaderFile() {
@ -309,16 +339,36 @@ public class CodeGenerator {
return listenerName+extST.render(); return listenerName+extST.render();
} }
/** A given grammar T, return a blank listener implementation /** A given grammar T, return the visitor name such as
* such as BlankTListener.java, if we're using the Java target. * TVisitor.java, if we're using the Java target.
*/ */
public String getBlankListenerFileName() { public String getVisitorFileName() {
assert g.name != null;
ST extST = templates.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; assert g.name != null;
ST extST = templates.getInstanceOf("codeFileExtension"); ST extST = templates.getInstanceOf("codeFileExtension");
String listenerName = g.name + "BaseListener"; String listenerName = g.name + "BaseListener";
return listenerName+extST.render(); 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 = templates.getInstanceOf("codeFileExtension");
String listenerName = g.name + "BaseVisitor";
return listenerName+extST.render();
}
/** What is the name of the vocab file generated for this grammar? /** What is the name of the vocab file generated for this grammar?
* Returns null if no .tokens file should be generated. * Returns null if no .tokens file should be generated.
*/ */

View File

@ -33,22 +33,12 @@ import org.antlr.runtime.tree.CommonTreeNodeStream;
import org.antlr.v4.analysis.LeftRecursiveRuleAltInfo; import org.antlr.v4.analysis.LeftRecursiveRuleAltInfo;
import org.antlr.v4.codegen.model.*; import org.antlr.v4.codegen.model.*;
import org.antlr.v4.codegen.model.decl.CodeBlock; import org.antlr.v4.codegen.model.decl.CodeBlock;
import org.antlr.v4.parse.ANTLRParser; import org.antlr.v4.parse.*;
import org.antlr.v4.parse.GrammarASTAdaptor; import org.antlr.v4.tool.*;
import org.antlr.v4.tool.Alternative; import org.antlr.v4.tool.ast.*;
import org.antlr.v4.tool.Grammar; import org.stringtemplate.v4.*;
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.*;
import java.util.List;
import java.util.Stack;
/** This receives events from SourceGenTriggers.g and asks factory to do work. /** This receives events from SourceGenTriggers.g and asks factory to do work.
* Then runs extensions in order on resulting SrcOps to get final list. * Then runs extensions in order on resulting SrcOps to get final list.
@ -118,9 +108,19 @@ public class OutputModelController {
return new ListenerFile(delegate, gen.getListenerFileName()); return new ListenerFile(delegate, gen.getListenerFileName());
} }
public OutputModelObject buildBlankListenerOutputModel() { public OutputModelObject buildBaseListenerOutputModel() {
CodeGenerator gen = delegate.getGenerator(); CodeGenerator gen = delegate.getGenerator();
return new BaseListenerFile(delegate, gen.getBlankListenerFileName()); 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) { public ParserFile parserFile(String fileName) {

View File

@ -0,0 +1,9 @@
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);
}
}

View File

@ -0,0 +1,9 @@
package org.antlr.v4.codegen.model;
import org.antlr.v4.codegen.OutputModelFactory;
public class DispatchMethod extends OutputModelObject {
public DispatchMethod(OutputModelFactory factory) {
super(factory);
}
}

View File

@ -0,0 +1,12 @@
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;
}
}

View File

@ -14,7 +14,6 @@ public class ListenerFile extends OutputFile {
public String grammarName; public String grammarName;
public String parserName; public String parserName;
public Set<String> listenerNames = new HashSet<String>(); public Set<String> listenerNames = new HashSet<String>();
// public List<String> ruleNames = new ArrayList<String>();
@ModelElement public Action header; @ModelElement public Action header;

View File

@ -1,20 +1,9 @@
package org.antlr.v4.codegen.model; package org.antlr.v4.codegen.model;
import org.antlr.v4.codegen.OutputModelFactory; import org.antlr.v4.codegen.OutputModelFactory;
import org.antlr.v4.misc.Triple;
import org.antlr.v4.tool.Rule;
import org.antlr.v4.tool.ast.AltAST;
import java.util.List; public class VisitorDispatchMethod extends DispatchMethod {
public VisitorDispatchMethod(OutputModelFactory factory) {
public class VisitorDispatchMethod extends OutputModelObject {
public String listenerName = "Rule";
public boolean isEnter;
public VisitorDispatchMethod(OutputModelFactory factory, Rule r, boolean isEnter) {
super(factory); super(factory);
this.isEnter = isEnter;
List<Triple<Integer,AltAST,String>> label = r.getAltLabels();
if ( label!=null ) listenerName = label.get(0).c;
} }
} }

View File

@ -0,0 +1,37 @@
package org.antlr.v4.codegen.model;
import org.antlr.v4.codegen.OutputModelFactory;
import org.antlr.v4.misc.Triple;
import org.antlr.v4.tool.*;
import org.antlr.v4.tool.ast.*;
import java.util.*;
public class VisitorFile extends OutputFile {
public String grammarName;
public String parserName;
public Set<String> visitorNames = new HashSet<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()) {
List<Triple<Integer,AltAST,String>> labels = r.getAltLabels();
if ( labels!=null ) {
for (Triple<Integer,AltAST,String> pair : labels) {
visitorNames.add(pair.c);
}
}
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);
}
}

View File

@ -30,6 +30,7 @@
package org.antlr.v4.codegen.model.decl; package org.antlr.v4.codegen.model.decl;
import org.antlr.v4.codegen.OutputModelFactory; import org.antlr.v4.codegen.OutputModelFactory;
import org.antlr.v4.codegen.model.VisitorDispatchMethod;
import org.antlr.v4.tool.Rule; import org.antlr.v4.tool.Rule;
/** A StructDecl to handle a -> label on alt */ /** A StructDecl to handle a -> label on alt */
@ -46,6 +47,14 @@ public class AltLabelStructDecl extends StructDecl {
.getInstanceOf("RuleContextNameSuffix").render(); .getInstanceOf("RuleContextNameSuffix").render();
} }
@Override
public void addDispatchMethods(Rule r) {
super.addDispatchMethods(r);
if ( factory.getGrammar().tool.gen_visitor ) {
dispatchMethods.add(new VisitorDispatchMethod(factory));
}
}
@Override @Override
public int hashCode() { public int hashCode() {
return name.hashCode(); return name.hashCode();

View File

@ -30,40 +30,37 @@
package org.antlr.v4.codegen.model.decl; package org.antlr.v4.codegen.model.decl;
import org.antlr.v4.codegen.OutputModelFactory; import org.antlr.v4.codegen.OutputModelFactory;
import org.antlr.v4.codegen.model.ModelElement; import org.antlr.v4.codegen.model.*;
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.runtime.misc.OrderedHashSet;
import org.antlr.v4.tool.Attribute; import org.antlr.v4.tool.*;
import org.antlr.v4.tool.Rule;
import java.util.ArrayList; import java.util.*;
import java.util.Collection;
import java.util.List;
/** This object models the structure holding all of the parameters, /** This object models the structure holding all of the parameters,
* return values, local variables, and labels associated with a rule. * return values, local variables, and labels associated with a rule.
*/ */
public class StructDecl extends Decl { public class StructDecl extends Decl {
public String superClass;
public boolean provideCopyFrom; public boolean provideCopyFrom;
@ModelElement public OrderedHashSet<Decl> attrs = new OrderedHashSet<Decl>(); @ModelElement public OrderedHashSet<Decl> attrs = new OrderedHashSet<Decl>();
@ModelElement public OrderedHashSet<Decl> getters = new OrderedHashSet<Decl>(); @ModelElement public OrderedHashSet<Decl> getters = new OrderedHashSet<Decl>();
@ModelElement public Collection<Attribute> ctorAttrs; @ModelElement public Collection<Attribute> ctorAttrs;
@ModelElement public List<VisitorDispatchMethod> visitorDispatchMethods; @ModelElement public List<? super DispatchMethod> dispatchMethods;
@ModelElement public List<OutputModelObject> interfaces; @ModelElement public List<OutputModelObject> interfaces;
@ModelElement public List<OutputModelObject> extensionMembers; @ModelElement public List<OutputModelObject> extensionMembers;
public StructDecl(OutputModelFactory factory, Rule r) { public StructDecl(OutputModelFactory factory, Rule r) {
super(factory, factory.getGenerator().target.getRuleFunctionContextStructName(r)); super(factory, factory.getGenerator().target.getRuleFunctionContextStructName(r));
addVisitorDispatchMethods(r); addDispatchMethods(r);
provideCopyFrom = r.hasAltSpecificContexts(); provideCopyFrom = r.hasAltSpecificContexts();
} }
public void addVisitorDispatchMethods(Rule r) { public void addDispatchMethods(Rule r) {
visitorDispatchMethods = new ArrayList<VisitorDispatchMethod>(); dispatchMethods = new ArrayList<DispatchMethod>();
visitorDispatchMethods.add(new VisitorDispatchMethod(factory, r, true)); dispatchMethods.add(new ListenerDispatchMethod(factory, true));
visitorDispatchMethods.add(new VisitorDispatchMethod(factory, r, false)); dispatchMethods.add(new ListenerDispatchMethod(factory, false));
if ( factory.getGrammar().tool.gen_visitor && !r.hasAltSpecificContexts() ) {
dispatchMethods.add(new VisitorDispatchMethod(factory));
}
} }
public void addDecl(Decl d) { public void addDecl(Decl d) {

View File

@ -40,10 +40,7 @@ import org.antlr.v4.tool.ast.*;
import org.stringtemplate.v4.misc.MultiMap; import org.stringtemplate.v4.misc.MultiMap;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.*;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/** No side-effects except for setting options into the appropriate node. /** No side-effects except for setting options into the appropriate node.
* TODO: make the side effects into a separate pass this * TODO: make the side effects into a separate pass this
@ -242,6 +239,41 @@ public class BasicSemanticChecks extends GrammarTreeVisitor {
// } // }
} }
Map<String,String> altLabelToRuleName = new HashMap<String, String>();
@Override
public void finishRule(RuleAST rule, GrammarAST ID, GrammarAST block) {
MultiMap<String,GrammarAST> ruleToAltLabels = new MultiMap<String, GrammarAST>();
if ( rule.isLexerRule() ) return;
BlockAST blk = (BlockAST)rule.getFirstChildWithType(BLOCK);
int nalts = blk.getChildCount();
for (int i=0; i< nalts; i++) {
AltAST altAST = (AltAST)blk.getChild(i);
if ( altAST.altLabel!=null ) {
ruleToAltLabels.map(rule.getRuleName(), altAST.altLabel);
String altLabel = altAST.altLabel.getText();
String prevRuleForLabel = altLabelToRuleName.get(altLabel);
if ( prevRuleForLabel!=null ) {
g.tool.errMgr.grammarError(ErrorType.ALT_LABEL_REDEF,
g.fileName, rule.getToken(),
altLabel,
rule.getRuleName(),
prevRuleForLabel);
}
else {
altLabelToRuleName.put(altLabel, rule.getRuleName());
}
}
}
List<GrammarAST> altLabels = 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, rule.getToken(), rule.getRuleName());
}
}
// Routines to do the actual work of checking issues with a grammar. // Routines to do the actual work of checking issues with a grammar.
// They are triggered by the visitor methods above. // They are triggered by the visitor methods above.

View File

@ -30,11 +30,16 @@
package org.antlr.v4.tool; package org.antlr.v4.tool;
import org.antlr.v4.Tool; import org.antlr.v4.Tool;
import org.stringtemplate.v4.*; import org.stringtemplate.v4.ST;
import org.stringtemplate.v4.misc.*; import org.stringtemplate.v4.STErrorListener;
import org.stringtemplate.v4.STGroup;
import org.stringtemplate.v4.STGroupFile;
import org.stringtemplate.v4.misc.ErrorBuffer;
import org.stringtemplate.v4.misc.STMessage;
import java.net.URL; import java.net.URL;
import java.util.*; import java.util.Collection;
import java.util.Locale;
public class ErrorManager { public class ErrorManager {
public static final String FORMATS_DIR = "org/antlr/v4/tool/templates/messages/formats/"; public static final String FORMATS_DIR = "org/antlr/v4/tool/templates/messages/formats/";
@ -190,7 +195,6 @@ public class ErrorManager {
public void toolError(ErrorType errorType, Object... args) { public void toolError(ErrorType errorType, Object... args) {
ToolMessage msg = new ToolMessage(errorType, args); ToolMessage msg = new ToolMessage(errorType, args);
emit(errorType, msg); emit(errorType, msg);
tool.error(msg);
} }
public void toolError(ErrorType errorType, Throwable e, Object... args) { public void toolError(ErrorType errorType, Throwable e, Object... args) {

View File

@ -43,8 +43,9 @@ package org.antlr.v4.tool;
public enum ErrorType { public enum ErrorType {
INVALID(0, "<INVALID>", ErrorSeverity.ERROR), INVALID(0, "<INVALID>", ErrorSeverity.ERROR),
// Tool errors
CANNOT_WRITE_FILE(1, "cannot write file <arg>: <arg2>", ErrorSeverity.ERROR), CANNOT_WRITE_FILE(1, "cannot write file <arg>: <arg2>", ErrorSeverity.ERROR),
// CANNOT_CLOSE_FILE(2, "cannot close file <arg>", ErrorSeverity.ERROR), INVALID_CMDLINE_ARG(2, "unknown command-line option <arg>", ErrorSeverity.ERROR),
CANNOT_FIND_TOKENS_FILE(3, "cannot find tokens file <arg>", ErrorSeverity.ERROR), CANNOT_FIND_TOKENS_FILE(3, "cannot find tokens file <arg>", ErrorSeverity.ERROR),
ERROR_READING_TOKENS_FILE(4, "cannot find tokens file <arg>: <arg2>", ErrorSeverity.ERROR), ERROR_READING_TOKENS_FILE(4, "cannot find tokens file <arg>: <arg2>", ErrorSeverity.ERROR),
DIR_NOT_FOUND(5, "directory not found: <arg>", ErrorSeverity.ERROR), DIR_NOT_FOUND(5, "directory not found: <arg>", ErrorSeverity.ERROR),
@ -139,7 +140,9 @@ public enum ErrorType {
ALL_OPS_NEED_SAME_ASSOC(118, "all operators of alt <arg> of left-recursive rule must have same associativity", ErrorSeverity.WARNING), ALL_OPS_NEED_SAME_ASSOC(118, "all operators of alt <arg> of left-recursive rule must have same associativity", ErrorSeverity.WARNING),
LEFT_RECURSION_CYCLES(119, "The following sets of rules are mutually left-recursive <arg:{c| [<c:{r|<r.name>}; separator=\", \">]}; separator=\" and \">", ErrorSeverity.ERROR), LEFT_RECURSION_CYCLES(119, "The following sets of rules are mutually left-recursive <arg:{c| [<c:{r|<r.name>}; separator=\", \">]}; separator=\" and \">", ErrorSeverity.ERROR),
MODE_NOT_IN_LEXER(120, "lexical modes are only allowed in lexer grammars", ErrorSeverity.ERROR), MODE_NOT_IN_LEXER(120, "lexical modes are only allowed in lexer grammars", ErrorSeverity.ERROR),
CANNOT_FIND_ATTRIBUTE_NAME_IN_DECL(121, "cannot find an attribute name in attribute declaration", ErrorSeverity.ERROR), CANNOT_FIND_ATTRIBUTE_NAME_IN_DECL(121, "cannot find an attribute name in attribute declaration", ErrorSeverity.ERROR),
RULE_WITH_TOO_FEW_ALT_LABELS(122, "rule <arg>: must label all alternatives or none", ErrorSeverity.ERROR),
ALT_LABEL_REDEF(123, "rule alt label <arg> redefined in rule <arg2>, originally in <arg3>", ErrorSeverity.ERROR),
/** Documentation comment is unterminated */ /** Documentation comment is unterminated */
//UNTERMINATED_DOC_COMMENT(, "", ErrorSeverity.ERROR), //UNTERMINATED_DOC_COMMENT(, "", ErrorSeverity.ERROR),

View File

@ -34,8 +34,6 @@ import org.antlr.runtime.tree.Tree;
import org.antlr.v4.parse.ANTLRParser; import org.antlr.v4.parse.ANTLRParser;
public class RuleAST extends GrammarASTWithOptions { public class RuleAST extends GrammarASTWithOptions {
public RuleAST(GrammarAST node) { public RuleAST(GrammarAST node) {
super(node); super(node);
} }
@ -43,6 +41,17 @@ public class RuleAST extends GrammarASTWithOptions {
public RuleAST(Token t) { super(t); } public RuleAST(Token t) { super(t); }
public RuleAST(int type) { super(type); } public RuleAST(int type) { super(type); }
public boolean isLexerRule() {
String name = getRuleName();
return name!=null && Character.isUpperCase(name.charAt(0));
}
public String getRuleName() {
GrammarAST nameNode = (GrammarAST)getChild(0);
if ( nameNode!=null ) return nameNode.getText();
return null;
}
@Override @Override
public Tree dupNode() { return new RuleAST(this); } public Tree dupNode() { return new RuleAST(this); }