add dbg classes. add mark/rewind

[git-p4: depot-paths = "//depot/code/antlr4/main/": change = 9034]
This commit is contained in:
parrt 2011-09-02 16:17:37 -08:00
parent 085dd05bf1
commit 62549b65bf
21 changed files with 3353 additions and 56 deletions

View File

@ -46,14 +46,14 @@ public interface IntStream {
int LA(int i);
/** Tell the stream to start buffering if it hasn't already. Return
* current input position, index(), or some other marker so that
* when passed to rewind() you get back to the same spot.
* rewind(mark()) should not affect the input cursor. The Lexer
* track line/col info as well as input index so its markers are
* not pure input indexes. Same for tree node streams.
* current input position, index(). seek(mark()) should not
* affect the input cursor.
*/
int mark();
/** Reset the stream so that next call to index() would return index arg. */
void rewind(int index);
/** Return the current input symbol index 0..n where n indicates the
* last symbol has been read. The index is the symbol about to be
* read not the most recently read symbol.

View File

@ -29,7 +29,8 @@
package org.antlr.v4.runtime;
import org.antlr.v4.runtime.tree.*;
import org.antlr.v4.runtime.tree.CommonTreeAdaptor;
import org.antlr.v4.runtime.tree.TreeAdaptor;
/** A parser for TokenStreams. "parser grammars" result in a subclass
* of this.
@ -86,11 +87,11 @@ public class Parser extends BaseRecognizer {
return input.getSourceName();
}
public void traceIn(String ruleName, int ruleIndex) {
super.traceIn(ruleName, ruleIndex, input.LT(1));
public void traceIn(int ruleIndex, ParserRuleContext ctx) {
super.traceIn(getRuleNames()[ruleIndex], ruleIndex, input.LT(1));
}
public void traceOut(String ruleName, int ruleIndex) {
super.traceOut(ruleName, ruleIndex, input.LT(1));
public void traceOut(int ruleIndex, ParserRuleContext ctx) {
super.traceOut(getRuleNames()[ruleIndex], ruleIndex, input.LT(1));
}
}

View File

@ -1,35 +0,0 @@
/*
[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;
public class RecognizerSharedState<StreamType> {
public StreamType input;
public RuleContext getContext() { return null; };
}

View File

@ -30,7 +30,8 @@
package org.antlr.v4.runtime.atn;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.dfa.*;
import org.antlr.v4.runtime.dfa.DFA;
import org.antlr.v4.runtime.dfa.DFAState;
import org.antlr.v4.runtime.misc.OrderedHashSet;
import org.stringtemplate.v4.misc.MultiMap;

View File

@ -0,0 +1,76 @@
/*
[The "BSD license"]
Copyright (c) 2005-2009 Terence Parr
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.antlr.v4.runtime.debug;
import org.antlr.v4.runtime.RecognitionException;
import org.antlr.v4.runtime.Token;
/** A blank listener that does nothing; useful for real classes so
* they don't have to have lots of blank methods and are less
* sensitive to updates to debug interface.
*/
public class BlankDebugEventListener implements DebugEventListener {
public void enterRule(String grammarFileName, String ruleName) {}
public void exitRule(String grammarFileName, String ruleName) {}
public void enterAlt(int alt) {}
public void enterSubRule(int decisionNumber) {}
public void exitSubRule(int decisionNumber) {}
public void enterDecision(int decisionNumber, boolean couldBacktrack) {}
public void exitDecision(int decisionNumber) {}
public void location(int line, int pos) {}
public void consumeToken(Token token) {}
public void consumeHiddenToken(Token token) {}
public void LT(int i, Token t) {}
public void mark(int i) {}
public void rewind(int i) {}
public void seek(int i) {}
public void recognitionException(RecognitionException e) {}
public void beginResync() {}
public void endResync() {}
public void semanticPredicate(boolean result, String predicate) {}
public void commence() {}
public void terminate() {}
// Tree parsing stuff
public void consumeNode(Object t) {}
public void LT(int i, Object t) {}
// AST Stuff
public void nilNode(Object t) {}
public void errorNode(Object t) {}
public void createNode(Object t) {}
public void createNode(Object node, Token token) {}
public void becomeRoot(Object newRoot, Object oldRoot) {}
public void addChild(Object root, Object child) {}
public void setTokenBoundaries(Object t, int tokenStartIndex, int tokenStopIndex) {}
}

View File

@ -0,0 +1,278 @@
/*
[The "BSD license"]
Copyright (c) 2005-2009 Terence Parr
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.antlr.v4.runtime.debug;
import org.antlr.v4.runtime.RecognitionException;
import org.antlr.v4.runtime.Token;
import java.util.ArrayList;
import java.util.List;
/** Broadcast debug events to multiple listeners. Lets you debug and still
* use the event mechanism to build parse trees etc... Not thread-safe.
* Don't add events in one thread while parser fires events in another.
*
* @see DebugEventRepeater
*/
public class DebugEventHub implements DebugEventListener {
protected List listeners = new ArrayList();
public DebugEventHub(DebugEventListener listener) {
listeners.add(listener);
}
public DebugEventHub(DebugEventListener a, DebugEventListener b) {
listeners.add(a);
listeners.add(b);
}
/** Add another listener to broadcast events too. Not thread-safe.
* Don't add events in one thread while parser fires events in another.
*/
public void addListener(DebugEventListener listener) {
listeners.add(listener);
}
/* To avoid a mess like this:
public void enterRule(final String ruleName) {
broadcast(new Code(){
public void exec(DebugEventListener listener) {listener.enterRule(ruleName);}}
);
}
I am dup'ing the for-loop in each. Where are Java closures!? blech!
*/
public void enterRule(String grammarFileName, String ruleName) {
for (int i = 0; i < listeners.size(); i++) {
DebugEventListener listener = (DebugEventListener)listeners.get(i);
listener.enterRule(grammarFileName, ruleName);
}
}
public void exitRule(String grammarFileName, String ruleName) {
for (int i = 0; i < listeners.size(); i++) {
DebugEventListener listener = (DebugEventListener)listeners.get(i);
listener.exitRule(grammarFileName, ruleName);
}
}
public void enterAlt(int alt) {
for (int i = 0; i < listeners.size(); i++) {
DebugEventListener listener = (DebugEventListener)listeners.get(i);
listener.enterAlt(alt);
}
}
public void enterSubRule(int decisionNumber) {
for (int i = 0; i < listeners.size(); i++) {
DebugEventListener listener = (DebugEventListener)listeners.get(i);
listener.enterSubRule(decisionNumber);
}
}
public void exitSubRule(int decisionNumber) {
for (int i = 0; i < listeners.size(); i++) {
DebugEventListener listener = (DebugEventListener)listeners.get(i);
listener.exitSubRule(decisionNumber);
}
}
public void enterDecision(int decisionNumber, boolean couldBacktrack) {
for (int i = 0; i < listeners.size(); i++) {
DebugEventListener listener = (DebugEventListener)listeners.get(i);
listener.enterDecision(decisionNumber, couldBacktrack);
}
}
public void exitDecision(int decisionNumber) {
for (int i = 0; i < listeners.size(); i++) {
DebugEventListener listener = (DebugEventListener)listeners.get(i);
listener.exitDecision(decisionNumber);
}
}
public void location(int line, int pos) {
for (int i = 0; i < listeners.size(); i++) {
DebugEventListener listener = (DebugEventListener)listeners.get(i);
listener.location(line, pos);
}
}
public void consumeToken(Token token) {
for (int i = 0; i < listeners.size(); i++) {
DebugEventListener listener = (DebugEventListener)listeners.get(i);
listener.consumeToken(token);
}
}
public void consumeHiddenToken(Token token) {
for (int i = 0; i < listeners.size(); i++) {
DebugEventListener listener = (DebugEventListener)listeners.get(i);
listener.consumeHiddenToken(token);
}
}
public void LT(int index, Token t) {
for (int i = 0; i < listeners.size(); i++) {
DebugEventListener listener = (DebugEventListener)listeners.get(i);
listener.LT(index, t);
}
}
public void mark(int index) {
for (int i = 0; i < listeners.size(); i++) {
DebugEventListener listener = (DebugEventListener)listeners.get(i);
listener.mark(index);
}
}
public void seek(int index) {
for (int i = 0; i < listeners.size(); i++) {
DebugEventListener listener = (DebugEventListener)listeners.get(i);
listener.seek(index);
}
}
public void rewind(int index) {
for (int i = 0; i < listeners.size(); i++) {
DebugEventListener listener = (DebugEventListener)listeners.get(i);
listener.rewind(index);
}
}
public void recognitionException(RecognitionException e) {
for (int i = 0; i < listeners.size(); i++) {
DebugEventListener listener = (DebugEventListener)listeners.get(i);
listener.recognitionException(e);
}
}
public void beginResync() {
for (int i = 0; i < listeners.size(); i++) {
DebugEventListener listener = (DebugEventListener)listeners.get(i);
listener.beginResync();
}
}
public void endResync() {
for (int i = 0; i < listeners.size(); i++) {
DebugEventListener listener = (DebugEventListener)listeners.get(i);
listener.endResync();
}
}
public void semanticPredicate(boolean result, String predicate) {
for (int i = 0; i < listeners.size(); i++) {
DebugEventListener listener = (DebugEventListener)listeners.get(i);
listener.semanticPredicate(result, predicate);
}
}
public void commence() {
for (int i = 0; i < listeners.size(); i++) {
DebugEventListener listener = (DebugEventListener)listeners.get(i);
listener.commence();
}
}
public void terminate() {
for (int i = 0; i < listeners.size(); i++) {
DebugEventListener listener = (DebugEventListener)listeners.get(i);
listener.terminate();
}
}
// Tree parsing stuff
public void consumeNode(Object t) {
for (int i = 0; i < listeners.size(); i++) {
DebugEventListener listener = (DebugEventListener)listeners.get(i);
listener.consumeNode(t);
}
}
public void LT(int index, Object t) {
for (int i = 0; i < listeners.size(); i++) {
DebugEventListener listener = (DebugEventListener)listeners.get(i);
listener.LT(index, t);
}
}
// AST Stuff
public void nilNode(Object t) {
for (int i = 0; i < listeners.size(); i++) {
DebugEventListener listener = (DebugEventListener)listeners.get(i);
listener.nilNode(t);
}
}
public void errorNode(Object t) {
for (int i = 0; i < listeners.size(); i++) {
DebugEventListener listener = (DebugEventListener)listeners.get(i);
listener.errorNode(t);
}
}
public void createNode(Object t) {
for (int i = 0; i < listeners.size(); i++) {
DebugEventListener listener = (DebugEventListener)listeners.get(i);
listener.createNode(t);
}
}
public void createNode(Object node, Token token) {
for (int i = 0; i < listeners.size(); i++) {
DebugEventListener listener = (DebugEventListener)listeners.get(i);
listener.createNode(node, token);
}
}
public void becomeRoot(Object newRoot, Object oldRoot) {
for (int i = 0; i < listeners.size(); i++) {
DebugEventListener listener = (DebugEventListener)listeners.get(i);
listener.becomeRoot(newRoot, oldRoot);
}
}
public void addChild(Object root, Object child) {
for (int i = 0; i < listeners.size(); i++) {
DebugEventListener listener = (DebugEventListener)listeners.get(i);
listener.addChild(root, child);
}
}
public void setTokenBoundaries(Object t, int tokenStartIndex, int tokenStopIndex) {
for (int i = 0; i < listeners.size(); i++) {
DebugEventListener listener = (DebugEventListener)listeners.get(i);
listener.setTokenBoundaries(t, tokenStartIndex, tokenStopIndex);
}
}
}

View File

@ -0,0 +1,311 @@
/*
[The "BSD license"]
Copyright (c) 2005-2009 Terence Parr
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.antlr.v4.runtime.debug;
import org.antlr.v4.runtime.RecognitionException;
import org.antlr.v4.runtime.Token;
/** All debugging events that a recognizer can trigger.
*
* I did not create a separate AST debugging interface as it would create
* lots of extra classes and DebugParser has a dbg var defined, which makes
* it hard to change to ASTDebugEventListener. I looked hard at this issue
* and it is easier to understand as one monolithic event interface for all
* possible events. Hopefully, adding ST debugging stuff won't be bad. Leave
* for future. 4/26/2006.
*/
public interface DebugEventListener {
/** Moved to version 2 for v3.1: added grammar name to enter/exit Rule */
public static final String PROTOCOL_VERSION = "2";
/** serialized version of true */
public static final int TRUE = 1;
public static final int FALSE = 0;
/** The parser has just entered a rule. No decision has been made about
* which alt is predicted. This is fired AFTER init actions have been
* executed. Attributes are defined and available etc...
* The grammarFileName allows composite grammars to jump around among
* multiple grammar files.
*/
public void enterRule(String grammarFileName, String ruleName);
/** Because rules can have lots of alternatives, it is very useful to
* know which alt you are entering. This is 1..n for n alts.
*/
public void enterAlt(int alt);
/** This is the last thing executed before leaving a rule. It is
* executed even if an exception is thrown. This is triggered after
* error reporting and recovery have occurred (unless the exception is
* not caught in this rule). This implies an "exitAlt" event.
* The grammarFileName allows composite grammars to jump around among
* multiple grammar files.
*/
public void exitRule(String grammarFileName, String ruleName);
/** Track entry into any (...) subrule other EBNF construct */
public void enterSubRule(int decisionNumber);
public void exitSubRule(int decisionNumber);
/** Every decision, fixed k or arbitrary, has an enter/exit event
* so that a GUI can easily track what LT/consume events are
* associated with prediction. You will see a single enter/exit
* subrule but multiple enter/exit decision events, one for each
* loop iteration.
*/
public void enterDecision(int decisionNumber, boolean couldBacktrack);
public void exitDecision(int decisionNumber);
/** The parser is going to look arbitrarily ahead; mark this location. */
public void mark(int index);
/** After an arbitrarily long lookahead, this informs the
* debugger that stream should be rewound to the position index.
*/
public void rewind(int index);
/** Set the input cursor to the position indicated by index. */
public void seek(int index);
/** An input token was consumed; matched by any kind of element.
* Trigger after the token was matched by things like match(), matchAny().
*/
public void consumeToken(Token t);
/** An off-channel input token was consumed.
* Trigger after the token was matched by things like match(), matchAny().
* (unless of course the hidden token is first stuff in the input stream).
*/
public void consumeHiddenToken(Token t);
/** Somebody (anybody) looked ahead. Note that this actually gets
* triggered by both LA and LT calls. The debugger will want to know
* which Token object was examined. Like consumeToken, this indicates
* what token was seen at that depth. A remote debugger cannot look
* ahead into a file it doesn't have so LT events must pass the token
* even if the info is redundant.
*/
public void LT(int i, Token t);
/** To watch a parser move through the grammar, the parser needs to
* inform the debugger what line/charPos it is passing in the grammar.
* For now, this does not know how to switch from one grammar to the
* other and back for island grammars etc...
*
* This should also allow breakpoints because the debugger can stop
* the parser whenever it hits this line/pos.
*/
public void location(int line, int pos);
/** A recognition exception occurred such as NoViableAltException. I made
* this a generic event so that I can alter the exception hierachy later
* without having to alter all the debug objects.
*
* Upon error, the stack of enter rule/subrule must be properly unwound.
* If no viable alt occurs it is within an enter/exit decision, which
* also must be rewound. Even the rewind for each mark must be unwount.
* In the Java target this is pretty easy using try/finally, if a bit
* ugly in the generated code. The rewind is generated in DFA.predict()
* actually so no code needs to be generated for that. For languages
* w/o this "finally" feature (C++?), the target implementor will have
* to build an event stack or something.
*
* Across a socket for remote debugging, only the RecognitionException
* data fields are transmitted. The token object or whatever that
* caused the problem was the last object referenced by LT. The
* immediately preceding LT event should hold the unexpected Token or
* char.
*
* Here is a sample event trace for grammar:
*
* b : C ({;}A|B) // {;} is there to prevent A|B becoming a set
* | D
* ;
*
* The sequence for this rule (with no viable alt in the subrule) for
* input 'c c' (there are 3 tokens) is:
*
* commence
* LT(1)
* enterRule b
* location 7 1
* enter decision 3
* LT(1)
* exit decision 3
* enterAlt1
* location 7 5
* LT(1)
* consumeToken [c/<4>,1:0]
* location 7 7
* enterSubRule 2
* enter decision 2
* LT(1)
* LT(1)
* recognitionException NoViableAltException 2 1 2
* exit decision 2
* exitSubRule 2
* beginResync
* LT(1)
* consumeToken [c/<4>,1:1]
* LT(1)
* endResync
* LT(-1)
* exitRule b
* terminate
*/
public void recognitionException(RecognitionException e);
/** Indicates the recognizer is about to consume tokens to resynchronize
* the parser. Any consume events from here until the recovered event
* are not part of the parse--they are dead tokens.
*/
public void beginResync();
/** Indicates that the recognizer has finished consuming tokens in order
* to resychronize. There may be multiple beginResync/endResync pairs
* before the recognizer comes out of errorRecovery mode (in which
* multiple errors are suppressed). This will be useful
* in a gui where you want to probably grey out tokens that are consumed
* but not matched to anything in grammar. Anything between
* a beginResync/endResync pair was tossed out by the parser.
*/
public void endResync();
/** A semantic predicate was evaluate with this result and action text */
public void semanticPredicate(boolean result, String predicate);
/** Announce that parsing has begun. Not technically useful except for
* sending events over a socket. A GUI for example will launch a thread
* to connect and communicate with a remote parser. The thread will want
* to notify the GUI when a connection is made. ANTLR parsers
* trigger this upon entry to the first rule (the ruleLevel is used to
* figure this out).
*/
public void commence();
/** Parsing is over; successfully or not. Mostly useful for telling
* remote debugging listeners that it's time to quit. When the rule
* invocation level goes to zero at the end of a rule, we are done
* parsing.
*/
public void terminate();
// T r e e P a r s i n g
/** Input for a tree parser is an AST, but we know nothing for sure
* about a node except its type and text (obtained from the adaptor).
* This is the analog of the consumeToken method. Again, the ID is
* the hashCode usually of the node so it only works if hashCode is
* not implemented. If the type is UP or DOWN, then
* the ID is not really meaningful as it's fixed--there is
* just one UP node and one DOWN navigation node.
* @param t
*/
public void consumeNode(Object t);
/** The tree parser lookedahead. If the type is UP or DOWN,
* then the ID is not really meaningful as it's fixed--there is
* just one UP node and one DOWN navigation node.
*/
public void LT(int i, Object t);
// A S T E v e n t s
/** A nil was created (even nil nodes have a unique ID...
* they are not "null" per se). As of 4/28/2006, this
* seems to be uniquely triggered when starting a new subtree
* such as when entering a subrule in automatic mode and when
* building a tree in rewrite mode.
*
* If you are receiving this event over a socket via
* RemoteDebugEventSocketListener then only t.ID is set.
*/
public void nilNode(Object t);
/** Upon syntax error, recognizers bracket the error with an error node
* if they are building ASTs.
* @param t
*/
public void errorNode(Object t);
/** Announce a new node built from token elements such as type etc...
*
* If you are receiving this event over a socket via
* RemoteDebugEventSocketListener then only t.ID, type, text are
* set.
*/
public void createNode(Object t);
/** Announce a new node built from an existing token.
*
* If you are receiving this event over a socket via
* RemoteDebugEventSocketListener then only node.ID and token.tokenIndex
* are set.
*/
public void createNode(Object node, Token token);
/** Make a node the new root of an existing root. See
*
* Note: the newRootID parameter is possibly different
* than the TreeAdaptor.becomeRoot() newRoot parameter.
* In our case, it will always be the result of calling
* TreeAdaptor.becomeRoot() and not root_n or whatever.
*
* The listener should assume that this event occurs
* only when the current subrule (or rule) subtree is
* being reset to newRootID.
*
* If you are receiving this event over a socket via
* RemoteDebugEventSocketListener then only IDs are set.
*
* @see org.antlr.v4.runtime.tree.TreeAdaptor becomeRoot()
*/
public void becomeRoot(Object newRoot, Object oldRoot);
/** Make childID a child of rootID.
*
* If you are receiving this event over a socket via
* RemoteDebugEventSocketListener then only IDs are set.
*
* @see org.antlr.v4.runtime.tree.TreeAdaptor addChild()
*/
public void addChild(Object root, Object child);
/** Set the token start/stop token index for a subtree root or node.
*
* If you are receiving this event over a socket via
* RemoteDebugEventSocketListener then only t.ID is set.
*/
public void setTokenBoundaries(Object t, int tokenStartIndex, int tokenStopIndex);
}

View File

@ -0,0 +1,86 @@
/*
[The "BSD license"]
Copyright (c) 2005-2009 Terence Parr
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.antlr.v4.runtime.debug;
import org.antlr.v4.runtime.RecognitionException;
import org.antlr.v4.runtime.Token;
/** A simple event repeater (proxy) that delegates all functionality to the
* listener sent into the ctor. Useful if you want to listen in on a few
* debug events w/o interrupting the debugger. Just subclass the repeater
* and override the methods you want to listen in on. Remember to call
* the method in this class so the event will continue on to the original
* recipient.
*
* @see DebugEventHub
*/
public class DebugEventRepeater implements DebugEventListener {
protected DebugEventListener listener;
public DebugEventRepeater(DebugEventListener listener) {
this.listener = listener;
}
public void enterRule(String grammarFileName, String ruleName) { listener.enterRule(grammarFileName, ruleName); }
public void exitRule(String grammarFileName, String ruleName) { listener.exitRule(grammarFileName, ruleName); }
public void enterAlt(int alt) { listener.enterAlt(alt); }
public void enterSubRule(int decisionNumber) { listener.enterSubRule(decisionNumber); }
public void exitSubRule(int decisionNumber) { listener.exitSubRule(decisionNumber); }
public void enterDecision(int decisionNumber, boolean couldBacktrack) { listener.enterDecision(decisionNumber, couldBacktrack); }
public void exitDecision(int decisionNumber) { listener.exitDecision(decisionNumber); }
public void location(int line, int pos) { listener.location(line, pos); }
public void consumeToken(Token token) { listener.consumeToken(token); }
public void consumeHiddenToken(Token token) { listener.consumeHiddenToken(token); }
public void LT(int i, Token t) { listener.LT(i, t); }
public void mark(int i) { listener.mark(i); }
public void rewind(int i) { listener.rewind(i); }
public void seek(int i) { listener.seek(i); }
public void recognitionException(RecognitionException e) { listener.recognitionException(e); }
public void beginResync() { listener.beginResync(); }
public void endResync() { listener.endResync(); }
public void semanticPredicate(boolean result, String predicate) { listener.semanticPredicate(result, predicate); }
public void commence() { listener.commence(); }
public void terminate() { listener.terminate(); }
// Tree parsing stuff
public void consumeNode(Object t) { listener.consumeNode(t); }
public void LT(int i, Object t) { listener.LT(i, t); }
// AST Stuff
public void nilNode(Object t) { listener.nilNode(t); }
public void errorNode(Object t) { listener.errorNode(t); }
public void createNode(Object t) { listener.createNode(t); }
public void createNode(Object node, Token token) { listener.createNode(node, token); }
public void becomeRoot(Object newRoot, Object oldRoot) { listener.becomeRoot(newRoot, oldRoot); }
public void addChild(Object root, Object child) { listener.addChild(root, child); }
public void setTokenBoundaries(Object t, int tokenStartIndex, int tokenStopIndex) {
listener.setTokenBoundaries(t, tokenStartIndex, tokenStopIndex);
}
}

View File

@ -0,0 +1,354 @@
/*
[The "BSD license"]
Copyright (c) 2005-2009 Terence Parr
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.antlr.v4.runtime.debug;
import org.antlr.v4.runtime.BaseRecognizer;
import org.antlr.v4.runtime.RecognitionException;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.TreeAdaptor;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
/** A proxy debug event listener that forwards events over a socket to
* a debugger (or any other listener) using a simple text-based protocol;
* one event per line. ANTLRWorks listens on server socket with a
* RemoteDebugEventSocketListener instance. These two objects must therefore
* be kept in sync. New events must be handled on both sides of socket.
*/
public class DebugEventSocketProxy extends BlankDebugEventListener {
public static final int DEFAULT_DEBUGGER_PORT = 49100; // was 49153
protected int port = DEFAULT_DEBUGGER_PORT;
protected ServerSocket serverSocket;
protected Socket socket;
protected String grammarFileName;
protected PrintWriter out;
protected BufferedReader in;
/** Who am i debugging? */
protected BaseRecognizer recognizer;
/** Almost certainly the recognizer will have adaptor set, but
* we don't know how to cast it (Parser or TreeParser) to get
* the adaptor field. Must be set with a constructor. :(
*/
protected TreeAdaptor adaptor;
public DebugEventSocketProxy(BaseRecognizer recognizer, TreeAdaptor adaptor) {
this(recognizer, DEFAULT_DEBUGGER_PORT, adaptor);
}
public DebugEventSocketProxy(BaseRecognizer recognizer, int port, TreeAdaptor adaptor) {
this.grammarFileName = recognizer.getGrammarFileName();
this.adaptor = adaptor;
this.port = port;
}
public void handshake() throws IOException {
if ( serverSocket==null ) {
serverSocket = new ServerSocket(port);
socket = serverSocket.accept();
socket.setTcpNoDelay(true);
OutputStream os = socket.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(os, "UTF8");
out = new PrintWriter(new BufferedWriter(osw));
InputStream is = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(is, "UTF8");
in = new BufferedReader(isr);
out.println("ANTLR "+ DebugEventListener.PROTOCOL_VERSION);
out.println("grammar \""+ grammarFileName);
out.flush();
ack();
}
}
public void commence() {
// don't bother sending event; listener will trigger upon connection
}
public void terminate() {
transmit("terminate");
out.close();
try {
socket.close();
}
catch (IOException ioe) {
ioe.printStackTrace(System.err);
}
}
protected void ack() {
try {
in.readLine();
}
catch (IOException ioe) {
ioe.printStackTrace(System.err);
}
}
protected void transmit(String event) {
out.println(event);
out.flush();
ack();
}
public void enterRule(String grammarFileName, String ruleName) {
transmit("enterRule\t"+grammarFileName+"\t"+ruleName);
}
public void enterAlt(int alt) {
transmit("enterAlt\t"+alt);
}
public void exitRule(String grammarFileName, String ruleName) {
transmit("exitRule\t"+grammarFileName+"\t"+ruleName);
}
public void enterSubRule(int decisionNumber) {
transmit("enterSubRule\t"+decisionNumber);
}
public void exitSubRule(int decisionNumber) {
transmit("exitSubRule\t"+decisionNumber);
}
public void enterDecision(int decisionNumber, boolean couldBacktrack) {
transmit("enterDecision\t"+decisionNumber+"\t"+couldBacktrack);
}
public void exitDecision(int decisionNumber) {
transmit("exitDecision\t"+decisionNumber);
}
public void consumeToken(Token t) {
String buf = serializeToken(t);
transmit("consumeToken\t"+buf);
}
public void consumeHiddenToken(Token t) {
String buf = serializeToken(t);
transmit("consumeHiddenToken\t"+buf);
}
public void LT(int i, Token t) {
if(t != null)
transmit("LT\t"+i+"\t"+serializeToken(t));
}
public void mark(int i) {
transmit("mark\t"+i);
}
public void rewind(int i) {
transmit("rewind\t"+i);
}
public void rewind() {
transmit("rewind");
}
public void beginBacktrack(int level) {
transmit("beginBacktrack\t"+level);
}
public void endBacktrack(int level, boolean successful) {
transmit("endBacktrack\t"+level+"\t"+(successful?TRUE:FALSE));
}
public void location(int line, int pos) {
transmit("location\t"+line+"\t"+pos);
}
public void recognitionException(RecognitionException e) {
StringBuffer buf = new StringBuffer(50);
buf.append("exception\t");
buf.append(e.getClass().getName());
// dump only the data common to all exceptions for now
buf.append("\t");
buf.append(e.index);
buf.append("\t");
buf.append(e.line);
buf.append("\t");
buf.append(e.charPositionInLine);
transmit(buf.toString());
}
public void beginResync() {
transmit("beginResync");
}
public void endResync() {
transmit("endResync");
}
public void semanticPredicate(boolean result, String predicate) {
StringBuffer buf = new StringBuffer(50);
buf.append("semanticPredicate\t");
buf.append(result);
serializeText(buf, predicate);
transmit(buf.toString());
}
// A S T P a r s i n g E v e n t s
public void consumeNode(Object t) {
StringBuffer buf = new StringBuffer(50);
buf.append("consumeNode");
serializeNode(buf, t);
transmit(buf.toString());
}
public void LT(int i, Object t) {
int ID = adaptor.getUniqueID(t);
String text = adaptor.getText(t);
int type = adaptor.getType(t);
StringBuffer buf = new StringBuffer(50);
buf.append("LN\t"); // lookahead node; distinguish from LT in protocol
buf.append(i);
serializeNode(buf, t);
transmit(buf.toString());
}
protected void serializeNode(StringBuffer buf, Object t) {
int ID = adaptor.getUniqueID(t);
String text = adaptor.getText(t);
int type = adaptor.getType(t);
buf.append("\t");
buf.append(ID);
buf.append("\t");
buf.append(type);
Token token = adaptor.getToken(t);
int line = -1;
int pos = -1;
if ( token!=null ) {
line = token.getLine();
pos = token.getCharPositionInLine();
}
buf.append("\t");
buf.append(line);
buf.append("\t");
buf.append(pos);
int tokenIndex = adaptor.getTokenStartIndex(t);
buf.append("\t");
buf.append(tokenIndex);
serializeText(buf, text);
}
// A S T E v e n t s
public void nilNode(Object t) {
int ID = adaptor.getUniqueID(t);
transmit("nilNode\t"+ID);
}
public void errorNode(Object t) {
int ID = adaptor.getUniqueID(t);
String text = t.toString();
StringBuffer buf = new StringBuffer(50);
buf.append("errorNode\t");
buf.append(ID);
buf.append("\t");
buf.append(Token.INVALID_TYPE);
serializeText(buf, text);
transmit(buf.toString());
}
public void createNode(Object t) {
int ID = adaptor.getUniqueID(t);
String text = adaptor.getText(t);
int type = adaptor.getType(t);
StringBuffer buf = new StringBuffer(50);
buf.append("createNodeFromTokenElements\t");
buf.append(ID);
buf.append("\t");
buf.append(type);
serializeText(buf, text);
transmit(buf.toString());
}
public void createNode(Object node, Token token) {
int ID = adaptor.getUniqueID(node);
int tokenIndex = token.getTokenIndex();
transmit("createNode\t"+ID+"\t"+tokenIndex);
}
public void becomeRoot(Object newRoot, Object oldRoot) {
int newRootID = adaptor.getUniqueID(newRoot);
int oldRootID = adaptor.getUniqueID(oldRoot);
transmit("becomeRoot\t"+newRootID+"\t"+oldRootID);
}
public void addChild(Object root, Object child) {
int rootID = adaptor.getUniqueID(root);
int childID = adaptor.getUniqueID(child);
transmit("addChild\t"+rootID+"\t"+childID);
}
public void setTokenBoundaries(Object t, int tokenStartIndex, int tokenStopIndex) {
int ID = adaptor.getUniqueID(t);
transmit("setTokenBoundaries\t"+ID+"\t"+tokenStartIndex+"\t"+tokenStopIndex);
}
// support
public void setTreeAdaptor(TreeAdaptor adaptor) { this.adaptor = adaptor; }
public TreeAdaptor getTreeAdaptor() { return adaptor; }
protected String serializeToken(Token t) {
StringBuffer buf = new StringBuffer(50);
buf.append(t.getTokenIndex()); buf.append('\t');
buf.append(t.getType()); buf.append('\t');
buf.append(t.getChannel()); buf.append('\t');
buf.append(t.getLine()); buf.append('\t');
buf.append(t.getCharPositionInLine());
serializeText(buf, t.getText());
return buf.toString();
}
protected void serializeText(StringBuffer buf, String text) {
buf.append("\t\"");
if ( text==null ) {
text = "";
}
// escape \n and \r all text for token appears to exist on one line
// this escape is slow but easy to understand
text = escapeNewlines(text);
buf.append(text);
}
protected String escapeNewlines(String txt) {
txt = txt.replaceAll("%","%25"); // escape all escape char ;)
txt = txt.replaceAll("\n","%0A"); // escape \n
txt = txt.replaceAll("\r","%0D"); // escape \r
return txt;
}
}

View File

@ -0,0 +1,83 @@
/*
[The "BSD license"]
Copyright (c) 2005-2009 Terence Parr
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.antlr.v4.runtime.debug;
import org.antlr.v4.runtime.Parser;
import org.antlr.v4.runtime.RecognitionException;
import org.antlr.v4.runtime.TokenStream;
import java.io.IOException;
public class DebugParser extends Parser {
/** Who to notify when events in the parser occur. */
protected DebugEventListener dbg = null;
/** Create a normal parser except wrap the token stream in a debug
* proxy that fires consume events.
*/
public DebugParser(TokenStream input, DebugEventListener dbg) {
super(input instanceof DebugTokenStream?input:new DebugTokenStream(input,dbg));
setDebugListener(dbg);
}
public DebugParser(TokenStream input) {
super(input instanceof DebugTokenStream ? input : new DebugTokenStream(input, null));
}
/** Provide a new debug event listener for this parser. Notify the
* input stream too that it should send events to this listener.
*/
public void setDebugListener(DebugEventListener dbg) {
if ( input instanceof DebugTokenStream ) {
((DebugTokenStream)input).setDebugListener(dbg);
}
this.dbg = dbg;
}
public DebugEventListener getDebugListener() {
return dbg;
}
public void reportError(IOException e) {
System.err.println(e);
e.printStackTrace(System.err);
}
public void beginResync() {
dbg.beginResync();
}
public void endResync() {
dbg.endResync();
}
public void reportError(RecognitionException e) {
super.reportError(e);
dbg.recognitionException(e);
}
}

View File

@ -0,0 +1,148 @@
/*
[The "BSD license"]
Copyright (c) 2005-2009 Terence Parr
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.antlr.v4.runtime.debug;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.TokenSource;
import org.antlr.v4.runtime.TokenStream;
public class DebugTokenStream implements TokenStream {
protected DebugEventListener dbg;
public TokenStream input;
protected boolean initialStreamState = true;
/** Track the last mark() call result value for use in rewind(). */
protected int lastMarker;
public DebugTokenStream(TokenStream input, DebugEventListener dbg) {
this.input = input;
setDebugListener(dbg);
// force TokenStream to get at least first valid token
// so we know if there are any hidden tokens first in the stream
input.LT(1);
}
public void setDebugListener(DebugEventListener dbg) {
this.dbg = dbg;
}
public void consume() {
if ( initialStreamState ) {
consumeInitialHiddenTokens();
}
int a = input.index();
Token t = input.LT(1);
input.consume();
int b = input.index();
dbg.consumeToken(t);
if ( b>a+1 ) {
// then we consumed more than one token; must be off channel tokens
for (int i=a+1; i<b; i++) {
dbg.consumeHiddenToken(input.get(i));
}
}
}
/* consume all initial off-channel tokens */
protected void consumeInitialHiddenTokens() {
int firstOnChannelTokenIndex = input.index();
for (int i=0; i<firstOnChannelTokenIndex; i++) {
dbg.consumeHiddenToken(input.get(i));
}
initialStreamState = false;
}
public Token LT(int i) {
if ( initialStreamState ) {
consumeInitialHiddenTokens();
}
dbg.LT(i, input.LT(i));
return input.LT(i);
}
public int LA(int i) {
if ( initialStreamState ) {
consumeInitialHiddenTokens();
}
dbg.LT(i, input.LT(i));
return input.LA(i);
}
public Token get(int i) {
return input.get(i);
}
public int mark() {
lastMarker = input.mark();
dbg.mark(lastMarker);
return lastMarker;
}
public int index() {
return input.index();
}
public int range() {
return input.range();
}
public void rewind(int index) {
dbg.rewind(index);
input.seek(index);
}
public void seek(int index) {
// TODO: implement seek in dbg interface
// db.seek(index);
input.seek(index);
}
public int size() {
return input.size();
}
public TokenSource getTokenSource() {
return input.getTokenSource();
}
public String getSourceName() {
return getTokenSource().getSourceName();
}
public String toString() {
return input.toString();
}
public String toString(int start, int stop) {
return input.toString(start,stop);
}
public String toString(Token start, Token stop) {
return input.toString(start,stop);
}
}

View File

@ -0,0 +1,256 @@
/*
[The "BSD license"]
Copyright (c) 2005-2009 Terence Parr
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.antlr.v4.runtime.debug;
import org.antlr.v4.runtime.RecognitionException;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.TokenStream;
import org.antlr.v4.runtime.tree.TreeAdaptor;
import java.util.List;
/** A TreeAdaptor proxy that fires debugging events to a DebugEventListener
* delegate and uses the TreeAdaptor delegate to do the actual work. All
* AST events are triggered by this adaptor; no code gen changes are needed
* in generated rules. Debugging events are triggered *after* invoking
* tree adaptor routines.
*
* Trees created with actions in rewrite actions like "-> ^(ADD {foo} {bar})"
* cannot be tracked as they might not use the adaptor to create foo, bar.
* The debug listener has to deal with tree node IDs for which it did
* not see a createNode event. A single <unknown> node is sufficient even
* if it represents a whole tree.
*/
public class DebugTreeAdaptor implements TreeAdaptor {
protected DebugEventListener dbg;
protected TreeAdaptor adaptor;
public DebugTreeAdaptor(DebugEventListener dbg, TreeAdaptor adaptor) {
this.dbg = dbg;
this.adaptor = adaptor;
}
public Object create(Token payload) {
if ( payload.getTokenIndex() < 0 ) {
// could be token conjured up during error recovery
return create(payload.getType(), payload.getText());
}
Object node = adaptor.create(payload);
dbg.createNode(node, payload);
return node;
}
public List<Object> createElementList() {
return adaptor.createElementList();
}
public Object errorNode(TokenStream input, Token start, Token stop,
RecognitionException e)
{
Object node = adaptor.errorNode(input, start, stop, e);
if ( node!=null ) {
dbg.errorNode(node);
}
return node;
}
public Object dupTree(Object tree) {
Object t = adaptor.dupTree(tree);
// walk the tree and emit create and add child events
// to simulate what dupTree has done. dupTree does not call this debug
// adapter so I must simulate.
simulateTreeConstruction(t);
return t;
}
/** ^(A B C): emit create A, create B, add child, ...*/
protected void simulateTreeConstruction(Object t) {
dbg.createNode(t);
int n = adaptor.getChildCount(t);
for (int i=0; i<n; i++) {
Object child = adaptor.getChild(t, i);
simulateTreeConstruction(child);
dbg.addChild(t, child);
}
}
public Object dupNode(Object treeNode) {
Object d = adaptor.dupNode(treeNode);
dbg.createNode(d);
return d;
}
public Object nil() {
Object node = adaptor.nil();
dbg.nilNode(node);
return node;
}
public boolean isNil(Object tree) {
return adaptor.isNil(tree);
}
public void addChild(Object t, Object child) {
if ( t==null || child==null ) {
return;
}
adaptor.addChild(t,child);
dbg.addChild(t, child);
}
public Object becomeRoot(Object newRoot, Object oldRoot) {
Object n = adaptor.becomeRoot(newRoot, oldRoot);
dbg.becomeRoot(newRoot, oldRoot);
return n;
}
public Object rulePostProcessing(Object root) {
return adaptor.rulePostProcessing(root);
}
public void addChild(Object t, Token child) {
Object n = this.create(child);
this.addChild(t, n);
}
public Object becomeRoot(Token newRoot, Object oldRoot) {
Object n = this.create(newRoot);
adaptor.becomeRoot(n, oldRoot);
dbg.becomeRoot(newRoot, oldRoot);
return n;
}
public Object create(int tokenType, Token fromToken) {
Object node = adaptor.create(tokenType, fromToken);
dbg.createNode(node);
return node;
}
public Object create(int tokenType, Token fromToken, String text) {
Object node = adaptor.create(tokenType, fromToken, text);
dbg.createNode(node);
return node;
}
public Object create(int tokenType, String text) {
Object node = adaptor.create(tokenType, text);
dbg.createNode(node);
return node;
}
public int getType(Object t) {
return adaptor.getType(t);
}
public void setType(Object t, int type) {
adaptor.setType(t, type);
}
public String getText(Object t) {
return adaptor.getText(t);
}
public void setText(Object t, String text) {
adaptor.setText(t, text);
}
public Token getToken(Object t) {
return adaptor.getToken(t);
}
public void setTokenBoundaries(Object t, Token startToken, Token stopToken) {
adaptor.setTokenBoundaries(t, startToken, stopToken);
if ( t!=null && startToken!=null && stopToken!=null ) {
dbg.setTokenBoundaries(
t, startToken.getTokenIndex(),
stopToken.getTokenIndex());
}
}
public int getTokenStartIndex(Object t) {
return adaptor.getTokenStartIndex(t);
}
public int getTokenStopIndex(Object t) {
return adaptor.getTokenStopIndex(t);
}
public Object getChild(Object t, int i) {
return adaptor.getChild(t, i);
}
public void setChild(Object t, int i, Object child) {
adaptor.setChild(t, i, child);
}
public Object deleteChild(Object t, int i) {
return deleteChild(t, i);
}
public int getChildCount(Object t) {
return adaptor.getChildCount(t);
}
public int getUniqueID(Object node) {
return adaptor.getUniqueID(node);
}
public Object getParent(Object t) {
return adaptor.getParent(t);
}
public int getChildIndex(Object t) {
return adaptor.getChildIndex(t);
}
public void setParent(Object t, Object parent) {
adaptor.setParent(t, parent);
}
public void setChildIndex(Object t, int index) {
adaptor.setChildIndex(t, index);
}
public void replaceChildren(Object parent, int startChildIndex, int stopChildIndex, Object t) {
adaptor.replaceChildren(parent, startChildIndex, stopChildIndex, t);
}
// support
public DebugEventListener getDebugListener() {
return dbg;
}
public void setDebugListener(DebugEventListener dbg) {
this.dbg = dbg;
}
public TreeAdaptor getTreeAdaptor() {
return adaptor;
}
}

View File

@ -0,0 +1,147 @@
/*
[The "BSD license"]
Copyright (c) 2005-2009 Terence Parr
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.antlr.v4.runtime.debug;
import org.antlr.v4.runtime.TokenStream;
import org.antlr.v4.runtime.tree.TreeAdaptor;
import org.antlr.v4.runtime.tree.TreeNodeStream;
/** Debug any tree node stream. The constructor accepts the stream
* and a debug listener. As node stream calls come in, debug events
* are triggered.
*/
public class DebugTreeNodeStream implements TreeNodeStream {
protected DebugEventListener dbg;
protected TreeAdaptor adaptor;
protected TreeNodeStream input;
protected boolean initialStreamState = true;
/** Track the last mark() call result value for use in rewind(). */
protected int lastMarker;
public DebugTreeNodeStream(TreeNodeStream input,
DebugEventListener dbg)
{
this.input = input;
this.adaptor = input.getTreeAdaptor();
this.input.setUniqueNavigationNodes(true);
setDebugListener(dbg);
}
public void setDebugListener(DebugEventListener dbg) {
this.dbg = dbg;
}
public TreeAdaptor getTreeAdaptor() {
return adaptor;
}
public void consume() {
Object node = input.LT(1);
input.consume();
dbg.consumeNode(node);
}
public Object get(int i) {
return input.get(i);
}
public Object LT(int i) {
Object node = input.LT(i);
int ID = adaptor.getUniqueID(node);
String text = adaptor.getText(node);
int type = adaptor.getType(node);
dbg.LT(i, node);
return node;
}
public int LA(int i) {
Object node = input.LT(i);
int ID = adaptor.getUniqueID(node);
String text = adaptor.getText(node);
int type = adaptor.getType(node);
dbg.LT(i, node);
return type;
}
public int mark() {
lastMarker = input.mark();
dbg.mark(lastMarker);
return lastMarker;
}
public void rewind(int marker) {
dbg.rewind(marker);
input.rewind(marker);
}
public int index() {
return input.index();
}
public void seek(int index) {
// TODO: implement seek in dbg interface
// db.seek(index);
input.seek(index);
}
public int size() {
return input.size();
}
public void reset() { ; }
public Object getTreeSource() {
return input;
}
public String getSourceName() {
return getTokenStream().getSourceName();
}
public TokenStream getTokenStream() {
return input.getTokenStream();
}
/** It is normally this object that instructs the node stream to
* create unique nav nodes, but to satisfy interface, we have to
* define it. It might be better to ignore the parameter but
* there might be a use for it later, so I'll leave.
*/
public void setUniqueNavigationNodes(boolean uniqueNavigationNodes) {
input.setUniqueNavigationNodes(uniqueNavigationNodes);
}
public void replaceChildren(Object parent, int startChildIndex, int stopChildIndex, Object t) {
input.replaceChildren(parent, startChildIndex, stopChildIndex, t);
}
public String toString(Object start, Object stop) {
return input.toString(start,stop);
}
}

View File

@ -0,0 +1,97 @@
/*
[The "BSD license"]
Copyright (c) 2005-2009 Terence Parr
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.antlr.v4.runtime.debug;
import org.antlr.v4.runtime.RecognitionException;
import org.antlr.v4.runtime.tree.TreeNodeStream;
import org.antlr.v4.runtime.tree.TreeParser;
import java.io.IOException;
public class DebugTreeParser extends TreeParser {
/** Who to notify when events in the parser occur. */
protected DebugEventListener dbg = null;
/** Used to differentiate between fixed lookahead and cyclic DFA decisions
* while profiling.
*/
public boolean isCyclicDecision = false;
/** Create a normal parser except wrap the token stream in a debug
* proxy that fires consume events.
*/
public DebugTreeParser(TreeNodeStream input, DebugEventListener dbg) {
super(input instanceof DebugTreeNodeStream?input:new DebugTreeNodeStream(input,dbg));
setDebugListener(dbg);
}
public DebugTreeParser(TreeNodeStream input) {
super(input instanceof DebugTreeNodeStream?input:new DebugTreeNodeStream(input,null));
}
/** Provide a new debug event listener for this parser. Notify the
* input stream too that it should send events to this listener.
*/
public void setDebugListener(DebugEventListener dbg) {
if ( input instanceof DebugTreeNodeStream ) {
((DebugTreeNodeStream)input).setDebugListener(dbg);
}
this.dbg = dbg;
}
public DebugEventListener getDebugListener() {
return dbg;
}
public void reportError(IOException e) {
System.err.println(e);
e.printStackTrace(System.err);
}
public void reportError(RecognitionException e) {
dbg.recognitionException(e);
}
// protected Object getMissingSymbol(IntStream input,
// RecognitionException e,
// int expectedTokenType,
// IntervalSet follow)
// {
// Object o = super.getMissingSymbol(input, e, expectedTokenType, follow);
// dbg.consumeNode(o);
// return o;
// }
public void beginResync() {
dbg.beginResync();
}
public void endResync() {
dbg.endResync();
}
}

View File

@ -0,0 +1,109 @@
/*
[The "BSD license"]
Copyright (c) 2005-2009 Terence Parr
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.antlr.v4.runtime.debug;
import org.antlr.runtime.tree.ParseTree;
import org.antlr.v4.runtime.RecognitionException;
import org.antlr.v4.runtime.Token;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
/** This parser listener tracks rule entry/exit and token matches
* to build a simple parse tree using ParseTree nodes.
*/
public class ParseTreeBuilder extends BlankDebugEventListener {
public static final String EPSILON_PAYLOAD = "<epsilon>";
Stack callStack = new Stack();
List hiddenTokens = new ArrayList();
int backtracking = 0;
public ParseTreeBuilder(String grammarName) {
ParseTree root = create("<grammar "+grammarName+">");
callStack.push(root);
}
public ParseTree getTree() {
return (ParseTree)callStack.elementAt(0);
}
/** What kind of node to create. You might want to override
* so I factored out creation here.
*/
public ParseTree create(Object payload) {
return new ParseTree(payload);
}
public ParseTree epsilonNode() {
return create(EPSILON_PAYLOAD);
}
/** Backtracking or cyclic DFA, don't want to add nodes to tree */
public void enterDecision(int d, boolean couldBacktrack) { backtracking++; }
public void exitDecision(int i) { backtracking--; }
public void enterRule(String filename, String ruleName) {
if ( backtracking>0 ) return;
ParseTree parentRuleNode = (ParseTree)callStack.peek();
ParseTree ruleNode = create(ruleName);
parentRuleNode.addChild(ruleNode);
callStack.push(ruleNode);
}
public void exitRule(String filename, String ruleName) {
if ( backtracking>0 ) return;
ParseTree ruleNode = (ParseTree)callStack.peek();
if ( ruleNode.getChildCount()==0 ) {
ruleNode.addChild(epsilonNode());
}
callStack.pop();
}
public void consumeToken(Token token) {
if ( backtracking>0 ) return;
ParseTree ruleNode = (ParseTree)callStack.peek();
ParseTree elementNode = create(token);
elementNode.hiddenTokens = this.hiddenTokens;
this.hiddenTokens = new ArrayList();
ruleNode.addChild(elementNode);
}
public void consumeHiddenToken(Token token) {
if ( backtracking>0 ) return;
hiddenTokens.add(token);
}
public void recognitionException(RecognitionException e) {
if ( backtracking>0 ) return;
ParseTree ruleNode = (ParseTree)callStack.peek();
ParseTree errorNode = create(e);
ruleNode.addChild(errorNode);
}
}

View File

@ -0,0 +1,722 @@
/*
[The "BSD license"]
Copyright (c) 2005-2009 Terence Parr
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.antlr.v4.runtime.debug;
import org.antlr.runtime.misc.DoubleKeyMap;
import org.antlr.v4.runtime.*;
import java.util.*;
/** Using the debug event interface, track what is happening in the parser
* and record statistics about the runtime.
*/
public class Profiler extends BlankDebugEventListener {
public static final String DATA_SEP = "\t";
public static final String newline = System.getProperty("line.separator");
static boolean dump = false;
public static class ProfileStats {
public String Version;
public String name;
public int numRuleInvocations;
public int numUniqueRulesInvoked;
public int numDecisionEvents;
public int numDecisionsCovered;
public int numDecisionsThatPotentiallyBacktrack;
public int numDecisionsThatDoBacktrack;
public int maxRuleInvocationDepth;
public float avgkPerDecisionEvent;
public float avgkPerBacktrackingDecisionEvent;
public float averageDecisionPercentBacktracks;
public int numBacktrackOccurrences; // doesn't count gated DFA edges
public int numFixedDecisions;
public int minDecisionMaxFixedLookaheads;
public int maxDecisionMaxFixedLookaheads;
public int avgDecisionMaxFixedLookaheads;
public int stddevDecisionMaxFixedLookaheads;
public int numCyclicDecisions;
public int minDecisionMaxCyclicLookaheads;
public int maxDecisionMaxCyclicLookaheads;
public int avgDecisionMaxCyclicLookaheads;
public int stddevDecisionMaxCyclicLookaheads;
// int Stats.min(toArray(decisionMaxSynPredLookaheads);
// int Stats.max(toArray(decisionMaxSynPredLookaheads);
// int Stats.avg(toArray(decisionMaxSynPredLookaheads);
// int Stats.stddev(toArray(decisionMaxSynPredLookaheads);
public int numSemanticPredicates;
public int numTokens;
public int numHiddenTokens;
public int numCharsMatched;
public int numHiddenCharsMatched;
public int numReportedErrors;
public int numMemoizationCacheHits;
public int numMemoizationCacheMisses;
public int numGuessingRuleInvocations;
public int numMemoizationCacheEntries;
}
public static class DecisionDescriptor {
public int decision;
public String fileName;
public String ruleName;
public int line;
public int pos;
public boolean couldBacktrack;
public int n;
public float avgk; // avg across all decision events
public int maxk;
public int numBacktrackOccurrences;
public int numSemPredEvals;
}
// all about a specific exec of a single decision
public static class DecisionEvent {
public DecisionDescriptor decision;
public int startIndex;
public int k;
public boolean backtracks; // doesn't count gated DFA edges
public boolean evalSemPred;
public long startTime;
public long stopTime;
public int numMemoizationCacheHits;
public int numMemoizationCacheMisses;
}
/** Because I may change the stats, I need to track that for later
* computations to be consistent.
*/
public static final String Version = "3";
public static final String RUNTIME_STATS_FILENAME = "runtime.stats";
/** Ack, should not store parser; can't do remote stuff. Well, we pass
* input stream around too so I guess it's ok.
*/
public DebugParser parser = null;
// working variables
protected int ruleLevel = 0;
//protected int decisionLevel = 0;
protected Token lastRealTokenTouchedInDecision;
protected Set<String> uniqueRules = new HashSet<String>();
protected Stack<String> currentGrammarFileName = new Stack();
protected Stack<String> currentRuleName = new Stack();
protected Stack<Integer> currentLine = new Stack();
protected Stack<Integer> currentPos = new Stack();
// Vector<DecisionStats>
//protected Vector decisions = new Vector(200); // need setSize
protected DoubleKeyMap<String,Integer, DecisionDescriptor> decisions =
new DoubleKeyMap<String,Integer, DecisionDescriptor>();
// Record a DecisionData for each decision we hit while parsing
protected List<DecisionEvent> decisionEvents = new ArrayList<DecisionEvent>();
protected Stack<DecisionEvent> decisionStack = new Stack<DecisionEvent>();
protected int backtrackDepth;
ProfileStats stats = new ProfileStats();
public Profiler() {
}
public Profiler(DebugParser parser) {
this.parser = parser;
}
public void enterRule(String grammarFileName, String ruleName) {
// System.out.println("enterRule "+grammarFileName+":"+ruleName);
ruleLevel++;
stats.numRuleInvocations++;
uniqueRules.add(grammarFileName+":"+ruleName);
stats.maxRuleInvocationDepth = Math.max(stats.maxRuleInvocationDepth, ruleLevel);
currentGrammarFileName.push(grammarFileName);
currentRuleName.push(ruleName);
}
public void exitRule(String grammarFileName, String ruleName) {
ruleLevel--;
currentGrammarFileName.pop();
currentRuleName.pop();
}
/** Track memoization; this is not part of standard debug interface
* but is triggered by profiling. Code gen inserts an override
* for this method in the recognizer, which triggers this method.
* Called from alreadyParsedRule().
*/
public void examineRuleMemoization(IntStream input,
int ruleIndex,
int stopIndex, // index or MEMO_RULE_UNKNOWN...
String ruleName)
{
if (dump) System.out.println("examine memo "+ruleName+" at "+input.index()+": "+stopIndex);
if ( stopIndex== BaseRecognizer.MEMO_RULE_UNKNOWN ) {
//System.out.println("rule "+ruleIndex+" missed @ "+input.index());
stats.numMemoizationCacheMisses++;
stats.numGuessingRuleInvocations++; // we'll have to enter
currentDecision().numMemoizationCacheMisses++;
}
else {
// regardless of rule success/failure, if in cache, we have a cache hit
//System.out.println("rule "+ruleIndex+" hit @ "+input.index());
stats.numMemoizationCacheHits++;
currentDecision().numMemoizationCacheHits++;
}
}
/** Warning: doesn't track success/failure, just unique recording event */
public void memoize(IntStream input,
int ruleIndex,
int ruleStartIndex,
String ruleName)
{
// count how many entries go into table
if (dump) System.out.println("memoize "+ruleName);
stats.numMemoizationCacheEntries++;
}
@Override
public void location(int line, int pos) {
currentLine.push(line);
currentPos.push(pos);
}
public void enterDecision(int decisionNumber, boolean couldBacktrack) {
lastRealTokenTouchedInDecision = null;
stats.numDecisionEvents++;
int startingLookaheadIndex = parser.getTokenStream().index();
TokenStream input = parser.getTokenStream();
if ( dump ) System.out.println("enterDecision canBacktrack="+couldBacktrack+" "+ decisionNumber +
" backtrack depth " + backtrackDepth +
" @ " + input.get(input.index()) +
" rule " +locationDescription());
String g = (String) currentGrammarFileName.peek();
DecisionDescriptor descriptor = decisions.get(g, decisionNumber);
if ( descriptor == null ) {
descriptor = new DecisionDescriptor();
decisions.put(g, decisionNumber, descriptor);
descriptor.decision = decisionNumber;
descriptor.fileName = (String)currentGrammarFileName.peek();
descriptor.ruleName = (String)currentRuleName.peek();
descriptor.line = (Integer)currentLine.peek();
descriptor.pos = (Integer)currentPos.peek();
descriptor.couldBacktrack = couldBacktrack;
}
descriptor.n++;
DecisionEvent d = new DecisionEvent();
decisionStack.push(d);
d.decision = descriptor;
d.startTime = System.currentTimeMillis();
d.startIndex = startingLookaheadIndex;
}
public void exitDecision(int decisionNumber) {
DecisionEvent d = decisionStack.pop();
d.stopTime = System.currentTimeMillis();
int lastTokenIndex = lastRealTokenTouchedInDecision.getTokenIndex();
int numHidden = getNumberOfHiddenTokens(d.startIndex, lastTokenIndex);
int depth = lastTokenIndex - d.startIndex - numHidden + 1; // +1 counts consuming start token as 1
d.k = depth;
d.decision.maxk = Math.max(d.decision.maxk, depth);
if (dump) System.out.println("exitDecision "+decisionNumber+" in "+d.decision.ruleName+
" lookahead "+d.k +" max token "+lastRealTokenTouchedInDecision);
decisionEvents.add(d); // done with decision; track all
}
public void consumeToken(Token token) {
if (dump) System.out.println("consume token "+token);
if ( !inDecision() ) {
stats.numTokens++;
return;
}
if ( lastRealTokenTouchedInDecision==null ||
lastRealTokenTouchedInDecision.getTokenIndex() < token.getTokenIndex() )
{
lastRealTokenTouchedInDecision = token;
}
DecisionEvent d = currentDecision();
// compute lookahead depth
int thisRefIndex = token.getTokenIndex();
int numHidden = getNumberOfHiddenTokens(d.startIndex, thisRefIndex);
int depth = thisRefIndex - d.startIndex - numHidden + 1; // +1 counts consuming start token as 1
//d.maxk = Math.max(d.maxk, depth);
if (dump) System.out.println("consume "+thisRefIndex+" "+depth+" tokens ahead in "+
d.decision.ruleName+"-"+d.decision.decision+" start index "+d.startIndex);
}
/** The parser is in a decision if the decision depth > 0. This
* works for backtracking also, which can have nested decisions.
*/
public boolean inDecision() {
return decisionStack.size()>0;
}
public void consumeHiddenToken(Token token) {
//System.out.println("consume hidden token "+token);
if ( !inDecision() ) stats.numHiddenTokens++;
}
/** Track refs to lookahead if in a fixed/nonfixed decision.
*/
public void LT(int i, Token t) {
if ( inDecision() && i>0 ) {
DecisionEvent d = currentDecision();
if (dump) System.out.println("LT("+i+")="+t+" index "+t.getTokenIndex()+" relative to "+d.decision.ruleName+"-"+
d.decision.decision+" start index "+d.startIndex);
if ( lastRealTokenTouchedInDecision==null ||
lastRealTokenTouchedInDecision.getTokenIndex() < t.getTokenIndex() )
{
lastRealTokenTouchedInDecision = t;
if (dump) System.out.println("set last token "+lastRealTokenTouchedInDecision);
}
// get starting index off stack
// int stackTop = lookaheadStack.size()-1;
// Integer startingIndex = (Integer)lookaheadStack.get(stackTop);
// // compute lookahead depth
// int thisRefIndex = parser.getTokenStream().index();
// int numHidden =
// getNumberOfHiddenTokens(startingIndex.intValue(), thisRefIndex);
// int depth = i + thisRefIndex - startingIndex.intValue() - numHidden;
// /*
// System.out.println("LT("+i+") @ index "+thisRefIndex+" is depth "+depth+
// " max is "+maxLookaheadInCurrentDecision);
// */
// if ( depth>maxLookaheadInCurrentDecision ) {
// maxLookaheadInCurrentDecision = depth;
// }
// d.maxk = currentDecision()/
}
}
/** Track backtracking decisions. You'll see a fixed or cyclic decision
* and then a backtrack.
*
* enter rule
* ...
* enter decision
* LA and possibly consumes (for cyclic DFAs)
* begin backtrack level
* mark m
* rewind m
* end backtrack level, success
* exit decision
* ...
* exit rule
*/
public void beginBacktrack(int level) {
if (dump) System.out.println("enter backtrack "+level);
backtrackDepth++;
DecisionEvent e = currentDecision();
if ( e.decision.couldBacktrack ) {
stats.numBacktrackOccurrences++;
e.decision.numBacktrackOccurrences++;
e.backtracks = true;
}
}
/** Successful or not, track how much lookahead synpreds use */
public void endBacktrack(int level, boolean successful) {
if (dump) System.out.println("exit backtrack "+level+": "+successful);
backtrackDepth--;
}
@Override
public void seek(int i) {
if (dump) System.out.println("seek "+i);
}
protected DecisionEvent currentDecision() {
return decisionStack.peek();
}
public void recognitionException(RecognitionException e) {
stats.numReportedErrors++;
}
public void semanticPredicate(boolean result, String predicate) {
stats.numSemanticPredicates++;
if ( inDecision() ) {
DecisionEvent d = currentDecision();
d.evalSemPred = true;
d.decision.numSemPredEvals++;
if (dump) System.out.println("eval "+predicate+" in "+d.decision.ruleName+"-"+
d.decision.decision);
}
}
public void terminate() {
for (DecisionEvent e : decisionEvents) {
//System.out.println("decision "+e.decision.decision+": k="+e.k);
e.decision.avgk += e.k;
stats.avgkPerDecisionEvent += e.k;
if ( e.backtracks ) { // doesn't count gated syn preds on DFA edges
stats.avgkPerBacktrackingDecisionEvent += e.k;
}
}
stats.averageDecisionPercentBacktracks = 0.0f;
for (DecisionDescriptor d : decisions.values()) {
stats.numDecisionsCovered++;
d.avgk /= (double)d.n;
if ( d.couldBacktrack ) {
stats.numDecisionsThatPotentiallyBacktrack++;
float percentBacktracks = d.numBacktrackOccurrences / (float)d.n;
//System.out.println("dec "+d.decision+" backtracks "+percentBacktracks*100+"%");
stats.averageDecisionPercentBacktracks += percentBacktracks;
}
// ignore rules that backtrack along gated DFA edges
if ( d.numBacktrackOccurrences > 0 ) {
stats.numDecisionsThatDoBacktrack++;
}
}
stats.averageDecisionPercentBacktracks /= stats.numDecisionsThatPotentiallyBacktrack;
stats.averageDecisionPercentBacktracks *= 100; // it's a percentage
stats.avgkPerDecisionEvent /= stats.numDecisionEvents;
stats.avgkPerBacktrackingDecisionEvent /= (double)stats.numBacktrackOccurrences;
System.err.println(toString());
System.err.println(getDecisionStatsDump());
// String stats = toNotifyString();
// try {
// Stats.writeReport(RUNTIME_STATS_FILENAME,stats);
// }
// catch (IOException ioe) {
// System.err.println(ioe);
// ioe.printStackTrace(System.err);
// }
}
public void setParser(DebugParser parser) {
this.parser = parser;
}
// R E P O R T I N G
public String toNotifyString() {
StringBuffer buf = new StringBuffer();
buf.append(Version);
buf.append('\t');
buf.append(parser.getClass().getName());
// buf.append('\t');
// buf.append(numRuleInvocations);
// buf.append('\t');
// buf.append(maxRuleInvocationDepth);
// buf.append('\t');
// buf.append(numFixedDecisions);
// buf.append('\t');
// buf.append(Stats.min(decisionMaxFixedLookaheads));
// buf.append('\t');
// buf.append(Stats.max(decisionMaxFixedLookaheads));
// buf.append('\t');
// buf.append(Stats.avg(decisionMaxFixedLookaheads));
// buf.append('\t');
// buf.append(Stats.stddev(decisionMaxFixedLookaheads));
// buf.append('\t');
// buf.append(numCyclicDecisions);
// buf.append('\t');
// buf.append(Stats.min(decisionMaxCyclicLookaheads));
// buf.append('\t');
// buf.append(Stats.max(decisionMaxCyclicLookaheads));
// buf.append('\t');
// buf.append(Stats.avg(decisionMaxCyclicLookaheads));
// buf.append('\t');
// buf.append(Stats.stddev(decisionMaxCyclicLookaheads));
// buf.append('\t');
// buf.append(numBacktrackDecisions);
// buf.append('\t');
// buf.append(Stats.min(toArray(decisionMaxSynPredLookaheads)));
// buf.append('\t');
// buf.append(Stats.max(toArray(decisionMaxSynPredLookaheads)));
// buf.append('\t');
// buf.append(Stats.avg(toArray(decisionMaxSynPredLookaheads)));
// buf.append('\t');
// buf.append(Stats.stddev(toArray(decisionMaxSynPredLookaheads)));
// buf.append('\t');
// buf.append(numSemanticPredicates);
// buf.append('\t');
// buf.append(parser.getTokenStream().size());
// buf.append('\t');
// buf.append(numHiddenTokens);
// buf.append('\t');
// buf.append(numCharsMatched);
// buf.append('\t');
// buf.append(numHiddenCharsMatched);
// buf.append('\t');
// buf.append(numberReportedErrors);
// buf.append('\t');
// buf.append(numMemoizationCacheHits);
// buf.append('\t');
// buf.append(numMemoizationCacheMisses);
// buf.append('\t');
// buf.append(numGuessingRuleInvocations);
// buf.append('\t');
// buf.append(numMemoizationCacheEntries);
return buf.toString();
}
public String toString() {
return toString(getReport());
}
public ProfileStats getReport() {
// TokenStream input = parser.getTokenStream();
// for (int i=0; i<input.size()&& lastRealTokenTouchedInDecision !=null&&i<= lastRealTokenTouchedInDecision.getTokenIndex(); i++) {
// Token t = input.get(i);
// if ( t.getChannel()!=Token.DEFAULT_CHANNEL ) {
// stats.numHiddenTokens++;
// stats.numHiddenCharsMatched += t.getText().length();
// }
// }
stats.Version = Version;
stats.name = parser.getClass().getName();
stats.numUniqueRulesInvoked = uniqueRules.size();
//stats.numCharsMatched = lastTokenConsumed.getStopIndex() + 1;
return stats;
}
public DoubleKeyMap getDecisionStats() {
return decisions;
}
public List getDecisionEvents() {
return decisionEvents;
}
public static String toString(ProfileStats stats) {
StringBuffer buf = new StringBuffer();
buf.append("ANTLR Runtime Report; Profile Version ");
buf.append(stats.Version);
buf.append(newline);
buf.append("parser name ");
buf.append(stats.name);
buf.append(newline);
buf.append("Number of rule invocations ");
buf.append(stats.numRuleInvocations);
buf.append(newline);
buf.append("Number of unique rules visited ");
buf.append(stats.numUniqueRulesInvoked);
buf.append(newline);
buf.append("Number of decision events ");
buf.append(stats.numDecisionEvents);
buf.append(newline);
buf.append("Overall average k per decision event ");
buf.append(stats.avgkPerDecisionEvent);
buf.append(newline);
buf.append("Number of backtracking occurrences (can be multiple per decision) ");
buf.append(stats.numBacktrackOccurrences);
buf.append(newline);
buf.append("Overall average k per decision event that backtracks ");
buf.append(stats.avgkPerBacktrackingDecisionEvent);
buf.append(newline);
buf.append("Number of rule invocations while backtracking ");
buf.append(stats.numGuessingRuleInvocations);
buf.append(newline);
buf.append("num decisions that potentially backtrack ");
buf.append(stats.numDecisionsThatPotentiallyBacktrack);
buf.append(newline);
buf.append("num decisions that do backtrack ");
buf.append(stats.numDecisionsThatDoBacktrack);
buf.append(newline);
buf.append("num decisions that potentially backtrack but don't ");
buf.append(stats.numDecisionsThatPotentiallyBacktrack - stats.numDecisionsThatDoBacktrack);
buf.append(newline);
buf.append("average % of time a potentially backtracking decision backtracks ");
buf.append(stats.averageDecisionPercentBacktracks);
buf.append(newline);
buf.append("num unique decisions covered ");
buf.append(stats.numDecisionsCovered);
buf.append(newline);
buf.append("max rule invocation nesting depth ");
buf.append(stats.maxRuleInvocationDepth);
buf.append(newline);
// buf.append("number of fixed lookahead decisions ");
// buf.append();
// buf.append('\n');
// buf.append("min lookahead used in a fixed lookahead decision ");
// buf.append();
// buf.append('\n');
// buf.append("max lookahead used in a fixed lookahead decision ");
// buf.append();
// buf.append('\n');
// buf.append("average lookahead depth used in fixed lookahead decisions ");
// buf.append();
// buf.append('\n');
// buf.append("standard deviation of depth used in fixed lookahead decisions ");
// buf.append();
// buf.append('\n');
// buf.append("number of arbitrary lookahead decisions ");
// buf.append();
// buf.append('\n');
// buf.append("min lookahead used in an arbitrary lookahead decision ");
// buf.append();
// buf.append('\n');
// buf.append("max lookahead used in an arbitrary lookahead decision ");
// buf.append();
// buf.append('\n');
// buf.append("average lookahead depth used in arbitrary lookahead decisions ");
// buf.append();
// buf.append('\n');
// buf.append("standard deviation of depth used in arbitrary lookahead decisions ");
// buf.append();
// buf.append('\n');
// buf.append("number of evaluated syntactic predicates ");
// buf.append();
// buf.append('\n');
// buf.append("min lookahead used in a syntactic predicate ");
// buf.append();
// buf.append('\n');
// buf.append("max lookahead used in a syntactic predicate ");
// buf.append();
// buf.append('\n');
// buf.append("average lookahead depth used in syntactic predicates ");
// buf.append();
// buf.append('\n');
// buf.append("standard deviation of depth used in syntactic predicates ");
// buf.append();
// buf.append('\n');
buf.append("rule memoization cache size ");
buf.append(stats.numMemoizationCacheEntries);
buf.append(newline);
buf.append("number of rule memoization cache hits ");
buf.append(stats.numMemoizationCacheHits);
buf.append(newline);
buf.append("number of rule memoization cache misses ");
buf.append(stats.numMemoizationCacheMisses);
buf.append(newline);
// buf.append("number of evaluated semantic predicates ");
// buf.append();
// buf.append(newline);
buf.append("number of tokens ");
buf.append(stats.numTokens);
buf.append(newline);
buf.append("number of hidden tokens ");
buf.append(stats.numHiddenTokens);
buf.append(newline);
buf.append("number of char ");
buf.append(stats.numCharsMatched);
buf.append(newline);
buf.append("number of hidden char ");
buf.append(stats.numHiddenCharsMatched);
buf.append(newline);
buf.append("number of syntax errors ");
buf.append(stats.numReportedErrors);
buf.append(newline);
return buf.toString();
}
public String getDecisionStatsDump() {
StringBuffer buf = new StringBuffer();
buf.append("location");
buf.append(DATA_SEP);
buf.append("n");
buf.append(DATA_SEP);
buf.append("avgk");
buf.append(DATA_SEP);
buf.append("maxk");
buf.append(DATA_SEP);
buf.append("synpred");
buf.append(DATA_SEP);
buf.append("sempred");
buf.append(DATA_SEP);
buf.append("canbacktrack");
buf.append("\n");
for (String fileName : decisions.keySet()) {
for (int d : decisions.keySet(fileName)) {
DecisionDescriptor s = decisions.get(fileName, d);
buf.append(s.decision);
buf.append("@");
buf.append(locationDescription(s.fileName,s.ruleName,s.line,s.pos)); // decision number
buf.append(DATA_SEP);
buf.append(s.n);
buf.append(DATA_SEP);
buf.append(String.format("%.2f",s.avgk));
buf.append(DATA_SEP);
buf.append(s.maxk);
buf.append(DATA_SEP);
buf.append(s.numBacktrackOccurrences);
buf.append(DATA_SEP);
buf.append(s.numSemPredEvals);
buf.append(DATA_SEP);
buf.append(s.couldBacktrack ?"1":"0");
buf.append(newline);
}
}
return buf.toString();
}
protected int[] trim(int[] X, int n) {
if ( n<X.length ) {
int[] trimmed = new int[n];
System.arraycopy(X,0,trimmed,0,n);
X = trimmed;
}
return X;
}
protected int[] toArray(List a) {
int[] x = new int[a.size()];
for (int i = 0; i < a.size(); i++) {
Integer I = (Integer) a.get(i);
x[i] = I.intValue();
}
return x;
}
/** Get num hidden tokens between i..j inclusive */
public int getNumberOfHiddenTokens(int i, int j) {
int n = 0;
TokenStream input = parser.getTokenStream();
for (int ti = i; ti<input.size() && ti <= j; ti++) {
Token t = input.get(ti);
if ( t.getChannel()!=Token.DEFAULT_CHANNEL ) {
n++;
}
}
return n;
}
protected String locationDescription() {
return locationDescription(
currentGrammarFileName.peek(),
currentRuleName.peek(),
currentLine.peek(),
currentPos.peek());
}
protected String locationDescription(String file, String rule, int line, int pos) {
return file+":"+line+":"+pos+"(" + rule + ")";
}
}

