forked from jasder/antlr
pull apart treeviewer; rename ANTLRParserListener
[git-p4: depot-paths = "//depot/code/antlr4/main/": change = 9148]
This commit is contained in:
parent
0dbebf879a
commit
6c104b7724
|
@ -32,7 +32,7 @@ package org.antlr.v4.runtime;
|
||||||
import com.sun.istack.internal.Nullable;
|
import com.sun.istack.internal.Nullable;
|
||||||
|
|
||||||
/** How to emit recognition errors */
|
/** How to emit recognition errors */
|
||||||
public interface ANTLRParserListener {
|
public interface ANTLRErrorListener {
|
||||||
/** Upon syntax error, notify any interested parties. This is not how to
|
/** Upon syntax error, notify any interested parties. This is not how to
|
||||||
* recover from errors or compute error messages. The parser
|
* recover from errors or compute error messages. The parser
|
||||||
* ANTLRErrorStrategy specifies how to recover from syntax errors
|
* ANTLRErrorStrategy specifies how to recover from syntax errors
|
|
@ -29,14 +29,10 @@
|
||||||
package org.antlr.v4.runtime;
|
package org.antlr.v4.runtime;
|
||||||
|
|
||||||
import com.sun.istack.internal.Nullable;
|
import com.sun.istack.internal.Nullable;
|
||||||
import org.antlr.v4.runtime.atn.ATNConfig;
|
import org.antlr.v4.runtime.atn.*;
|
||||||
import org.antlr.v4.runtime.atn.ParserATNSimulator;
|
import org.antlr.v4.runtime.misc.*;
|
||||||
import org.antlr.v4.runtime.misc.IntervalSet;
|
|
||||||
import org.antlr.v4.runtime.misc.OrderedHashSet;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/** A generic recognizer that can handle recognizers generated from
|
/** A generic recognizer that can handle recognizers generated from
|
||||||
* parser and tree grammars. This is all the parsing
|
* parser and tree grammars. This is all the parsing
|
||||||
|
@ -163,17 +159,22 @@ public abstract class BaseRecognizer extends Recognizer<ParserATNSimulator> {
|
||||||
int line = offendingToken.getLine();
|
int line = offendingToken.getLine();
|
||||||
int charPositionInLine = offendingToken.getCharPositionInLine();
|
int charPositionInLine = offendingToken.getCharPositionInLine();
|
||||||
if ( _listeners==null || _listeners.size()==0 ) {
|
if ( _listeners==null || _listeners.size()==0 ) {
|
||||||
emitErrorMessage("line "+line+":"+charPositionInLine+" "+msg);
|
System.err.println("line "+line+":"+charPositionInLine+" "+msg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (ANTLRParserListener pl : _listeners) {
|
for (ANTLRErrorListener pl : _listeners) {
|
||||||
pl.error(this, offendingToken, line, charPositionInLine, msg, e);
|
pl.error(this, offendingToken, line, charPositionInLine, msg, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void enterOuterAlt(ParserRuleContext localctx, int altNum) {
|
public void enterOuterAlt(ParserRuleContext localctx, int altNum) {
|
||||||
// if we have new localctx, make sure we add to parse tree
|
// if we have new localctx, make sure we replace existing ctx
|
||||||
if ( buildParseTrees && _ctx != localctx ) addContextToParseTree();
|
// that is previous child of parse tree
|
||||||
|
if ( buildParseTrees && _ctx != localctx ) {
|
||||||
|
RuleContext parent = _ctx.parent;
|
||||||
|
parent.removeLastChild();
|
||||||
|
if ( parent!=null ) parent.addChild(localctx);
|
||||||
|
}
|
||||||
_ctx = localctx;
|
_ctx = localctx;
|
||||||
_ctx.altNum = altNum;
|
_ctx.altNum = altNum;
|
||||||
}
|
}
|
||||||
|
@ -195,7 +196,7 @@ public abstract class BaseRecognizer extends Recognizer<ParserATNSimulator> {
|
||||||
if ( buildParseTrees ) {
|
if ( buildParseTrees ) {
|
||||||
// TODO: tree parsers?
|
// TODO: tree parsers?
|
||||||
if ( _errHandler.inErrorRecoveryMode(this) ) {
|
if ( _errHandler.inErrorRecoveryMode(this) ) {
|
||||||
System.out.println("consume in error recovery mode for "+o);
|
// System.out.println("consume in error recovery mode for "+o);
|
||||||
_ctx.addErrorNode((Token) o);
|
_ctx.addErrorNode((Token) o);
|
||||||
}
|
}
|
||||||
else _ctx.addChild((Token)o);
|
else _ctx.addChild((Token)o);
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
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.atn.RuleTransition;
|
|
||||||
import org.antlr.v4.runtime.misc.IntervalSet;
|
import org.antlr.v4.runtime.misc.IntervalSet;
|
||||||
|
|
||||||
/** This is the default error handling mechanism for ANTLR parsers
|
/** This is the default error handling mechanism for ANTLR parsers
|
||||||
|
@ -50,8 +48,8 @@ public class DefaultANTLRErrorStrategy implements ANTLRErrorStrategy {
|
||||||
// if we've already reported an error and have not matched a token
|
// if we've already reported an error and have not matched a token
|
||||||
// yet successfully, don't report any errors.
|
// yet successfully, don't report any errors.
|
||||||
if (errorRecoveryMode) {
|
if (errorRecoveryMode) {
|
||||||
System.err.print("[SPURIOUS] ");
|
// System.err.print("[SPURIOUS] ");
|
||||||
//return; // don't count spurious errors
|
return; // don't count spurious errors
|
||||||
}
|
}
|
||||||
recognizer.syntaxErrors++;
|
recognizer.syntaxErrors++;
|
||||||
beginErrorCondition(recognizer);
|
beginErrorCondition(recognizer);
|
||||||
|
@ -90,6 +88,7 @@ public class DefaultANTLRErrorStrategy implements ANTLRErrorStrategy {
|
||||||
// at least to prevent an infinite loop; this is a failsafe.
|
// at least to prevent an infinite loop; this is a failsafe.
|
||||||
// System.err.println("seen error condition before index="+
|
// System.err.println("seen error condition before index="+
|
||||||
// lastErrorIndex+", states="+lastErrorStates);
|
// lastErrorIndex+", states="+lastErrorStates);
|
||||||
|
// System.err.println("FAILSAFE consumes "+recognizer.getTokenNames()[recognizer.getInputStream().LA(1)]);
|
||||||
recognizer.consume();
|
recognizer.consume();
|
||||||
}
|
}
|
||||||
lastErrorIndex = recognizer.getInputStream().index();
|
lastErrorIndex = recognizer.getInputStream().index();
|
||||||
|
@ -426,7 +425,7 @@ public class DefaultANTLRErrorStrategy implements ANTLRErrorStrategy {
|
||||||
|
|
||||||
/** Consume tokens until one matches the given token set */
|
/** Consume tokens until one matches the given token set */
|
||||||
public void consumeUntil(BaseRecognizer recognizer, IntervalSet set) {
|
public void consumeUntil(BaseRecognizer recognizer, IntervalSet set) {
|
||||||
System.out.println("consumeUntil("+set.toString(recognizer.getTokenNames())+")");
|
// System.out.println("consumeUntil("+set.toString(recognizer.getTokenNames())+")");
|
||||||
int ttype = recognizer.getInputStream().LA(1);
|
int ttype = recognizer.getInputStream().LA(1);
|
||||||
while (ttype != Token.EOF && !set.contains(ttype) ) {
|
while (ttype != Token.EOF && !set.contains(ttype) ) {
|
||||||
//System.out.println("consume during recover LA(1)="+getTokenNames()[input.LA(1)]);
|
//System.out.println("consume during recover LA(1)="+getTokenNames()[input.LA(1)]);
|
||||||
|
|
|
@ -37,7 +37,7 @@ public class Recognizer<ATNInterpreter> {
|
||||||
public static final int EOF=-1;
|
public static final int EOF=-1;
|
||||||
|
|
||||||
protected ANTLRErrorStrategy _errHandler = new DefaultANTLRErrorStrategy();
|
protected ANTLRErrorStrategy _errHandler = new DefaultANTLRErrorStrategy();
|
||||||
protected List<ANTLRParserListener> _listeners;
|
protected List<ANTLRErrorListener> _listeners;
|
||||||
|
|
||||||
protected ATNInterpreter _interp;
|
protected ATNInterpreter _interp;
|
||||||
|
|
||||||
|
@ -194,24 +194,19 @@ public class Recognizer<ATNInterpreter> {
|
||||||
return "'"+s+"'";
|
return "'"+s+"'";
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Override this method to change where error messages go */
|
public void addListener(ANTLRErrorListener pl) {
|
||||||
public void emitErrorMessage(String msg) {
|
|
||||||
System.err.println(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addListener(ANTLRParserListener pl) {
|
|
||||||
if ( _listeners ==null ) {
|
if ( _listeners ==null ) {
|
||||||
_listeners =
|
_listeners =
|
||||||
Collections.synchronizedList(new ArrayList<ANTLRParserListener>(2));
|
Collections.synchronizedList(new ArrayList<ANTLRErrorListener>(2));
|
||||||
}
|
}
|
||||||
if ( pl!=null ) _listeners.add(pl);
|
if ( pl!=null ) _listeners.add(pl);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeListener(ANTLRParserListener pl) { _listeners.remove(pl); }
|
public void removeListener(ANTLRErrorListener pl) { _listeners.remove(pl); }
|
||||||
|
|
||||||
public void removeListeners() { _listeners.clear(); }
|
public void removeListeners() { _listeners.clear(); }
|
||||||
|
|
||||||
public List<ANTLRParserListener> getListeners() { return _listeners; }
|
public List<ANTLRErrorListener> getListeners() { return _listeners; }
|
||||||
|
|
||||||
public ANTLRErrorStrategy getErrHandler() { return _errHandler; }
|
public ANTLRErrorStrategy getErrHandler() { return _errHandler; }
|
||||||
|
|
||||||
|
|
|
@ -28,15 +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.Interval;
|
import org.antlr.v4.runtime.misc.Interval;
|
||||||
import org.antlr.v4.runtime.tree.ParseTree;
|
import org.antlr.v4.runtime.tree.*;
|
||||||
import org.antlr.v4.runtime.tree.ParseTreeListener;
|
|
||||||
import org.antlr.v4.runtime.tree.Trees;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/** Rules can return start/stop info as well as possible trees and templates.
|
/** Rules can return start/stop info as well as possible trees and templates.
|
||||||
* Each context knows about invoking context and pointer into ATN so we
|
* Each context knows about invoking context and pointer into ATN so we
|
||||||
|
@ -166,6 +162,16 @@ public class RuleContext implements ParseTree.RuleNode {
|
||||||
children.add(ruleInvocation);
|
children.add(ruleInvocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Used by enterOuterAlt to toss out a RuleContext previously added as
|
||||||
|
* we entered a rule. If we have # label, we will need to remove
|
||||||
|
* generic ruleContext object.
|
||||||
|
*/
|
||||||
|
public void removeLastChild() {
|
||||||
|
if ( children!=null ) {
|
||||||
|
children.remove(children.size()-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void trace(int s) {
|
public void trace(int s) {
|
||||||
if ( states==null ) states = new ArrayList<Integer>();
|
if ( states==null ) states = new ArrayList<Integer>();
|
||||||
states.add(s);
|
states.add(s);
|
||||||
|
|
|
@ -123,7 +123,12 @@ public class ATNState {
|
||||||
transitions.set(i, e);
|
transitions.set(i, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean onlyHasEpsilonTransitions() {
|
protected int epsilonOnlyTransitions = -1;
|
||||||
|
|
||||||
|
//lexer atn sim: getEpTar: 13.2%
|
||||||
|
// ruleCtx.equals 10%
|
||||||
|
public boolean onlyHasEpsilonTransitions() { // 22% time
|
||||||
|
if ( epsilonOnlyTransitions>=0 ) return epsilonOnlyTransitions==1;
|
||||||
if ( transitions.size()==0 ) return false;
|
if ( transitions.size()==0 ) return false;
|
||||||
for (Transition t : transitions) {
|
for (Transition t : transitions) {
|
||||||
if ( !t.isEpsilon() ) return false;
|
if ( !t.isEpsilon() ) return false;
|
||||||
|
|
|
@ -29,17 +29,13 @@
|
||||||
|
|
||||||
package org.antlr.v4.runtime.tree;
|
package org.antlr.v4.runtime.tree;
|
||||||
|
|
||||||
import org.antlr.v4.runtime.BaseRecognizer;
|
import org.antlr.v4.runtime.*;
|
||||||
import org.antlr.v4.runtime.Token;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.TreeSet;
|
|
||||||
|
|
||||||
/** A set of utility routines useful for all kinds of ANTLR trees */
|
/** A set of utility routines useful for all kinds of ANTLR trees */
|
||||||
public class Trees {
|
public class Trees {
|
||||||
/** Print out a whole tree in LISP form. toString is used on the
|
/** Print out a whole tree in LISP form. getNodeText is used on the
|
||||||
* node payloads to get the text for the nodes. Detect
|
* node payloads to get the text for the nodes. Detect
|
||||||
* parse trees and extract data appropriately.
|
* parse trees and extract data appropriately.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,164 @@
|
||||||
|
/*
|
||||||
|
[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.runtime.tree.gui;
|
||||||
|
|
||||||
|
import org.abego.treelayout.TreeForTreeLayout;
|
||||||
|
import org.abego.treelayout.util.*;
|
||||||
|
import org.antlr.v4.runtime.tree.Tree;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class AntlrTreeLayouter {
|
||||||
|
// TODO: provide public interface to the configuration/nodeExtent
|
||||||
|
private double gapBetweenLevels = 50;
|
||||||
|
private double gapBetweenNodes = 10;
|
||||||
|
private double nodeWidth = 60;
|
||||||
|
private double nodeHeight = 20;
|
||||||
|
|
||||||
|
public TreeViewer.AntlrTreeLayout layout(Tree tree) {
|
||||||
|
return new TreeViewer.AntlrTreeLayout(new AntlrTreeForTreeLayout(tree),
|
||||||
|
new FixedNodeExtentProvider<Tree>(nodeWidth, nodeHeight),
|
||||||
|
// new TreeViewer.VariableExtentProvide<Tree>(),
|
||||||
|
new DefaultConfiguration<Tree>(gapBetweenLevels,
|
||||||
|
gapBetweenNodes));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class AntlrTreeForTreeLayout 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 AntlrTreeForTreeLayout(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 (Tree) parentNode
|
||||||
|
.getChild(parentNode.getChildCount() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Tree getFirstChild(Tree parentNode) {
|
||||||
|
return (Tree) 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,257 +1,14 @@
|
||||||
package org.antlr.v4.runtime.tree.gui;
|
package org.antlr.v4.runtime.tree.gui;
|
||||||
|
|
||||||
import java.awt.Color;
|
import org.abego.treelayout.*;
|
||||||
import java.awt.Container;
|
|
||||||
import java.awt.Dimension;
|
|
||||||
import java.awt.FontMetrics;
|
|
||||||
import java.awt.Graphics;
|
|
||||||
import java.awt.geom.Rectangle2D;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.NoSuchElementException;
|
|
||||||
|
|
||||||
import javax.swing.BorderFactory;
|
|
||||||
import javax.swing.JComponent;
|
|
||||||
import javax.swing.JDialog;
|
|
||||||
|
|
||||||
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.abego.treelayout.util.FixedNodeExtentProvider;
|
|
||||||
import org.antlr.v4.runtime.tree.Tree;
|
import org.antlr.v4.runtime.tree.Tree;
|
||||||
|
|
||||||
public class TreeViewer {
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.geom.Rectangle2D;
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
public class TreeViewer extends JComponent {
|
||||||
|
private Font font = new Font(Font.MONOSPACED, Font.PLAIN, 12);
|
||||||
private static class AntlrTreeLayout extends TreeLayout<Tree> {
|
|
||||||
public AntlrTreeLayout(TreeForTreeLayout<Tree> tree,
|
|
||||||
NodeExtentProvider<Tree> nodeExtentProvider,
|
|
||||||
Configuration<Tree> configuration) {
|
|
||||||
super(tree, nodeExtentProvider, configuration);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
|
||||||
|
|
||||||
private static class AntlrTreeLayouter {
|
|
||||||
// TODO: provide public interface to the configuration/nodeExtent
|
|
||||||
private double gapBetweenLevels = 50;
|
|
||||||
private double gapBetweenNodes = 10;
|
|
||||||
private double nodeWidth = 60;
|
|
||||||
private double nodeHeight = 20;
|
|
||||||
|
|
||||||
public AntlrTreeLayout layout(Tree tree) {
|
|
||||||
return new AntlrTreeLayout(new AntlrTreeForTreeLayout(tree),
|
|
||||||
new FixedNodeExtentProvider<Tree>(nodeWidth, nodeHeight),
|
|
||||||
new DefaultConfiguration<Tree>(gapBetweenLevels,
|
|
||||||
gapBetweenNodes));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class AntlrTreeForTreeLayout 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 AntlrTreeForTreeLayout(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 (Tree) parentNode
|
|
||||||
.getChild(parentNode.getChildCount() - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Tree getFirstChild(Tree parentNode) {
|
|
||||||
return (Tree) 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
|
||||||
|
|
||||||
private class AntlrTreePane extends JComponent {
|
|
||||||
private final AntlrTreeLayout treeLayout;
|
|
||||||
|
|
||||||
private TreeForTreeLayout<Tree> getTree() {
|
|
||||||
return treeLayout.getTree();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Iterable<Tree> getChildren(Tree parent) {
|
|
||||||
return getTree().getChildren(parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Rectangle2D.Double getBoundsOfNode(Tree node) {
|
|
||||||
return treeLayout.getNodeBounds().get(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getText(Tree tree) {
|
|
||||||
return treeTextProvider.getText(tree);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specifies the tree to be displayed by passing in a {@link TreeLayout}
|
|
||||||
* for that tree.
|
|
||||||
*
|
|
||||||
* @param treeLayout
|
|
||||||
*/
|
|
||||||
public AntlrTreePane(AntlrTreeLayout treeLayout) {
|
|
||||||
this.treeLayout = treeLayout;
|
|
||||||
|
|
||||||
Dimension size = treeLayout.getBounds().getBounds().getSize();
|
|
||||||
setPreferredSize(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
|
||||||
// painting
|
|
||||||
|
|
||||||
private void paintEdges(Graphics g, Tree parent) {
|
|
||||||
if (!getTree().isLeaf(parent)) {
|
|
||||||
Rectangle2D.Double b1 = getBoundsOfNode(parent);
|
|
||||||
double x1 = b1.getCenterX();
|
|
||||||
double y1 = b1.getCenterY();
|
|
||||||
for (Tree child : getChildren(parent)) {
|
|
||||||
Rectangle2D.Double b2 = getBoundsOfNode(child);
|
|
||||||
g.drawLine((int) x1, (int) y1, (int) b2.getCenterX(),
|
|
||||||
(int) b2.getCenterY());
|
|
||||||
|
|
||||||
paintEdges(g, child);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void paintBox(Graphics g, Tree tree) {
|
|
||||||
// draw the box in the background
|
|
||||||
g.setColor(boxColor);
|
|
||||||
Rectangle2D.Double box = getBoundsOfNode(tree);
|
|
||||||
g.fillRoundRect((int) box.x, (int) box.y, (int) box.width - 1,
|
|
||||||
(int) box.height - 1, arcSize, arcSize);
|
|
||||||
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(getFont());
|
|
||||||
int x = (int) box.x + arcSize / 2;
|
|
||||||
int y = (int) box.y + m.getAscent() + m.getLeading() + 1;
|
|
||||||
for (int i = 0; i < lines.length; i++) {
|
|
||||||
g.drawString(lines[i], x, y);
|
|
||||||
y += m.getHeight();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void paint(Graphics g) {
|
|
||||||
super.paint(g);
|
|
||||||
|
|
||||||
paintEdges(g, getTree().getRoot());
|
|
||||||
|
|
||||||
// paint the boxes
|
|
||||||
for (Tree Tree : treeLayout.getNodeBounds().keySet()) {
|
|
||||||
paintBox(g, Tree);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
|
||||||
|
|
||||||
private int arcSize = 10;
|
private int arcSize = 10;
|
||||||
|
|
||||||
|
@ -299,6 +56,112 @@ public class TreeViewer {
|
||||||
this.textColor = textColor;
|
this.textColor = textColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private TreeViewer.AntlrTreeLayout treeLayout;
|
||||||
|
|
||||||
|
private TreeForTreeLayout<Tree> getTree() {
|
||||||
|
return treeLayout.getTree();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Iterable<Tree> getChildren(Tree parent) {
|
||||||
|
return getTree().getChildren(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Rectangle2D.Double getBoundsOfNode(Tree node) {
|
||||||
|
return treeLayout.getNodeBounds().get(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getText(Tree tree) {
|
||||||
|
return treeTextProvider.getText(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TreeViewer(Tree tree) {
|
||||||
|
this.treeLayout = layouter.layout(tree);
|
||||||
|
Dimension size = treeLayout.getBounds().getBounds().getSize();
|
||||||
|
setPreferredSize(size);
|
||||||
|
setFont(font);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// painting
|
||||||
|
|
||||||
|
private void paintEdges(Graphics g, Tree parent) {
|
||||||
|
if (!getTree().isLeaf(parent)) {
|
||||||
|
Rectangle2D.Double b1 = getBoundsOfNode(parent);
|
||||||
|
double x1 = b1.getCenterX();
|
||||||
|
double y1 = b1.getCenterY();
|
||||||
|
for (Tree child : getChildren(parent)) {
|
||||||
|
Rectangle2D.Double b2 = getBoundsOfNode(child);
|
||||||
|
g.drawLine((int) x1, (int) y1, (int) b2.getCenterX(),
|
||||||
|
(int) b2.getCenterY());
|
||||||
|
|
||||||
|
paintEdges(g, child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void paintBox(Graphics g, Tree tree) {
|
||||||
|
// draw the box in the background
|
||||||
|
g.setColor(boxColor);
|
||||||
|
Rectangle2D.Double box = getBoundsOfNode(tree);
|
||||||
|
g.fillRoundRect((int) box.x, (int) box.y, (int) box.width - 1,
|
||||||
|
(int) box.height - 1, arcSize, arcSize);
|
||||||
|
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;
|
||||||
|
int y = (int) box.y + m.getAscent() + m.getLeading() + 1;
|
||||||
|
for (int i = 0; i < lines.length; i++) {
|
||||||
|
g.drawString(lines[i], x, y);
|
||||||
|
y += m.getHeight();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paint(Graphics g) {
|
||||||
|
super.paint(g);
|
||||||
|
|
||||||
|
paintEdges(g, getTree().getRoot());
|
||||||
|
|
||||||
|
// paint the boxes
|
||||||
|
for (Tree Tree : treeLayout.getNodeBounds().keySet()) {
|
||||||
|
paintBox(g, Tree);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
public static class VariableExtentProvide<Tree> implements NodeExtentProvider<Tree> {
|
||||||
|
@Override
|
||||||
|
public double getHeight(Tree tree) {
|
||||||
|
// FontMetrics m = getFontMetrics(font);
|
||||||
|
// String text = treeTextProvider.getText(tree);
|
||||||
|
// int x = (int) box.x + arcSize / 2;
|
||||||
|
// int y = (int) box.y + m.getAscent() + m.getLeading() + 1;
|
||||||
|
// int hgt = metrics.getHeight();
|
||||||
|
// // get the advance of my text in this font and render context
|
||||||
|
// int adv = metrics.stringWidth(text);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getWidth(Tree tree) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static class AntlrTreeLayout extends TreeLayout<Tree> {
|
||||||
|
public AntlrTreeLayout(TreeForTreeLayout<Tree> tree,
|
||||||
|
NodeExtentProvider<Tree> nodeExtentProvider,
|
||||||
|
Configuration<Tree> configuration) {
|
||||||
|
super(tree, nodeExtentProvider, configuration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
private TreeTextProvider treeTextProvider = new DefaultTreeTextProvider();
|
private TreeTextProvider treeTextProvider = new DefaultTreeTextProvider();
|
||||||
|
@ -328,10 +191,8 @@ public class TreeViewer {
|
||||||
dialog.setVisible(true);
|
dialog.setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void open(Tree tree) {
|
public void open() {
|
||||||
AntlrTreeLayout layout = layouter.layout(tree);
|
showInDialog(this);
|
||||||
AntlrTreePane panel = new AntlrTreePane(layout);
|
|
||||||
showInDialog(panel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,9 @@
|
||||||
grammar T;
|
grammar T;
|
||||||
s : i=ifstat {System.out.println(_input.toString(0,_input.index()-1));} ;
|
s : i=ifstat {System.out.println(_input.toString(0,_input.index()-1));} ;
|
||||||
|
|
||||||
ifstat : 'if' {$s::start = null;} '(' INT ')' ID '=' ID ';' # DoIf;
|
ifstat : 'if' '(' expr ')' assign ;
|
||||||
|
assign : ID '=' expr ';' ;
|
||||||
r[int x] returns [int y]
|
expr : INT | ID ;
|
||||||
locals [int z]
|
|
||||||
: name=ID # foo
|
|
||||||
| ID (ID|';'{;}) # bar
|
|
||||||
;
|
|
||||||
|
|
||||||
EQ : '=' ;
|
EQ : '=' ;
|
||||||
INT : '0'..'9'+ ;
|
INT : '0'..'9'+ ;
|
||||||
|
|
|
@ -1,9 +1,23 @@
|
||||||
import org.antlr.v4.runtime.ANTLRFileStream;
|
import org.antlr.v4.runtime.*;
|
||||||
import org.antlr.v4.runtime.CommonTokenStream;
|
import org.antlr.v4.runtime.tree.*;
|
||||||
import org.antlr.v4.runtime.ParserRuleContext;
|
import org.antlr.v4.runtime.tree.gui.*;
|
||||||
import org.antlr.v4.runtime.tree.ParseTreeWalker;
|
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
public class TestT {
|
public class TestT {
|
||||||
|
|
||||||
|
public static class MyTreeTextProvider implements TreeTextProvider {
|
||||||
|
BaseRecognizer parser;
|
||||||
|
public MyTreeTextProvider(BaseRecognizer parser) {
|
||||||
|
this.parser = parser;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getText(Tree node) {
|
||||||
|
return String.valueOf(Trees.getNodeText(node, parser));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
TLexer t = new TLexer(new ANTLRFileStream(args[0]));
|
TLexer t = new TLexer(new ANTLRFileStream(args[0]));
|
||||||
CommonTokenStream tokens = new CommonTokenStream(t);
|
CommonTokenStream tokens = new CommonTokenStream(t);
|
||||||
|
@ -16,16 +30,21 @@ public class TestT {
|
||||||
TParser.sContext tree = p.s();
|
TParser.sContext tree = p.s();
|
||||||
|
|
||||||
System.out.println(tree.toStringTree(p));
|
System.out.println(tree.toStringTree(p));
|
||||||
|
TreeViewer tv = new TreeViewer(tree);
|
||||||
ParseTreeWalker walker = new ParseTreeWalker();
|
tv.setTreeTextProvider(new MyTreeTextProvider(p));
|
||||||
TListener listener = new BlankTListener() {
|
tv.setBoxColor(Color.lightGray);
|
||||||
public void enterEveryRule(ParserRuleContext ctx) {
|
tv.setBorderColor(Color.darkGray);
|
||||||
System.out.println("enter rule "+TParser.ruleNames[ctx.ruleIndex]);
|
tv.open();
|
||||||
}
|
//
|
||||||
public void exitRule(TParser.DoIfContext ctx) { // specific to rule ifstat
|
// ParseTreeWalker walker = new ParseTreeWalker();
|
||||||
System.out.println("exit rule ifstat");
|
// TListener listener = new BlankTListener() {
|
||||||
}
|
// public void enterEveryRule(ParserRuleContext ctx) {
|
||||||
};
|
// System.out.println("enter rule "+TParser.ruleNames[ctx.ruleIndex]);
|
||||||
walker.walk(listener, tree);
|
// }
|
||||||
|
// public void exitRule(TParser.DoIfContext ctx) { // specific to rule ifstat
|
||||||
|
// System.out.println("exit rule ifstat");
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
// walker.walk(listener, tree);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue