CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-antlr--antlr-runtime

Java runtime library for ANTLR v3 - a framework for constructing recognizers, interpreters, compilers, and translators from grammatical descriptions.

Pending
Overview
Eval results
Files

debug-support.mddocs/

Debug Support

Debugging, profiling, and tracing tools for parser development and optimization. The debug package provides comprehensive instrumentation for understanding parser behavior and performance characteristics.

Capabilities

Debug Event Listener

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();
}

Debug Parser

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());
}

Debug Token Stream

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);

Profiler

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());

Tracer

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 events

Remote Debugging

Support 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());
}

Utility Classes

Blank Debug Event Listener

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() {}
}

Debug Event Hub

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)
}

Common Patterns

Custom Debug Listener

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);
    }
}

Performance Monitoring

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);
        }
    }
}

Conditional Debugging

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

docs

character-streams.md

debug-support.md

error-handling.md

index.md

lexical-analysis.md

parsing.md

token-streams.md

tree-construction.md

tile.json