View File

@ -0,0 +1,508 @@
/*
[The "BSD license"]
Copyright (c) 2005-2009 Terence Parr
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.antlr.v4.runtime.debug;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.RecognitionException;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.BaseTree;
import org.antlr.v4.runtime.tree.Tree;
import java.io.*;
import java.net.ConnectException;
import java.net.Socket;
import java.util.StringTokenizer;
public class RemoteDebugEventSocketListener implements Runnable {
static final int MAX_EVENT_ELEMENTS = 8;
DebugEventListener listener;
String machine;
int port;
Socket channel = null;
PrintWriter out;
BufferedReader in;
String event;
/** Version of ANTLR (dictates events) */
public String version;
public String grammarFileName;
/** Track the last token index we saw during a consume. If same, then
* set a flag that we have a problem.
*/
int previousTokenIndex = -1;
boolean tokenIndexesInvalid = false;
public static class ProxyToken implements Token {
int index;
int type;
int channel;
int line;
int charPos;
String text;
public ProxyToken(int index) { this.index = index; }
public ProxyToken(int index, int type, int channel,
int line, int charPos, String text)
{
this.index = index;
this.type = type;
this.channel = channel;
this.line = line;
this.charPos = charPos;
this.text = text;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public int getType() {
return type;
}
public void setType(int ttype) {
this.type = ttype;
}
public int getLine() {
return line;
}
public void setLine(int line) {
this.line = line;
}
public int getCharPositionInLine() {
return charPos;
}
public void setCharPositionInLine(int pos) {
this.charPos = pos;
}
public int getChannel() {
return channel;
}
public void setChannel(int channel) {
this.channel = channel;
}
public int getTokenIndex() {
return index;
}
public void setTokenIndex(int index) {
this.index = index;
}
public CharStream getInputStream() {
return null;
}
public void setInputStream(CharStream input) {
}
public String toString() {
String channelStr = "";
if ( channel!= Token.DEFAULT_CHANNEL ) {
channelStr=",channel="+channel;
}
return "["+getText()+"/<"+type+">"+channelStr+","+line+":"+getCharPositionInLine()+",@"+index+"]";
}
}
public static class ProxyTree extends BaseTree {
public int ID;
public int type;
public int line = 0;
public int charPos = -1;
public int tokenIndex = -1;
public String text;
public ProxyTree(int ID, int type, int line, int charPos, int tokenIndex, String text) {
this.ID = ID;
this.type = type;
this.line = line;
this.charPos = charPos;
this.tokenIndex = tokenIndex;
this.text = text;
}
public ProxyTree(int ID) { this.ID = ID; }
public int getTokenStartIndex() { return tokenIndex; }
public void setTokenStartIndex(int index) { }
public int getTokenStopIndex() { return 0; }
public void setTokenStopIndex(int index) { }
public Tree dupNode() { return null; }
public int getType() { return type; }
public String getText() { return text; }
public String toString() {
return "fix this";
}
}
public RemoteDebugEventSocketListener(DebugEventListener listener,
String machine,
int port) throws IOException
{
this.listener = listener;
this.machine = machine;
this.port = port;
if( !openConnection() ) {
throw new ConnectException();
}
}
protected void eventHandler() {
try {
handshake();
event = in.readLine();
while ( event!=null ) {
dispatch(event);
ack();
event = in.readLine();
}
}
catch (Exception e) {
System.err.println(e);
e.printStackTrace(System.err);
}
finally {
closeConnection();
}
}
protected boolean openConnection() {
boolean success = false;
try {
channel = new Socket(machine, port);
channel.setTcpNoDelay(true);
OutputStream os = channel.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(os, "UTF8");
out = new PrintWriter(new BufferedWriter(osw));
InputStream is = channel.getInputStream();
InputStreamReader isr = new InputStreamReader(is, "UTF8");
in = new BufferedReader(isr);
success = true;
} catch(Exception e) {
System.err.println(e);
}
return success;
}
protected void closeConnection() {
try {
in.close(); in = null;
out.close(); out = null;
channel.close(); channel=null;
}
catch (Exception e) {
System.err.println(e);
e.printStackTrace(System.err);
}
finally {
if ( in!=null ) {
try {in.close();} catch (IOException ioe) {
System.err.println(ioe);
}
}
if ( out!=null ) {
out.close();
}
if ( channel!=null ) {
try {channel.close();} catch (IOException ioe) {
System.err.println(ioe);
}
}
}
}
protected void handshake() throws IOException {
String antlrLine = in.readLine();
String[] antlrElements = getEventElements(antlrLine);
version = antlrElements[1];
String grammarLine = in.readLine();
String[] grammarElements = getEventElements(grammarLine);
grammarFileName = grammarElements[1];
ack();
listener.commence(); // inform listener after handshake
}
protected void ack() {
out.println("ack");
out.flush();
}
protected void dispatch(String line) {
//System.out.println("event: "+line);
String[] elements = getEventElements(line);
if ( elements==null || elements[0]==null ) {
System.err.println("unknown debug event: "+line);
return;
}
if ( elements[0].equals("enterRule") ) {
listener.enterRule(elements[1], elements[2]);
}
else if ( elements[0].equals("exitRule") ) {
listener.exitRule(elements[1], elements[2]);
}
else if ( elements[0].equals("enterAlt") ) {
listener.enterAlt(Integer.parseInt(elements[1]));
}
else if ( elements[0].equals("enterSubRule") ) {
listener.enterSubRule(Integer.parseInt(elements[1]));
}
else if ( elements[0].equals("exitSubRule") ) {
listener.exitSubRule(Integer.parseInt(elements[1]));
}
else if ( elements[0].equals("enterDecision") ) {
listener.enterDecision(Integer.parseInt(elements[1]), elements[2].equals("true"));
}
else if ( elements[0].equals("exitDecision") ) {
listener.exitDecision(Integer.parseInt(elements[1]));
}
else if ( elements[0].equals("location") ) {
listener.location(Integer.parseInt(elements[1]),
Integer.parseInt(elements[2]));
}
else if ( elements[0].equals("consumeToken") ) {
ProxyToken t = deserializeToken(elements, 1);
if ( t.getTokenIndex() == previousTokenIndex ) {
tokenIndexesInvalid = true;
}
previousTokenIndex = t.getTokenIndex();
listener.consumeToken(t);
}
else if ( elements[0].equals("consumeHiddenToken") ) {
ProxyToken t = deserializeToken(elements, 1);
if ( t.getTokenIndex() == previousTokenIndex ) {
tokenIndexesInvalid = true;
}
previousTokenIndex = t.getTokenIndex();
listener.consumeHiddenToken(t);
}
else if ( elements[0].equals("LT") ) {
Token t = deserializeToken(elements, 2);
listener.LT(Integer.parseInt(elements[1]), t);
}
else if ( elements[0].equals("exception") ) {
String excName = elements[1];
String indexS = elements[2];
String lineS = elements[3];
String posS = elements[4];
Class excClass = null;
try {
excClass = Class.forName(excName);
RecognitionException e =
(RecognitionException)excClass.newInstance();
e.index = Integer.parseInt(indexS);
e.line = Integer.parseInt(lineS);
e.charPositionInLine = Integer.parseInt(posS);
listener.recognitionException(e);
}
catch (ClassNotFoundException cnfe) {
System.err.println("can't find class "+cnfe);
cnfe.printStackTrace(System.err);
}
catch (InstantiationException ie) {
System.err.println("can't instantiate class "+ie);
ie.printStackTrace(System.err);
}
catch (IllegalAccessException iae) {
System.err.println("can't access class "+iae);
iae.printStackTrace(System.err);
}
}
else if ( elements[0].equals("beginResync") ) {
listener.beginResync();
}
else if ( elements[0].equals("endResync") ) {
listener.endResync();
}
else if ( elements[0].equals("terminate") ) {
listener.terminate();
}
else if ( elements[0].equals("semanticPredicate") ) {
Boolean result = Boolean.valueOf(elements[1]);
String predicateText = elements[2];
predicateText = unEscapeNewlines(predicateText);
listener.semanticPredicate(result.booleanValue(),
predicateText);
}
else if ( elements[0].equals("consumeNode") ) {
ProxyTree node = deserializeNode(elements, 1);
listener.consumeNode(node);
}
else if ( elements[0].equals("LN") ) {
int i = Integer.parseInt(elements[1]);
ProxyTree node = deserializeNode(elements, 2);
listener.LT(i, node);
}
else if ( elements[0].equals("createNodeFromTokenElements") ) {
int ID = Integer.parseInt(elements[1]);
int type = Integer.parseInt(elements[2]);
String text = elements[3];
text = unEscapeNewlines(text);
ProxyTree node = new ProxyTree(ID, type, -1, -1, -1, text);
listener.createNode(node);
}
else if ( elements[0].equals("createNode") ) {
int ID = Integer.parseInt(elements[1]);
int tokenIndex = Integer.parseInt(elements[2]);
// create dummy node/token filled with ID, tokenIndex
ProxyTree node = new ProxyTree(ID);
ProxyToken token = new ProxyToken(tokenIndex);
listener.createNode(node, token);
}
else if ( elements[0].equals("nilNode") ) {
int ID = Integer.parseInt(elements[1]);
ProxyTree node = new ProxyTree(ID);
listener.nilNode(node);
}
else if ( elements[0].equals("errorNode") ) {
// TODO: do we need a special tree here?
int ID = Integer.parseInt(elements[1]);
int type = Integer.parseInt(elements[2]);
String text = elements[3];
text = unEscapeNewlines(text);
ProxyTree node = new ProxyTree(ID, type, -1, -1, -1, text);
listener.errorNode(node);
}
else if ( elements[0].equals("becomeRoot") ) {
int newRootID = Integer.parseInt(elements[1]);
int oldRootID = Integer.parseInt(elements[2]);
ProxyTree newRoot = new ProxyTree(newRootID);
ProxyTree oldRoot = new ProxyTree(oldRootID);
listener.becomeRoot(newRoot, oldRoot);
}
else if ( elements[0].equals("addChild") ) {
int rootID = Integer.parseInt(elements[1]);
int childID = Integer.parseInt(elements[2]);
ProxyTree root = new ProxyTree(rootID);
ProxyTree child = new ProxyTree(childID);
listener.addChild(root, child);
}
else if ( elements[0].equals("setTokenBoundaries") ) {
int ID = Integer.parseInt(elements[1]);
ProxyTree node = new ProxyTree(ID);
listener.setTokenBoundaries(
node,
Integer.parseInt(elements[2]),
Integer.parseInt(elements[3]));
}
else {
System.err.println("unknown debug event: "+line);
}
}
protected ProxyTree deserializeNode(String[] elements, int offset) {
int ID = Integer.parseInt(elements[offset+0]);
int type = Integer.parseInt(elements[offset+1]);
int tokenLine = Integer.parseInt(elements[offset+2]);
int charPositionInLine = Integer.parseInt(elements[offset+3]);
int tokenIndex = Integer.parseInt(elements[offset+4]);
String text = elements[offset+5];
text = unEscapeNewlines(text);
return new ProxyTree(ID, type, tokenLine, charPositionInLine, tokenIndex, text);
}
protected ProxyToken deserializeToken(String[] elements,
int offset)
{
String indexS = elements[offset+0];
String typeS = elements[offset+1];
String channelS = elements[offset+2];
String lineS = elements[offset+3];
String posS = elements[offset+4];
String text = elements[offset+5];
text = unEscapeNewlines(text);
int index = Integer.parseInt(indexS);
ProxyToken t =
new ProxyToken(index,
Integer.parseInt(typeS),
Integer.parseInt(channelS),
Integer.parseInt(lineS),
Integer.parseInt(posS),
text);
return t;
}
/** Create a thread to listen to the remote running recognizer */
public void start() {
Thread t = new Thread(this);
t.start();
}
public void run() {
eventHandler();
}
// M i s c
public String[] getEventElements(String event) {
if ( event==null ) {
return null;
}
String[] elements = new String[MAX_EVENT_ELEMENTS];
String str = null; // a string element if present (must be last)
try {
int firstQuoteIndex = event.indexOf('"');
if ( firstQuoteIndex>=0 ) {
// treat specially; has a string argument like "a comment\n
// Note that the string is terminated by \n not end quote.
// Easier to parse that way.
String eventWithoutString = event.substring(0,firstQuoteIndex);
str = event.substring(firstQuoteIndex+1,event.length());
event = eventWithoutString;
}
StringTokenizer st = new StringTokenizer(event, "\t", false);
int i = 0;
while ( st.hasMoreTokens() ) {
if ( i>=MAX_EVENT_ELEMENTS ) {
// ErrorManager.internalError("event has more than "+MAX_EVENT_ELEMENTS+" args: "+event);
return elements;
}
elements[i] = st.nextToken();
i++;
}
if ( str!=null ) {
elements[i] = str;
}
}
catch (Exception e) {
e.printStackTrace(System.err);
}
return elements;
}
protected String unEscapeNewlines(String txt) {
// this unescape is slow but easy to understand
txt = txt.replaceAll("%0A","\n"); // unescape \n
txt = txt.replaceAll("%0D","\r"); // unescape \r
txt = txt.replaceAll("%25","%"); // undo escaped escape chars
return txt;
}
public boolean tokenIndexesAreInvalid() {
return false;
//return tokenIndexesInvalid;
}
}

