Java runtime library for ANTLR v3 - a framework for constructing recognizers, interpreters, compilers, and translators from grammatical descriptions.
—
Debugging, profiling, and tracing tools for parser development and optimization. The debug package provides comprehensive instrumentation for understanding parser behavior and performance characteristics.
Interface for receiving detailed parsing events during execution.
/**
* Interface for debug event handling
*/
public interface DebugEventListener {
/** The parser has just entered rule ruleName */
public void enterRule(String grammarFileName, String ruleName);
/** Because rules can have lots of alternatives, we need an event for each.
* The default implementation does nothing. The decision number is the decision
* in the DFA for the decision state.
*/
public void enterAlt(int alt);
/** The parser has just exited rule ruleName */
public void exitRule(String grammarFileName, String ruleName);
/** The parser has just entered a subrule in the parser such as
* the () subrule.
*/
public void enterSubRule(int decisionNumber);
/** The parser has just exited a subrule in the parser such as
* the () subrule.
*/
public void exitSubRule(int decisionNumber);
/** A decision was made about which alt to enter. */
public void enterDecision(int decisionNumber, boolean couldBacktrack);
/** We are done deciding which alt to enter. */
public void exitDecision(int decisionNumber);
/** The parser consumed a token; tell me which one and by which rule
* that token matched or what rule invoked nextToken. This is perfect
* place to count consumed input or to build trees.
*/
public void consumeToken(Token token);
/** Track the progress of consuming tokens during error recovery.
* This is useful to highlight the input during debugging and
* in error messages.
*/
public void consumeHiddenToken(Token token);
/** Input stream is requesting a look ahead the k-th token.
* k==1 is the current token. k==0 is invalid.
* This event is fired before the next token is actually
* computed so you can see in the debugger what could happen
* next. This is not an indication that the token will be
* consumed next.
*/
public void LT(int i, Token t);
/** Announce that the parser has created a nil node */
public void nilNode(Object t);
/** Announce that the parser has created an error node in the tree
* such as when you find input that does not match the input expected
* via the grammar.
*/
public void errorNode(Object t);
/** Announce that the parser has just created node t for rule r. */
public void createNode(Object t, Token token);
/** Announce that the parser has just altered the tree -- usually by
* making rule node t the new root of old root r.
*/
public void createNode(Object t, int tokenType, String text);
/** Announce that the parser has just altered the tree -- usually by
* making rule node t the new root of old root r.
*/
public void becomeRoot(Object newRoot, Object oldRoot);
/** Announce that the parser has just added child t as a child of root r. */
public void addChild(Object root, Object child);
/** The parser has set the token boundaries for tree node t from
* tokens with indexes start..stop.
*/
public void setTokenBoundaries(Object t, int tokenStartIndex, int tokenStopIndex);
/** Alter the type of tree node t to tokenType */
public void setType(Object node, int tokenType);
/** Alter the text of tree node t to tokenText */
public void setText(Object node, String tokenText);
/** We are done parsing; successfully or not. Track time and tell
* listeners we are done.
*/
public void terminate();
}Parser wrapper that generates debug events during parsing.
/**
* Parser with debugging capabilities
*/
public class DebugParser extends Parser {
/** 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;
public DebugParser(TokenStream input, DebugEventListener dbg, RecognizerSharedState state);
public DebugParser(TokenStream input, RecognizerSharedState state);
public DebugParser(TokenStream input, DebugEventListener dbg);
/** 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);
public List<String> getRuleInvocationStack();
public void reportError(IOException e);
public void reportError(RecognitionException e);
public void beginResync();
public void endResync();
public void beginBacktrack(int level);
public void endBacktrack(int level, boolean successful);
}Usage Examples:
import org.antlr.runtime.debug.*;
import org.antlr.runtime.*;
// Create debug parser with custom listener
MyLexer lexer = new MyLexer(new ANTLRStringStream("x = 42;"));
DebugTokenStream tokens = new DebugTokenStream(new CommonTokenStream(lexer), null);
DebugEventListener listener = new TraceDebugEventListener();
DebugParser parser = new DebugParser(tokens, listener);
// Set up debugging
parser.setDebugListener(listener);
tokens.setDebugListener(listener);
// Parse with debugging enabled
try {
parser.program();
} catch (RecognitionException e) {
System.err.println("Parse failed: " + e.getMessage());
}Token stream wrapper that generates debug events for token access.
/**
* Token stream with debugging support
*/
public class DebugTokenStream implements TokenStream {
protected DebugEventListener dbg;
protected TokenStream input;
protected int lastMarker;
public DebugTokenStream(TokenStream input, DebugEventListener dbg);
public void setDebugListener(DebugEventListener dbg);
public void consume();
public Token get(int i);
public Token LT(int k);
public int mark();
public int index();
public void rewind(int marker);
public void rewind();
public void release(int marker);
public void seek(int index);
public int size();
public TokenSource getTokenSource();
public String getSourceName();
public String toString(int start, int stop);
public String toString(Token start, Token stop);
public String toString();
// Delegate to wrapped stream
public int LA(int i);
public int range();
}Usage Examples:
// Debug token stream with custom listener
CommonTokenStream baseStream = new CommonTokenStream(lexer);
DebugEventListener listener = new MyDebugListener();
DebugTokenStream debugStream = new DebugTokenStream(baseStream, listener);
// Token access generates debug events
Token token = debugStream.LT(1); // Fires LT event
debugStream.consume(); // Fires consumeToken event
// Use with debug parser
DebugParser parser = new DebugParser(debugStream, listener);Performance profiler for analyzing parser efficiency and bottlenecks.
/**
* Performance profiling for parsing
*/
public class Profiler extends BlankDebugEventListener {
public static final String DATA_SEP = "\t";
public static final String newline = System.getProperty("line.separator");
/** Because I may change the stats, I need to track that for later
* computations to be consistent.
*/
public static final String Version = "2";
public static final String RUNTIME_STATS_FILENAME = "runtime.stats";
protected Stats stats;
// all about a specific decision
public static class DecisionDescriptor {
public String fileName;
public String ruleName;
public int decision;
public int maxk;
public boolean couldBacktrack;
public boolean cyclic;
public boolean greedy;
public boolean blockLevel;
public boolean isFixed;
public int numAlts;
public String toString();
}
// Profile stats for decisions
public static class ProfileStats {
public String Version;
public String name;
public int numRuleInvocations;
public int numGuessedEarlyExits;
public int numMemoizationCacheMisses;
public int numMemoizationCacheHits;
public int numBacktrackOccurrences;
public List<DecisionEvent> events;
public List<DecisionDescriptor> decisions;
public String toString();
public String toNotifyString();
}
public Profiler();
public Profiler(Parser parser);
public void terminate();
public void enterDecision(int decisionNumber, boolean couldBacktrack);
public void exitDecision(int decisionNumber);
public void consumeToken(Token token);
public void consumeHiddenToken(Token token);
public void nextToken(Token t);
public void memoize(IntStream input, int ruleIndex, int ruleStartIndex, boolean success);
public void mark(int i);
public void rewind(int i);
public void rewind();
public void beginBacktrack(int level);
public void endBacktrack(int level, boolean successful);
public void location(int line, int pos);
public void recognitionException(RecognitionException e);
public void beginResync();
public void endResync();
public void semanticPredicate(boolean result, String predicate);
public void setParser(Parser parser);
public Parser getParser();
// Report generation
public String getReport();
public String getReport(String name);
public ProfileStats getDecisionProfile();
public String toNotifyString();
public String toString();
}Usage Examples:
// Profile parser performance
MyLexer lexer = new MyLexer(new ANTLRStringStream(input));
CommonTokenStream tokens = new CommonTokenStream(lexer);
MyParser parser = new MyParser(tokens);
// Add profiler
Profiler profiler = new Profiler(parser);
parser.setDebugListener(profiler);
// Parse and collect statistics
parser.program();
// Get performance report
String report = profiler.getReport();
System.out.println("Performance Report:");
System.out.println(report);
// Get decision profile
ProfileStats stats = profiler.getDecisionProfile();
System.out.println("Decision stats: " + stats.toString());Simple trace listener that prints parsing events to console.
/**
* Tracing debug event listener
*/
public class Tracer extends BlankDebugEventListener {
public Parser recognizer;
public Tracer(Parser recognizer);
public void enterRule(String ruleName);
public void exitRule(String ruleName);
}
/**
* Debug listener that prints trace information
*/
public class TraceDebugEventListener extends BlankDebugEventListener {
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 consumeToken(Token token);
public void consumeHiddenToken(Token token);
public void LT(int i, Token t);
public void nilNode(Object t);
public void errorNode(Object t);
public void createNode(Object t, Token token);
public void createNode(Object t, int tokenType, String text);
public void becomeRoot(Object newRoot, Object oldRoot);
public void addChild(Object root, Object child);
public void setTokenBoundaries(Object t, int tokenStartIndex, int tokenStopIndex);
}Usage Examples:
// Simple rule tracing
MyParser parser = new MyParser(tokens);
Tracer tracer = new Tracer(parser);
parser.setDebugListener(tracer);
// This will print rule entry/exit during parsing
parser.program();
// Full event tracing
TraceDebugEventListener trace = new TraceDebugEventListener();
DebugParser debugParser = new DebugParser(tokens, trace);
debugParser.program(); // Prints all parse eventsSupport for remote debugging sessions via socket communication.
/**
* Socket proxy for debug events
*/
public class DebugEventSocketProxy extends BlankDebugEventListener {
public static final int DEFAULT_DEBUGGER_PORT = 0xBFBF;
protected int port;
protected ServerSocket serverSocket;
protected Socket socket;
protected PrintWriter out;
protected BufferedReader in;
public DebugEventSocketProxy(Parser recognizer, int port, TreeAdaptor adaptor)
throws IOException;
public void handshake() throws IOException;
public void commence();
public void terminate();
protected void transmit(String event);
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 consumeToken(Token token);
public void consumeHiddenToken(Token token);
public void LT(int i, Token t);
public void mark(int marker);
public void rewind(int marker);
public void rewind();
public void beginBacktrack(int level);
public void endBacktrack(int level, boolean successful);
public void location(int line, int pos);
public void recognitionException(RecognitionException e);
public void beginResync();
public void endResync();
public void semanticPredicate(boolean result, String predicate);
}
/**
* Remote socket listener for debug events
*/
public class RemoteDebugEventSocketListener {
public static final int MAX_EVENT_ELEMENTS = 8;
protected String grammarFileName;
protected String[] ruleNames;
protected ServerSocket serverSocket;
protected Socket clientSocket;
protected PrintWriter out;
protected BufferedReader in;
protected boolean sessionStarted = false;
public RemoteDebugEventSocketListener(String grammarFileName, String[] ruleNames)
throws IOException;
public void start();
public void waitForDebuggerToAttach() throws IOException;
public void handshake() throws IOException;
public void commence();
public void terminate();
}Usage Examples:
// Remote debugging setup
try {
MyParser parser = new MyParser(tokens);
DebugEventSocketProxy proxy = new DebugEventSocketProxy(parser, 0xBFBF, new CommonTreeAdaptor());
parser.setDebugListener(proxy);
System.out.println("Waiting for debugger to attach...");
proxy.handshake(); // Wait for debugger connection
// Parse with remote debugging
parser.program();
} catch (IOException e) {
System.err.println("Remote debugging failed: " + e.getMessage());
}No-op implementation providing a base for selective event handling.
/**
* No-op debug event listener implementation
*/
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 consumeToken(Token token) {}
public void consumeHiddenToken(Token token) {}
public void LT(int i, Token t) {}
public void nilNode(Object t) {}
public void errorNode(Object t) {}
public void createNode(Object t, Token token) {}
public void createNode(Object t, int tokenType, String text) {}
public void becomeRoot(Object newRoot, Object oldRoot) {}
public void addChild(Object root, Object child) {}
public void setTokenBoundaries(Object t, int tokenStartIndex, int tokenStopIndex) {}
public void setType(Object node, int tokenType) {}
public void setText(Object node, String tokenText) {}
public void terminate() {}
}Multiplexer for sending debug events to multiple listeners.
/**
* Hub for managing multiple debug listeners
*/
public class DebugEventHub implements DebugEventListener {
protected List<DebugEventListener> listeners;
public DebugEventHub(DebugEventListener listener, DebugEventListener... others);
public void addListener(DebugEventListener listener);
public void removeListener(DebugEventListener listener);
/* All methods delegate to all listeners */
public void enterRule(String grammarFileName, String ruleName);
public void exitRule(String grammarFileName, String ruleName);
// ... (implements all DebugEventListener methods)
}public class MyDebugListener extends BlankDebugEventListener {
private int ruleDepth = 0;
private long startTime;
@Override
public void enterRule(String grammarFileName, String ruleName) {
System.out.println(indent() + "-> " + ruleName);
ruleDepth++;
startTime = System.currentTimeMillis();
}
@Override
public void exitRule(String grammarFileName, String ruleName) {
ruleDepth--;
long elapsed = System.currentTimeMillis() - startTime;
System.out.println(indent() + "<- " + ruleName + " (" + elapsed + "ms)");
}
@Override
public void consumeToken(Token token) {
System.out.println(indent() + "consume: " + token.getText());
}
private String indent() {
return " ".repeat(ruleDepth);
}
}public class PerformanceMonitor extends BlankDebugEventListener {
private Map<String, RuleStats> ruleStats = new HashMap<>();
private Stack<RuleEntry> callStack = new Stack<>();
private static class RuleEntry {
String name;
long startTime;
RuleEntry(String name) {
this.name = name;
this.startTime = System.nanoTime();
}
}
private static class RuleStats {
int callCount;
long totalTime;
long maxTime;
void addCall(long time) {
callCount++;
totalTime += time;
maxTime = Math.max(maxTime, time);
}
}
@Override
public void enterRule(String grammarFileName, String ruleName) {
callStack.push(new RuleEntry(ruleName));
}
@Override
public void exitRule(String grammarFileName, String ruleName) {
if (!callStack.isEmpty()) {
RuleEntry entry = callStack.pop();
long elapsed = System.nanoTime() - entry.startTime;
ruleStats.computeIfAbsent(ruleName, k -> new RuleStats())
.addCall(elapsed);
}
}
public void printReport() {
System.out.println("Performance Report:");
System.out.println("Rule\t\tCalls\tTotal(ms)\tAvg(ms)\t\tMax(ms)");
for (Map.Entry<String, RuleStats> entry : ruleStats.entrySet()) {
RuleStats stats = entry.getValue();
double totalMs = stats.totalTime / 1_000_000.0;
double avgMs = totalMs / stats.callCount;
double maxMs = stats.maxTime / 1_000_000.0;
System.out.printf("%s\t\t%d\t%.2f\t\t%.2f\t\t%.2f%n",
entry.getKey(), stats.callCount, totalMs, avgMs, maxMs);
}
}
}public class ConditionalDebugger extends BlankDebugEventListener {
private final Set<String> debugRules;
private final boolean debugTokens;
private boolean inDebugRule = false;
public ConditionalDebugger(Set<String> debugRules, boolean debugTokens) {
this.debugRules = debugRules;
this.debugTokens = debugTokens;
}
@Override
public void enterRule(String grammarFileName, String ruleName) {
if (debugRules.contains(ruleName)) {
inDebugRule = true;
System.out.println("DEBUG: Entering " + ruleName);
}
}
@Override
public void exitRule(String grammarFileName, String ruleName) {
if (debugRules.contains(ruleName)) {
System.out.println("DEBUG: Exiting " + ruleName);
inDebugRule = false;
}
}
@Override
public void consumeToken(Token token) {
if (debugTokens && inDebugRule) {
System.out.println("DEBUG: Token: " + token.getText() +
" at " + token.getLine() + ":" + token.getCharPositionInLine());
}
}
}Install with Tessl CLI
npx tessl i tessl/maven-org-antlr--antlr-runtime