CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-ohm-js

An object-oriented language for parsing and pattern matching based on parsing expression grammars

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

parsing-and-matching.mddocs/

Parsing and Matching

Grammar parsing capabilities with support for incremental parsing, detailed error reporting, and trace visualization.

Imports

import { grammar } from "ohm-js";

For TypeScript:

import { grammar, Grammar, MatchResult, Matcher, Interval, RuleInfo, LineAndColumnInfo } from "ohm-js";

Capabilities

Grammar Interface

Core interface for compiled grammar objects with parsing and semantic capabilities.

/**
 * An Ohm Grammar compiled from source code
 */
interface Grammar {
  /** Name of the grammar */
  name: string;
  /** Parent grammar if this extends another grammar */
  superGrammar: Grammar;
  /** Dictionary of rule definitions in this grammar */
  rules: {[ruleName: string]: RuleInfo};

  /** Return true if the grammar is a built-in grammar, otherwise false */
  isBuiltIn(): boolean;

  /**
   * Try to match input with this grammar, returning a MatchResult
   * @param input - String to parse
   * @param startRule - Optional rule name to start matching from
   * @returns MatchResult indicating success or failure
   */
  match(input: string, startRule?: string): MatchResult;

  /**
   * Create a new Matcher object which supports incrementally matching
   * this grammar against a changing input string
   * @returns Matcher instance for incremental parsing
   */
  matcher(): Matcher;

  /**
   * Like match() except returns a trace object whose toString() returns
   * a summary of each parsing step useful for debugging
   * @param input - String to parse
   * @param startRule - Optional rule name to start matching from
   * @returns Trace object with debugging information
   */
  trace(input: string, startRule?: string): Object;

  /**
   * Create a new Semantics object for this Grammar
   * @returns Empty semantics instance
   */
  createSemantics(): Semantics;

  /**
   * Create a new Semantics object for this Grammar that inherits all
   * of the operations and attributes in superSemantics
   * @param superSemantics - Parent semantics to inherit from
   * @returns Extended semantics instance
   */
  extendSemantics(superSemantics: Semantics): Semantics;
}

Usage Examples:

import { grammar } from "ohm-js";

const g = grammar(`
  Calculator {
    expr = number ("+" number)*
    number = digit+
  }
`);

// Basic matching
const match = g.match("2+3+4");
if (match.succeeded()) {
  console.log("Parsing succeeded!");
}

// Match with specific start rule
const numberMatch = g.match("42", "number");

// Check if grammar is built-in
console.log("Is built-in:", g.isBuiltIn()); // false

Match Results

Result object returned by grammar match operations containing success/failure status and error information.

/**
 * Result of Grammar#match operation
 */
interface MatchResult {
  /**
   * True iff match succeeded
   */
  succeeded(): boolean;

  /**
   * True iff match did not succeed
   */
  failed(): boolean;

  /**
   * If match failed contains an error message indicating where and
   * why the match failed. This message is suitable for end users of a
   * language (i.e., people who do not have access to the grammar source).
   */
  message?: string;

  /**
   * If match failed contains an abbreviated version of this.message that
   * does not include an excerpt from the invalid input.
   */
  shortMessage?: string;

  /**
   * If this MatchResult is a failure, returns an Interval indicating
   * the position of the rightmost failure.
   */
  getInterval(): Interval;
}

Usage Examples:

const match = grammar.match("invalid input");

if (match.failed()) {
  console.error("Parse error:", match.message);
  console.error("Short error:", match.shortMessage);
  
  const errorPos = match.getInterval();
  console.error("Error at position:", errorPos.startIdx);
}

Incremental Parsing

Matcher objects support incremental parsing for use in editors and IDEs where the input changes frequently.

/**
 * Matcher objects are used to incrementally match a changing input
 * against a Grammar, e.g. in an editor or IDE.
 */
interface Matcher {
  /** The grammar this matcher uses */
  grammar: Grammar;

  /**
   * Return the current input string
   */
  getInput(): string;

  /**
   * Set the input string to `str`
   * @param str - New input string
   */
  setInput(str: string): void;

  /**
   * Edit the current input string, replacing the characters between
   * `startIdx` and `endIdx` with `str`
   * @param startIdx - Start position of replacement
   * @param endIdx - End position of replacement
   * @param str - Replacement string
   * @returns This matcher for chaining
   */
  replaceInputRange(startIdx: number, endIdx: number, str: string): Matcher;

  /**
   * Like Grammar#match, but operates incrementally
   * @param optStartRule - Optional start rule name
   * @returns MatchResult from current input
   */
  match(optStartRule?: string): MatchResult;

  /**
   * Like Grammar#trace, but operates incrementally
   * @param optStartRule - Optional start rule name
   * @returns Trace object for debugging
   */
  trace(optStartRule?: string): Object;
}

Usage Examples:

const matcher = grammar.matcher();

// Set initial input
matcher.setInput("2 + 3");
let result = matcher.match();

// Edit the input incrementally
matcher.replaceInputRange(0, 1, "5"); // Changes "2 + 3" to "5 + 3"
result = matcher.match();

// Get current input
console.log("Current input:", matcher.getInput());

Rule Information

Metadata about individual grammar rules.

interface RuleInfo {
  /** Parsing expression body of the rule */
  body: PExpr;
  /** Formal parameter names for parameterized rules */
  formals: string[];
  /** Description string from grammar source */
  description: string;
  /** Source interval where rule is defined */
  source: Interval;
}

Interval Information

Represents a subset of a string with position and content information.

/**
 * An Interval represents a subset of a string
 */
interface Interval {
  /** The string containing the interval */
  sourceString: string;
  /** The start index of the interval in `sourceString` */
  startIdx: number;
  /** The end index of the interval in `sourceString` */
  endIdx: number;
  /** Contents of interval */
  contents: string;

  /** Returns a new Interval at the start of this one */
  collapsedLeft(): Interval;
  /** Returns a new Interval at the end of this one */
  collapsedRight(): Interval;
  /** Returns a new Interval that covers this and all argument Intervals */
  coverageWith(...intervals: Interval[]): Interval;
  /** Returns a nicely-formatted string describing the start of the Interval */
  getLineAndColumnMessage(): string;
  /** Returns structure with line and column number information */
  getLineAndColumn(): LineAndColumnInfo;
  /** Returns array of intervals representing the difference operation */
  minus(that: Interval): Interval[];
  /** Returns a new Interval relative to another interval */
  relativeTo(that: Interval): Interval;
  /** Returns a new Interval with whitespace trimmed from both ends */
  trimmed(): Interval;
  /** Returns a new Interval with given length at startIdx + offset */
  subInterval(offset: number, len: number): Interval;
}

interface LineAndColumnInfo {
  offset: number;
  lineNum: number;
  colNum: number;
  line: string;
  prevLine: string;
  nextLine: string;
  toString(...ranges: number[][]): string;
}

Error Handling and Debugging

Trace Support

Ohm provides detailed tracing capabilities for debugging grammar matching:

// Get detailed trace information
const trace = grammar.trace("invalid input");
console.log(trace.toString()); // Prints step-by-step parsing trace

Error Messages

Match failures provide human-readable error messages:

const match = grammar.match("2 +"); // Incomplete expression

if (match.failed()) {
  // Full error message with context
  console.error(match.message);
  
  // Shorter error without input excerpt  
  console.error(match.shortMessage);
  
  // Get error position for highlighting
  const errorInterval = match.getInterval();
  console.error(`Error at line ${errorInterval.getLineAndColumn().lineNum}`);
}

Install with Tessl CLI

npx tessl i tessl/npm-ohm-js

docs

grammar-management.md

index.md

parsing-and-matching.md

parsing-expressions.md

semantic-actions.md

utilities-and-extras.md

tile.json