Added visitor gen mechanism, runtime support
This commit is contained in:
parent
66e7e0fad2
commit
725b105135
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
>>
|
>>
|
||||||
|
|
||||||
|
|
|
@ -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("/") ||
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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),
|
||||||
|
|
||||||
|
|
|
@ -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); }
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue