forked from jasder/antlr
Parse error systemTweaks, removing the debug API since we won't use it
[git-p4: depot-paths = "//depot/code/antlr4/main/": change = 9090]
This commit is contained in:
parent
9894221249
commit
298a15e017
|
@ -31,9 +31,47 @@ package org.antlr.v4.runtime;
|
|||
|
||||
import com.sun.istack.internal.Nullable;
|
||||
|
||||
/** */
|
||||
/** How to emit recognition errors */
|
||||
public interface ANTLRParserListener {
|
||||
/** Upon syntax error, notify any interested parties. This is not how to
|
||||
* recover from errors or compute error messages. The parser
|
||||
* ANTLRErrorStrategy specifies how to recover from syntax errors
|
||||
* and how to compute error messages. This listener's job is simply to
|
||||
* emit a computed message, though it has enough information to
|
||||
* create its own message in many cases.
|
||||
*
|
||||
* The RecognitionException is non-null for all syntax errors except
|
||||
* when we discover mismatched token errors that we can recover from
|
||||
* in-line, without returning from the surrounding rule (via the
|
||||
* single token insertion and deletion mechanism).
|
||||
*
|
||||
* @param recognizer
|
||||
* What parser got the error. From this object, you
|
||||
* can access the context as well as the input stream.
|
||||
* @param startTokenIndex
|
||||
* The offending token index in the input token stream.
|
||||
* If no viable alternative error, it's the token index
|
||||
* at which we started production for the decision.
|
||||
* @param stopTokenIndex
|
||||
* Normally a copy of the offending token index unless we
|
||||
* found a no viable alternative error. In that case,
|
||||
* this is the offending token index.
|
||||
* @param line
|
||||
* At what line in input to the error occur? This always refers to
|
||||
* stopTokenIndex
|
||||
* @param charPositionInLine
|
||||
* At what character position within that line did the error occur.
|
||||
* @param msg
|
||||
* The message to emit
|
||||
* @param e
|
||||
* The exception generated by the parser that led to
|
||||
* the reporting of an error. It is null in the case where
|
||||
* the parser was able to recover in line without exiting the
|
||||
* surrounding rule.
|
||||
*/
|
||||
public void error(BaseRecognizer recognizer,
|
||||
int startTokenIndex,
|
||||
int stopTokenIndex,
|
||||
int line,
|
||||
int charPositionInLine,
|
||||
String msg,
|
||||
|
|
|
@ -222,7 +222,7 @@ public abstract class BaseRecognizer extends Recognizer<ParserATNSimulator> {
|
|||
syntaxErrors++; // don't count spurious
|
||||
errorRecovery = true;
|
||||
|
||||
notifyListeners(e.line, e.charPositionInLine, e.getMessage(), e);
|
||||
notifyListeners(e.offendingToken, e.getMessage(), e);
|
||||
}
|
||||
|
||||
|
||||
|
@ -519,15 +519,23 @@ public abstract class BaseRecognizer extends Recognizer<ParserATNSimulator> {
|
|||
*/
|
||||
protected Object getCurrentInputSymbol() { return null; }
|
||||
|
||||
public void notifyListeners(int line, int charPositionInLine, String msg,
|
||||
public void notifyListeners(Token offendingToken, String msg,
|
||||
@Nullable RecognitionException e)
|
||||
{
|
||||
int line = offendingToken.getLine();
|
||||
int charPositionInLine = offendingToken.getCharPositionInLine();
|
||||
int start = getInputStream().index();
|
||||
int stop = start;
|
||||
if ( e instanceof NoViableAltException ) {
|
||||
start = ((NoViableAltException) e).startIndex;
|
||||
stop = e.offendingTokenIndex;
|
||||
}
|
||||
if ( _listeners==null || _listeners.size()==0 ) {
|
||||
emitErrorMessage("line "+line+":"+charPositionInLine+" "+msg);
|
||||
return;
|
||||
}
|
||||
for (ANTLRParserListener pl : _listeners) {
|
||||
pl.error(this, line, charPositionInLine, msg, e);
|
||||
pl.error(this, start, stop, line, charPositionInLine, msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -65,7 +65,7 @@ public class BufferedTokenStream implements TokenStream {
|
|||
*/
|
||||
protected int p = -1;
|
||||
|
||||
protected int range = -1; // how deep have we gone?
|
||||
// protected int range = -1; // how deep have we gone?
|
||||
|
||||
public BufferedTokenStream() {;}
|
||||
|
||||
|
@ -77,7 +77,7 @@ public class BufferedTokenStream implements TokenStream {
|
|||
|
||||
public int index() { return p; }
|
||||
|
||||
public int range() { return range; }
|
||||
// public int range() { return range; }
|
||||
|
||||
public int mark() {
|
||||
if ( p == -1 ) setup();
|
||||
|
@ -176,7 +176,7 @@ public class BufferedTokenStream implements TokenStream {
|
|||
// EOF must be last token
|
||||
return tokens.get(tokens.size()-1);
|
||||
}
|
||||
if ( i>range ) range = i;
|
||||
// if ( i>range ) range = i;
|
||||
return tokens.get(i);
|
||||
}
|
||||
|
||||
|
|
|
@ -100,7 +100,7 @@ public class CommonTokenStream extends BufferedTokenStream {
|
|||
i = skipOffTokenChannels(i+1);
|
||||
n++;
|
||||
}
|
||||
if ( i>range ) range = i;
|
||||
// if ( i>range ) range = i;
|
||||
return tokens.get(i);
|
||||
}
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ public class DefaultANTLRErrorStrategy implements ANTLRErrorStrategy {
|
|||
else {
|
||||
System.err.println("unknown recognition error type: "+e.getClass().getName());
|
||||
if ( recognizer!=null ) {
|
||||
recognizer.notifyListeners(e.line, e.charPositionInLine, e.getMessage(), e);
|
||||
recognizer.notifyListeners(e.offendingToken, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -84,8 +84,8 @@ public class DefaultANTLRErrorStrategy implements ANTLRErrorStrategy {
|
|||
if ( recognizer.errorRecovery ) return;
|
||||
trackError(recognizer);
|
||||
|
||||
String msg = "no viable alternative at input "+getTokenErrorDisplay(e.token);
|
||||
recognizer.notifyListeners(e.line, e.charPositionInLine, msg, e);
|
||||
String msg = "no viable alternative at input "+getTokenErrorDisplay(e.offendingToken);
|
||||
recognizer.notifyListeners(e.offendingToken, msg, e);
|
||||
}
|
||||
|
||||
public void reportInputMismatch(BaseRecognizer recognizer,
|
||||
|
@ -95,9 +95,9 @@ public class DefaultANTLRErrorStrategy implements ANTLRErrorStrategy {
|
|||
if ( recognizer.errorRecovery ) return;
|
||||
trackError(recognizer);
|
||||
|
||||
String msg = "mismatched input "+getTokenErrorDisplay(e.token)+
|
||||
String msg = "mismatched input "+getTokenErrorDisplay(e.offendingToken)+
|
||||
" expecting "+e.expecting.toString(recognizer.getTokenNames());
|
||||
recognizer.notifyListeners(e.line, e.charPositionInLine, msg, e);
|
||||
recognizer.notifyListeners(e.offendingToken, msg, e);
|
||||
}
|
||||
|
||||
public void reportFailedPredicate(BaseRecognizer recognizer,
|
||||
|
@ -110,7 +110,7 @@ public class DefaultANTLRErrorStrategy implements ANTLRErrorStrategy {
|
|||
String ruleName = recognizer.getRuleNames()[recognizer._ctx.getRuleIndex()];
|
||||
String msg = "rule "+ruleName+" failed predicate: {"+
|
||||
e.predicateText+"}?";
|
||||
recognizer.notifyListeners(e.line, e.charPositionInLine, msg, e);
|
||||
recognizer.notifyListeners(e.offendingToken, msg, e);
|
||||
}
|
||||
|
||||
public void reportUnwantedToken(BaseRecognizer recognizer) {
|
||||
|
@ -123,7 +123,7 @@ public class DefaultANTLRErrorStrategy implements ANTLRErrorStrategy {
|
|||
IntervalSet expecting = getExpectedTokens(recognizer);
|
||||
String msg = "extraneous input "+tokenName+" expecting "+
|
||||
expecting.toString(recognizer.getTokenNames());
|
||||
recognizer.notifyListeners(t.getLine(), t.getCharPositionInLine(), msg, null);
|
||||
recognizer.notifyListeners(t, msg, null);
|
||||
}
|
||||
|
||||
public void reportMissingToken(BaseRecognizer recognizer) {
|
||||
|
@ -135,7 +135,7 @@ public class DefaultANTLRErrorStrategy implements ANTLRErrorStrategy {
|
|||
String msg = "missing "+expecting.toString(recognizer.getTokenNames())+
|
||||
" at "+getTokenErrorDisplay(t);
|
||||
|
||||
recognizer.notifyListeners(t.getLine(), t.getCharPositionInLine(), msg, null);
|
||||
recognizer.notifyListeners(t, msg, null);
|
||||
}
|
||||
|
||||
/** Attempt to recover from a single missing or extra token.
|
||||
|
@ -318,7 +318,7 @@ public class DefaultANTLRErrorStrategy implements ANTLRErrorStrategy {
|
|||
// if ( recognizer.errorRecovery ) return;
|
||||
// trackError(recognizer);
|
||||
//
|
||||
// recognizer.notifyListeners(e.line, e.charPositionInLine, "dsfdkjasdf");
|
||||
// recognizer.notifyListeners(e.offendingToken, "dsfdkjasdf");
|
||||
// }
|
||||
|
||||
/* Compute the error recovery set for the current rule. During
|
||||
|
|
|
@ -46,11 +46,11 @@ public class MissingTokenException extends MismatchedTokenException {
|
|||
}
|
||||
|
||||
public String toString() {
|
||||
if ( inserted!=null && token!=null ) {
|
||||
return "MissingTokenException(inserted "+inserted+" at "+token.getText()+")";
|
||||
if ( inserted!=null && offendingToken !=null ) {
|
||||
return "MissingTokenException(inserted "+inserted+" at "+ offendingToken.getText()+")";
|
||||
}
|
||||
if ( token!=null ) {
|
||||
return "MissingTokenException(at "+token.getText()+")";
|
||||
if ( offendingToken !=null ) {
|
||||
return "MissingTokenException(at "+ offendingToken.getText()+")";
|
||||
}
|
||||
return "MissingTokenException";
|
||||
}
|
||||
|
|
|
@ -34,6 +34,12 @@ import org.antlr.v4.runtime.misc.OrderedHashSet;
|
|||
public class NoViableAltException extends RecognitionException {
|
||||
/** Prediction began at what input index? */
|
||||
public int startIndex;
|
||||
/** The token object at the start index; the input stream might
|
||||
* not be buffering tokens so get a reference to it. (At the
|
||||
* time the error occurred, of course the stream needs to keep a
|
||||
* buffer all of the tokens but later we might not have access to those.)
|
||||
*/
|
||||
public Token startToken;
|
||||
|
||||
/** Which configurations did we try at input.index() that couldn't match input.LT(1)? */
|
||||
public OrderedHashSet<ATNConfig> deadEndConfigs;
|
||||
|
@ -46,17 +52,20 @@ public class NoViableAltException extends RecognitionException {
|
|||
}
|
||||
|
||||
public NoViableAltException(BaseRecognizer recognizer, IntStream input,
|
||||
Token startToken,
|
||||
OrderedHashSet<ATNConfig> deadEndConfigs,
|
||||
RuleContext ctx)
|
||||
{
|
||||
super(recognizer, input, ctx);
|
||||
this.deadEndConfigs = deadEndConfigs;
|
||||
this.startToken = startToken;
|
||||
this.startIndex = startToken.getTokenIndex();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
if ( recognizer!=null ) {
|
||||
TokenStream tokens = ((Parser)recognizer).getTokenStream();
|
||||
String bad = tokens.toString(startIndex, index);
|
||||
String bad = tokens.toString(startIndex, offendingTokenIndex);
|
||||
return "NoViableAltException(input=\""+bad+"\" last token type is "+getUnexpectedType()+")";
|
||||
}
|
||||
return "NoViableAltException(last token type is "+getUnexpectedType()+")";
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
*/
|
||||
package org.antlr.v4.runtime;
|
||||
|
||||
import com.sun.org.apache.regexp.internal.RE;
|
||||
import org.antlr.v4.runtime.misc.IntervalSet;
|
||||
import org.antlr.v4.runtime.tree.*;
|
||||
|
||||
|
@ -74,30 +73,22 @@ public class RecognitionException extends RuntimeException {
|
|||
public IntStream input;
|
||||
|
||||
/** What is index of token/char were we looking at when the error occurred? */
|
||||
public int index;
|
||||
public int offendingTokenIndex;
|
||||
|
||||
/** The current Token when an error occurred. Since not all streams
|
||||
* can retrieve the ith Token, we have to track the Token object.
|
||||
* For parsers. Even when it's a tree parser, token might be set.
|
||||
*/
|
||||
public Token token;
|
||||
public Token offendingToken;
|
||||
|
||||
/** If this is a tree parser exception, node is set to the node with
|
||||
* the problem.
|
||||
*/
|
||||
public Object node;
|
||||
public Object offendingNode;
|
||||
|
||||
/** The current char when an error occurred. For lexers. */
|
||||
public int c;
|
||||
|
||||
/** Track the line at which the error occurred in case this is
|
||||
* generated from a lexer. We need to track this since the
|
||||
* unexpected char doesn't carry the line info.
|
||||
*/
|
||||
public int line;
|
||||
|
||||
public int charPositionInLine;
|
||||
|
||||
/** If you are parsing a tree node stream, you will encounter som
|
||||
* imaginary nodes w/o line/col info. We now search backwards looking
|
||||
* for most recent token with line/col info, but notify getErrorHeader()
|
||||
|
@ -125,11 +116,9 @@ public class RecognitionException extends RuntimeException {
|
|||
// this.expecting = viableTokensFollowingThisRule.or(firstSet);
|
||||
if ( recognizer!=null ) expecting = recognizer._interp.atn.nextTokens(ctx);
|
||||
|
||||
this.index = input.index();
|
||||
this.offendingTokenIndex = input.index();
|
||||
if ( input instanceof TokenStream ) {
|
||||
this.token = ((TokenStream)input).LT(1);
|
||||
this.line = token.getLine();
|
||||
this.charPositionInLine = token.getCharPositionInLine();
|
||||
this.offendingToken = ((TokenStream)input).LT(1);
|
||||
}
|
||||
else if ( input instanceof ASTNodeStream) {
|
||||
//extractInformationFromTreeNodeStream(input);
|
||||
|
@ -186,14 +175,14 @@ public class RecognitionException extends RuntimeException {
|
|||
|
||||
/** Return the token type or char of the unexpected input element */
|
||||
public int getUnexpectedType() {
|
||||
if ( recognizer==null ) return token.getType();
|
||||
if ( recognizer==null ) return offendingToken.getType();
|
||||
if ( recognizer.getInputStream() instanceof TokenStream) {
|
||||
return token.getType();
|
||||
return offendingToken.getType();
|
||||
}
|
||||
else if ( recognizer.getInputStream() instanceof ASTNodeStream) {
|
||||
ASTNodeStream nodes = (ASTNodeStream)recognizer.getInputStream();
|
||||
ASTAdaptor adaptor = nodes.getTreeAdaptor();
|
||||
return adaptor.getType(node);
|
||||
return adaptor.getType(offendingNode);
|
||||
}
|
||||
else {
|
||||
return c;
|
||||
|
|
|
@ -109,7 +109,7 @@ public class Recognizer<ATNInterpreter> {
|
|||
else {
|
||||
tokenName = tokenNames[mte.expecting.getSingleElement()];
|
||||
}
|
||||
msg = "missing "+tokenName+" at "+getTokenErrorDisplay(e.token);
|
||||
msg = "missing "+tokenName+" at "+getTokenErrorDisplay(e.offendingToken);
|
||||
}
|
||||
else if ( e instanceof MismatchedTokenException ) {
|
||||
MismatchedTokenException mte = (MismatchedTokenException)e;
|
||||
|
@ -120,7 +120,7 @@ public class Recognizer<ATNInterpreter> {
|
|||
// else {
|
||||
// tokenName = tokenNames[mte.expecting.getSingleElement()];
|
||||
// }
|
||||
msg = "mismatched input "+getTokenErrorDisplay(e.token)+
|
||||
msg = "mismatched input "+getTokenErrorDisplay(e.offendingToken)+
|
||||
" expecting "+tokenName;
|
||||
}
|
||||
else if ( e instanceof MismatchedASTNodeException) {
|
||||
|
@ -132,7 +132,7 @@ public class Recognizer<ATNInterpreter> {
|
|||
else {
|
||||
tokenName = tokenNames[mtne.expecting.getSingleElement()];
|
||||
}
|
||||
msg = "mismatched tree node: "+mtne.node+
|
||||
msg = "mismatched tree node: "+mtne.offendingNode +
|
||||
" expecting "+tokenName;
|
||||
}
|
||||
else if ( e instanceof NoViableAltException ) {
|
||||
|
@ -140,16 +140,16 @@ public class Recognizer<ATNInterpreter> {
|
|||
// for development, can add "decision=<<"+nvae.grammarDecisionDescription+">>"
|
||||
// and "(decision="+nvae.decisionNumber+") and
|
||||
// "state "+nvae.stateNumber
|
||||
msg = "no viable alternative at input "+getTokenErrorDisplay(e.token);
|
||||
msg = "no viable alternative at input "+getTokenErrorDisplay(e.offendingToken);
|
||||
}
|
||||
else if ( e instanceof MismatchedSetException ) {
|
||||
MismatchedSetException mse = (MismatchedSetException)e;
|
||||
msg = "mismatched input "+getTokenErrorDisplay(e.token)+
|
||||
msg = "mismatched input "+getTokenErrorDisplay(e.offendingToken)+
|
||||
" expecting set "+mse.expecting;
|
||||
}
|
||||
else if ( e instanceof MismatchedNotSetException ) {
|
||||
MismatchedNotSetException mse = (MismatchedNotSetException)e;
|
||||
msg = "mismatched input "+getTokenErrorDisplay(e.token)+
|
||||
msg = "mismatched input "+getTokenErrorDisplay(e.offendingToken)+
|
||||
" expecting set "+mse.expecting;
|
||||
}
|
||||
else if ( e instanceof FailedPredicateException ) {
|
||||
|
@ -162,7 +162,9 @@ public class Recognizer<ATNInterpreter> {
|
|||
|
||||
/** What is the error header, normally line/character position information? */
|
||||
public String getErrorHeader(RecognitionException e) {
|
||||
return "line "+e.line+":"+e.charPositionInLine;
|
||||
int line = e.offendingToken.getLine();
|
||||
int charPositionInLine = e.offendingToken.getCharPositionInLine();
|
||||
return "line "+line+":"+charPositionInLine;
|
||||
}
|
||||
|
||||
/** How should a token be displayed in an error message? The default
|
||||
|
|
|
@ -42,7 +42,7 @@ public interface TokenStream extends IntStream {
|
|||
/** How far ahead has the stream been asked to look? The return
|
||||
* value is a valid index from 0..n-1.
|
||||
*/
|
||||
int range();
|
||||
// int range();
|
||||
|
||||
/** Get a token at an absolute index i; 0..n-1. This is really only
|
||||
* needed for profiling and debugging and token stream rewriting.
|
||||
|
|
|
@ -38,14 +38,14 @@ public class UnwantedTokenException extends MismatchedTokenException {
|
|||
}
|
||||
|
||||
public Token getUnexpectedToken() {
|
||||
return token;
|
||||
return offendingToken;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
String exp = ", expected "+expecting;
|
||||
if ( token==null ) {
|
||||
if ( offendingToken ==null ) {
|
||||
return "UnwantedTokenException(found="+null+exp+")";
|
||||
}
|
||||
return "UnwantedTokenException(found="+token.getText()+exp+")";
|
||||
return "UnwantedTokenException(found="+ offendingToken.getText()+exp+")";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ public class ParserATNSimulator extends ATNSimulator {
|
|||
// System.out.println(dot.getDOT(atn.rules.get(1), parser.getRuleNames()));
|
||||
}
|
||||
|
||||
public int adaptivePredict(IntStream input, int decision, RuleContext outerContext) {
|
||||
public int adaptivePredict(TokenStream input, int decision, RuleContext outerContext) {
|
||||
predict_calls++;
|
||||
DFA dfa = decisionToDFA[decision];
|
||||
if ( dfa==null || dfa.s0==null ) {
|
||||
|
@ -100,7 +100,7 @@ public class ParserATNSimulator extends ATNSimulator {
|
|||
}
|
||||
}
|
||||
|
||||
public int predictATN(DFA dfa, IntStream input,
|
||||
public int predictATN(DFA dfa, TokenStream input,
|
||||
RuleContext outerContext,
|
||||
boolean useContext)
|
||||
{
|
||||
|
@ -133,14 +133,14 @@ public class ParserATNSimulator extends ATNSimulator {
|
|||
}
|
||||
|
||||
// doesn't create DFA when matching
|
||||
public int matchATN(IntStream input, ATNState startState) {
|
||||
public int matchATN(TokenStream input, ATNState startState) {
|
||||
DFA dfa = new DFA(startState);
|
||||
RuleContext ctx = RuleContext.EMPTY;
|
||||
OrderedHashSet<ATNConfig> s0_closure = computeStartState(dfa.decision, startState, ctx);
|
||||
return execATN(input, dfa, input.index(), s0_closure, false);
|
||||
}
|
||||
|
||||
public int execDFA(IntStream input, DFA dfa, DFAState s0, RuleContext outerContext) {
|
||||
public int execDFA(TokenStream input, DFA dfa, DFAState s0, RuleContext outerContext) {
|
||||
// dump(dfa);
|
||||
if ( outerContext==null ) outerContext = RuleContext.EMPTY;
|
||||
this.outerContext = outerContext;
|
||||
|
@ -229,7 +229,7 @@ public class ParserATNSimulator extends ATNSimulator {
|
|||
return prevAcceptState.prediction;
|
||||
}
|
||||
|
||||
public String getInputString(IntStream input, int start) {
|
||||
public String getInputString(TokenStream input, int start) {
|
||||
if ( input instanceof TokenStream ) {
|
||||
return ((TokenStream)input).toString(start,input.index());
|
||||
}
|
||||
|
@ -239,7 +239,7 @@ public class ParserATNSimulator extends ATNSimulator {
|
|||
return "n/a";
|
||||
}
|
||||
|
||||
public int execATN(IntStream input,
|
||||
public int execATN(TokenStream input,
|
||||
DFA dfa,
|
||||
int startIndex,
|
||||
OrderedHashSet<ATNConfig> s0,
|
||||
|
@ -365,8 +365,9 @@ public class ParserATNSimulator extends ATNSimulator {
|
|||
|
||||
if ( prevAccept==null ) {
|
||||
System.out.println("no viable token at input "+ getLookaheadName(input) +", index "+input.index());
|
||||
NoViableAltException nvae = new NoViableAltException(parser, input, closure, outerContext);
|
||||
nvae.startIndex = startIndex;
|
||||
NoViableAltException nvae =
|
||||
new NoViableAltException(parser, input, input.get(startIndex),
|
||||
closure, outerContext);
|
||||
throw nvae;
|
||||
}
|
||||
|
||||
|
@ -395,7 +396,7 @@ public class ParserATNSimulator extends ATNSimulator {
|
|||
return exitAlt;
|
||||
}
|
||||
|
||||
public int retryWithContext(IntStream input,
|
||||
public int retryWithContext(TokenStream input,
|
||||
DFA dfa,
|
||||
int startIndex,
|
||||
RuleContext originalContext,
|
||||
|
@ -800,11 +801,20 @@ public class ParserATNSimulator extends ATNSimulator {
|
|||
|
||||
public String getTokenName(int t) {
|
||||
if ( t==-1 ) return "EOF";
|
||||
if ( parser!=null && parser.getTokenNames()!=null ) return parser.getTokenNames()[t]+"<"+t+">";
|
||||
String[] tokensNames = parser.getTokenNames();
|
||||
if ( parser!=null && tokensNames !=null ) {
|
||||
if ( t>=tokensNames.length ) {
|
||||
System.err.println(t+" ttype out of range: "+Arrays.toString(tokensNames));
|
||||
System.err.println(((CommonTokenStream)parser.getInputStream()).getTokens());
|
||||
}
|
||||
else {
|
||||
return tokensNames[t]+"<"+t+">";
|
||||
}
|
||||
}
|
||||
return String.valueOf(t);
|
||||
}
|
||||
|
||||
public String getLookaheadName(IntStream input) {
|
||||
public String getLookaheadName(TokenStream input) {
|
||||
return getTokenName(input.LA(1));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
/*
|
||||
[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.*;
|
||||
|
||||
/** 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 release(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) {}
|
||||
}
|
||||
|
||||
|
|
@ -1,276 +0,0 @@
|
|||
/*
|
||||
[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.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/** 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 release(int marker) {
|
||||
for (int i = 0; i < listeners.size(); i++) {
|
||||
DebugEventListener listener = (DebugEventListener)listeners.get(i);
|
||||
listener.release(marker);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,310 +0,0 @@
|
|||
/*
|
||||
[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.*;
|
||||
|
||||
/** 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 release(int marker);
|
||||
|
||||
/** 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 ASTAdaptor.becomeRoot() newRoot parameter.
|
||||
* In our case, it will always be the result of calling
|
||||
* ASTAdaptor.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.ASTAdaptor 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.ASTAdaptor 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);
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
/*
|
||||
[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.*;
|
||||
|
||||
/** 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 release(int i) { listener.release(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);
|
||||
}
|
||||
}
|
|
@ -1,351 +0,0 @@
|
|||
/*
|
||||
[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.*;
|
||||
import org.antlr.v4.runtime.tree.ASTAdaptor;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
|
||||
/** 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 ASTAdaptor adaptor;
|
||||
|
||||
public DebugEventSocketProxy(BaseRecognizer recognizer, ASTAdaptor adaptor) {
|
||||
this(recognizer, DEFAULT_DEBUGGER_PORT, adaptor);
|
||||
}
|
||||
|
||||
public DebugEventSocketProxy(BaseRecognizer recognizer, int port, ASTAdaptor 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 release(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(ASTAdaptor adaptor) { this.adaptor = adaptor; }
|
||||
public ASTAdaptor 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
/*
|
||||
[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.*;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -1,146 +0,0 @@
|
|||
/*
|
||||
[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.*;
|
||||
|
||||
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 release(int index) {
|
||||
dbg.release(index);
|
||||
input.release(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);
|
||||
}
|
||||
}
|
|
@ -1,254 +0,0 @@
|
|||
/*
|
||||
[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.*;
|
||||
import org.antlr.v4.runtime.tree.ASTAdaptor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/** An ASTAdaptor proxy that fires debugging events to a DebugEventListener
|
||||
* delegate and uses the ASTAdaptor 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 ASTAdaptor {
|
||||
protected DebugEventListener dbg;
|
||||
protected ASTAdaptor adaptor;
|
||||
|
||||
public DebugTreeAdaptor(DebugEventListener dbg, ASTAdaptor 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 ASTAdaptor getTreeAdaptor() {
|
||||
return adaptor;
|
||||
}
|
||||
}
|
|
@ -1,146 +0,0 @@
|
|||
/*
|
||||
[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.*;
|
||||
|
||||
/** 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 ASTNodeStream {
|
||||
protected DebugEventListener dbg;
|
||||
protected ASTAdaptor adaptor;
|
||||
protected ASTNodeStream input;
|
||||
protected boolean initialStreamState = true;
|
||||
|
||||
/** Track the last mark() call result value for use in rewind(). */
|
||||
protected int lastMarker;
|
||||
|
||||
public DebugTreeNodeStream(ASTNodeStream 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 ASTAdaptor 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 release(int marker) {
|
||||
dbg.release(marker);
|
||||
input.release(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);
|
||||
}
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
/*
|
||||
[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.*;
|
||||
|
||||
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(ASTNodeStream input, DebugEventListener dbg) {
|
||||
super(input instanceof DebugTreeNodeStream?input:new DebugTreeNodeStream(input,dbg));
|
||||
setDebugListener(dbg);
|
||||
}
|
||||
|
||||
public DebugTreeParser(ASTNodeStream 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();
|
||||
}
|
||||
}
|
|
@ -1,513 +0,0 @@
|
|||
/*
|
||||
[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.*;
|
||||
import org.antlr.v4.runtime.misc.Interval;
|
||||
import org.antlr.v4.runtime.tree.BaseAST;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
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 TokenSource getTokenSource() {
|
||||
return null;
|
||||
}
|
||||
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 BaseAST {
|
||||
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 getCharPositionInLine() {
|
||||
return charPos;
|
||||
}
|
||||
|
||||
public Token getPayload() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getLine() {
|
||||
return line;
|
||||
}
|
||||
|
||||
public Interval getSourceInterval() {
|
||||
return new Interval(tokenIndex, tokenIndex);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
/*
|
||||
[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.ASTAdaptor;
|
||||
|
||||
/** Print out (most of) the events... Useful for debugging, testing... */
|
||||
public class TraceDebugEventListener extends BlankDebugEventListener {
|
||||
ASTAdaptor adaptor;
|
||||
|
||||
public TraceDebugEventListener(ASTAdaptor 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
/*
|
||||
[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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enterRule(String grammarFileName, String ruleName) {
|
||||
for (int i=1; i<=level; i++) {System.out.print(" ");}
|
||||
System.out.println("> "+ruleName+" lookahead(1)="+getInputSymbol(1));
|
||||
level++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exitRule(String grammarFileName, String ruleName) {
|
||||
level--;
|
||||
for (int i=1; i<=level; i++) {System.out.print(" ");}
|
||||
System.out.println("< "+ruleName+" lookahead(1)="+getInputSymbol(1));
|
||||
}
|
||||
|
||||
protected Object getInputSymbol(int k) {
|
||||
if ( input instanceof TokenStream) {
|
||||
return ((TokenStream)input).LT(k);
|
||||
}
|
||||
return new Character((char)input.LA(k));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -99,10 +99,10 @@ public class CommonErrorNode extends CommonAST {
|
|||
", resync="+getText()+">";
|
||||
}
|
||||
else if ( trappedException instanceof MismatchedTokenException ) {
|
||||
return "<mismatched token: "+trappedException.token+", resync="+getText()+">";
|
||||
return "<mismatched token: "+trappedException.offendingToken +", resync="+getText()+">";
|
||||
}
|
||||
else if ( trappedException instanceof NoViableAltException ) {
|
||||
return "<unexpected: "+trappedException.token+
|
||||
return "<unexpected: "+trappedException.offendingToken +
|
||||
", resync="+getText()+">";
|
||||
}
|
||||
return "<error: "+getText()+">";
|
||||
|
|
|
@ -148,8 +148,11 @@ public class TreeParser extends BaseRecognizer {
|
|||
* the input tree not the user.
|
||||
*/
|
||||
public String getErrorHeader(RecognitionException e) {
|
||||
// todo: might not have token; use node?
|
||||
int line = e.offendingToken.getLine();
|
||||
int charPositionInLine = e.offendingToken.getCharPositionInLine();
|
||||
return getGrammarFileName()+": node from "+
|
||||
(e.approximateLineInfo?"after ":"")+"line "+e.line+":"+e.charPositionInLine;
|
||||
(e.approximateLineInfo?"after ":"")+"line "+line+":"+charPositionInLine;
|
||||
}
|
||||
|
||||
/** Tree parsers parse nodes they usually have a token object as
|
||||
|
@ -158,10 +161,10 @@ public class TreeParser extends BaseRecognizer {
|
|||
public String getErrorMessage(RecognitionException e, String[] tokenNames) {
|
||||
if ( this instanceof TreeParser ) {
|
||||
ASTAdaptor adaptor = ((ASTNodeStream)e.input).getTreeAdaptor();
|
||||
e.token = adaptor.getToken(e.node);
|
||||
if ( e.token==null ) { // could be an UP/DOWN node
|
||||
e.token = new CommonToken(adaptor.getType(e.node),
|
||||
adaptor.getText(e.node));
|
||||
e.offendingToken = adaptor.getToken(e.offendingNode);
|
||||
if ( e.offendingToken ==null ) { // could be an UP/DOWN node
|
||||
e.offendingToken = new CommonToken(adaptor.getType(e.offendingNode),
|
||||
adaptor.getText(e.offendingNode));
|
||||
}
|
||||
}
|
||||
return super.getErrorMessage(e);
|
||||
|
|
|
@ -86,8 +86,10 @@ public <file.ASTLabelType> match(int ttype) throws RecognitionException {
|
|||
Parser_(parser, funcs, atn, sempredFuncs, ctor, extras) ::= <<
|
||||
@SuppressWarnings({"all", "warnings", "unchecked", "unused"})
|
||||
public class <parser.name> extends <parser.superclass> {
|
||||
<if(parser.tokens)>
|
||||
public static final int
|
||||
<parser.tokens:{k | <k>=<parser.tokens.(k)>}; separator=", ", wrap, anchor>;
|
||||
<endif>
|
||||
public static final String[] tokenNames = {
|
||||
"\<INVALID>", "\<INVALID>", "\<INVALID>",
|
||||
<parser.tokenNames:{k | "<k>"}; separator=", ", wrap, anchor>
|
||||
|
|
|
@ -68,8 +68,8 @@ public class TestATNInterpreter extends BaseTest {
|
|||
checkMatchedAlt(lg, g, "ac", 1);
|
||||
}
|
||||
catch (NoViableAltException re) {
|
||||
errorIndex = re.index;
|
||||
errorTokenType = re.token.getType();
|
||||
errorIndex = re.offendingTokenIndex;
|
||||
errorTokenType = re.offendingToken.getType();
|
||||
}
|
||||
assertEquals(1, errorIndex);
|
||||
assertEquals(errorTokenType, 5);
|
||||
|
@ -94,8 +94,8 @@ public class TestATNInterpreter extends BaseTest {
|
|||
checkMatchedAlt(lg, g, "abd", 1);
|
||||
}
|
||||
catch (NoViableAltException re) {
|
||||
errorIndex = re.index;
|
||||
errorTokenType = re.token.getType();
|
||||
errorIndex = re.offendingTokenIndex;
|
||||
errorTokenType = re.offendingToken.getType();
|
||||
}
|
||||
assertEquals(2, errorIndex);
|
||||
assertEquals(errorTokenType, 6);
|
||||
|
@ -117,8 +117,8 @@ public class TestATNInterpreter extends BaseTest {
|
|||
checkMatchedAlt(lg, g, "abd", 1);
|
||||
}
|
||||
catch (NoViableAltException re) {
|
||||
errorIndex = re.index;
|
||||
errorTokenType = re.token.getType();
|
||||
errorIndex = re.offendingTokenIndex;
|
||||
errorTokenType = re.offendingToken.getType();
|
||||
}
|
||||
assertEquals(2, errorIndex);
|
||||
assertEquals(errorTokenType, 6);
|
||||
|
@ -186,8 +186,8 @@ public class TestATNInterpreter extends BaseTest {
|
|||
checkMatchedAlt(lg, g, "abd", 1);
|
||||
}
|
||||
catch (NoViableAltException re) {
|
||||
errorIndex = re.index;
|
||||
errorTokenType = re.token.getType();
|
||||
errorIndex = re.offendingTokenIndex;
|
||||
errorTokenType = re.offendingToken.getType();
|
||||
}
|
||||
assertEquals(2, errorIndex);
|
||||
assertEquals(6, errorTokenType);
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* [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.test;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/** test runtime parse errors */
|
||||
public class TestParseErrors extends BaseTest {
|
||||
@Test public void testLL2() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : 'a' 'b'" +
|
||||
" | 'a' 'c'" +
|
||||
";\n" +
|
||||
"q : 'e' ;\n" +
|
||||
"WS : ' ' ;\n";
|
||||
String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "ae", false);
|
||||
String expecting = "line 1:1 no viable alternative at input 'e'\n";
|
||||
String result = stderrDuringParse;
|
||||
assertEquals(expecting, result);
|
||||
}
|
||||
|
||||
@Test public void testLL3() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : 'a' 'b'* 'c'" +
|
||||
" | 'a' 'b' 'd'" +
|
||||
" ;\n" +
|
||||
"q : 'e' ;\n";
|
||||
System.out.println(grammar);
|
||||
String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "abe", false);
|
||||
String expecting = "line 1:2 no viable alternative at input 'e'\n";
|
||||
String result = stderrDuringParse;
|
||||
assertEquals(expecting, result);
|
||||
}
|
||||
|
||||
@Test public void testLLStar() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"a : 'a'+ 'b'" +
|
||||
" | 'a'+ 'c'" +
|
||||
";\n" +
|
||||
"q : 'e' ;\n";
|
||||
String found = execParser("T.g", grammar, "TParser", "TLexer", "a", "aaae", false);
|
||||
String expecting = "line 1:3 no viable alternative at input 'e'\n";
|
||||
String result = stderrDuringParse;
|
||||
assertEquals(expecting, result);
|
||||
}
|
||||
|
||||
@Test public void testLL1ErrorInfo() throws Exception {
|
||||
String grammar =
|
||||
"grammar T;\n" +
|
||||
"start : animal (AND acClass)? service EOF;\n" +
|
||||
"animal : (DOG | CAT );\n" +
|
||||
"service : (HARDWARE | SOFTWARE) ;\n" +
|
||||
"AND : 'and';\n" +
|
||||
"DOG : 'dog';\n" +
|
||||
"CAT : 'cat';\n" +
|
||||
"HARDWARE: 'hardware';\n" +
|
||||
"SOFTWARE: 'software';\n" +
|
||||
"WS : ' ' {skip();} ;" +
|
||||
"acClass\n" +
|
||||
"@init\n" +
|
||||
"{ System.out.println(computeContextSensitiveRuleFOLLOW().toString(tokenNames)); }\n" +
|
||||
" : ;\n";
|
||||
String result = execParser("T.g", grammar, "TParser", "TLexer", "start", "dog and software", false);
|
||||
String expecting = "{HARDWARE,SOFTWARE}\n";
|
||||
assertEquals(expecting, result);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue