Error Handling

Handling parse errors and debugging grammars

Error Handling

This page covers error handling, debugging, and troubleshooting in Galore.

ParseError

When parsing fails, Galore throws a ParseError:

import { newParser, ParseError } from "galore";

const [parser] = newParser(grammar);

try {
  const result = parser.parse("invalid input");
} catch (err) {
  if (err instanceof ParseError) {
    console.log(err.message);  // Error description
    console.log(err.type);     // Error type (e.g., "UnexpectedToken")
    console.log(err.value);    // Additional context
  }
}

Error Types

TypeDescription
UnexpectedToken Parser encountered a token it didn't expect
TokenizerError Lexer couldn't match input (from TLEX)

Error Value

The value property contains context about the error:

catch (err) {
  if (err instanceof ParseError && err.type === "UnexpectedToken") {
    const { state, token } = err.value;
    console.log(`State: ${state}`);
    console.log(`Token: ${token.tag} = "${token.value}"`);
    console.log(`Position: ${token.start}-${token.end}`);
  }
}

Error Callbacks

onTokenError

Handle lexer errors (invalid characters) without throwing:

const result = parser.parse(input, {
  ruleHandlers: {},
  onTokenError: (err, tape) => {
    console.warn(`Skipping invalid character at position ${tape.offset}`);

    // Skip the invalid character and continue
    tape.advance();

    // Return true to retry tokenization
    return true;
  }
});

Return true to retry after handling, or false (or throw) to abort.

actionResolver

Handle parse table conflicts at runtime:

const result = parser.parse(input, {
  ruleHandlers: {},
  actionResolver: (actions, stack, tokenbuffer) => {
    // Multiple actions available (conflict)
    console.log("Conflict:", actions.map(a => a.toString()));

    // Choose based on context
    // Prefer shift over reduce (default behavior for many parsers)
    const shift = actions.find(a => a.tag === LRActionType.SHIFT);
    if (shift) return shift;

    // Otherwise take first action
    return actions[0];
  }
});

Debug Output

Enable debug output to see lexer and parser internals:

const [parser] = newParser(grammar, {
  type: "lalr",
  debug: "all"  // "all" | "lexer" | "parser"
});

Debug Options

ValueOutput
"lexer" Tokenizer program and token stream
"parser" Parse table and state transitions
"all" Both lexer and parser output

Common Errors

Unexpected Token

The parser received a token that doesn't match any expected symbol in the current state.

Causes:

  • Input doesn't match the grammar
  • Missing token definition
  • Incorrect grammar rule

Debugging:

  1. Check the token that caused the error
  2. Verify the grammar accepts that token in context
  3. Use the Playground to test the grammar

Action Handler Not Found

Error: Action handler not found: handlerName

A grammar rule has a { handlerName } action but no matching function in ruleHandlers.

Fix: Provide all required handlers:

parser.parse(input, {
  ruleHandlers: {
    handlerName: (rule, parent, ...children) => {
      // Handle the reduction
    }
  }
});

Multiple Actions Found

Error: Multiple actions found.

The parse table has a conflict (shift-reduce or reduce-reduce) and no actionResolver was provided.

Fix: Either resolve the grammar conflict or provide an actionResolver:

parser.parse(input, {
  ruleHandlers: {},
  actionResolver: (actions) => actions[0]  // Take first action
});

See Parse Tables for conflict resolution strategies.

Invalid Token Tag

Error: Invalid token tag: UNKNOWN

The lexer produced a token with a tag that doesn't match any terminal in the grammar.

Fix: Ensure token names match between lexer and grammar.

Using the Playground

The Interactive Playground is the best tool for debugging grammars:

  • Parse table visualization - See all states and actions
  • Conflict highlighting - Cells with conflicts are highlighted in amber
  • LR item hints - Hover over states to see item sets
  • Live parsing - Test inputs and see parse trees immediately

Checking for Conflicts

Programmatically check if a grammar has conflicts:

import { newParser } from "galore";

const [parser, tokenFunc, itemGraph] = newParser(grammar, { type: "lalr" });

if (parser.parseTable.hasConflicts) {
  console.log("Grammar has conflicts!");
  console.log(parser.parseTable.conflictActions);
}