View File

@ -0,0 +1,96 @@
/*
[The "BSD license"]
Copyright (c) 2005-2009 Terence Parr
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.antlr.v4.runtime.debug;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.TreeAdaptor;
/** Print out (most of) the events... Useful for debugging, testing... */
public class TraceDebugEventListener extends BlankDebugEventListener {
TreeAdaptor adaptor;
public TraceDebugEventListener(TreeAdaptor adaptor) {
this.adaptor = adaptor;
}
public void enterRule(String ruleName) { System.out.println("enterRule "+ruleName); }
public void exitRule(String ruleName) { System.out.println("exitRule "+ruleName); }
public void enterSubRule(int decisionNumber) { System.out.println("enterSubRule"); }
public void exitSubRule(int decisionNumber) { System.out.println("exitSubRule"); }
public void location(int line, int pos) {System.out.println("location "+line+":"+pos);}
// Tree parsing stuff
public void consumeNode(Object t) {
int ID = adaptor.getUniqueID(t);
String text = adaptor.getText(t);
int type = adaptor.getType(t);
System.out.println("consumeNode "+ID+" "+text+" "+type);
}
public void LT(int i, Object t) {
int ID = adaptor.getUniqueID(t);
String text = adaptor.getText(t);
int type = adaptor.getType(t);
System.out.println("LT "+i+" "+ID+" "+text+" "+type);
}
// AST stuff
public void nilNode(Object t) {System.out.println("nilNode "+adaptor.getUniqueID(t));}
public void createNode(Object t) {
int ID = adaptor.getUniqueID(t);
String text = adaptor.getText(t);
int type = adaptor.getType(t);
System.out.println("create "+ID+": "+text+", "+type);
}
public void createNode(Object node, Token token) {
int ID = adaptor.getUniqueID(node);
String text = adaptor.getText(node);
int tokenIndex = token.getTokenIndex();
System.out.println("create "+ID+": "+tokenIndex);
}
public void becomeRoot(Object newRoot, Object oldRoot) {
System.out.println("becomeRoot "+adaptor.getUniqueID(newRoot)+", "+
adaptor.getUniqueID(oldRoot));
}
public void addChild(Object root, Object child) {
System.out.println("addChild "+adaptor.getUniqueID(root)+", "+
adaptor.getUniqueID(child));
}
public void setTokenBoundaries(Object t, int tokenStartIndex, int tokenStopIndex) {
System.out.println("setTokenBoundaries "+adaptor.getUniqueID(t)+", "+
tokenStartIndex+", "+tokenStopIndex);
}
}

View File

@ -0,0 +1,65 @@
/*
[The "BSD license"]
Copyright (c) 2005-2009 Terence Parr
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.antlr.v4.runtime.debug;
import org.antlr.v4.runtime.IntStream;
import org.antlr.v4.runtime.TokenStream;
/** The default tracer mimics the traceParser behavior of ANTLR 2.x.
* This listens for debugging events from the parser and implies
* that you cannot debug and trace at the same time.
*/
public class Tracer extends BlankDebugEventListener {
public IntStream input;
protected int level = 0;
public Tracer(IntStream input) {
this.input = input;
}
public void enterRule(String ruleName) {
for (int i=1; i<=level; i++) {System.out.print(" ");}
System.out.println("> "+ruleName+" lookahead(1)="+getInputSymbol(1));
level++;
}
public void exitRule(String ruleName) {
level--;
for (int i=1; i<=level; i++) {System.out.print(" ");}
System.out.println("< "+ruleName+" lookahead(1)="+getInputSymbol(1));
}
public Object getInputSymbol(int k) {
if ( input instanceof TokenStream) {
return ((TokenStream)input).LT(k);
}
return new Character((char)input.LA(k));
}
}

View File

@ -29,7 +29,8 @@
package org.antlr.v4.runtime.tree;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.RecognitionException;
import org.antlr.v4.runtime.TokenStream;
/**
Cut-n-paste from material I'm not using in the book anymore (edit later
@ -84,9 +85,6 @@ public class TreeFilter extends TreeParser {
protected TreeAdaptor originalAdaptor;
public TreeFilter(TreeNodeStream input) {
this(input, new RecognizerSharedState());
}
public TreeFilter(TreeNodeStream input, RecognizerSharedState state) {
super(input);
originalAdaptor = (TreeAdaptor) input.getTreeAdaptor();
originalTokenStream = input.getTokenStream();

View File

@ -31,7 +31,8 @@ package org.antlr.v4.runtime.tree;
import org.antlr.v4.runtime.*;
import java.util.regex.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** A parser for a stream of tree nodes. "tree grammars" result in a subclass
* of this. All the error reporting and recovery is shared with Parser via
@ -54,11 +55,6 @@ public class TreeParser extends BaseRecognizer {
setTreeNodeStream(input);
}
public TreeParser(TreeNodeStream input, RecognizerSharedState<?> state) {
super((TokenStream)input); // share the state object with another parser
setTreeNodeStream(input);
}
public void reset() {
super.reset(); // reset all recognizer state variables
if ( input!=null ) {