CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-pegjs

Parser generator for JavaScript that produces fast parsers with excellent error reporting

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

compiler-system.mddocs/

Compiler System

Multi-pass compiler architecture for transforming grammar ASTs into executable parser code with validation, optimization, and code generation capabilities.

Capabilities

Main Compiler Interface

Compiles grammar ASTs into executable parsers through configurable compilation passes.

/**
 * Compiler module interface
 */
const compiler = {
  /**
   * Compile grammar AST into executable parser
   * @param ast - Grammar AST from parser
   * @param passes - Compilation passes organized by stage
   * @param options - Compilation options
   * @returns Generated parser object or source code string
   * @throws {GrammarError} If AST contains semantic errors
   */
  compile: function(ast, passes, options),
  
  /**
   * AST visitor builder for traversing syntax trees
   */
  visitor: {
    build: function(functions)
  },
  
  /**
   * Built-in compilation passes organized by stage
   */
  passes: {
    check: Object,      // Validation passes
    transform: Object,  // AST transformation passes  
    generate: Object    // Code generation passes
  }
};

Usage Examples:

const peg = require("pegjs");

// Parse grammar into AST
const ast = peg.parser.parse("start = 'hello'");

// Compile with default passes
const parser = peg.compiler.compile(
  ast,
  peg.compiler.passes,
  { output: "parser" }
);

// Compile with custom pass configuration
const customPasses = {
  check: [
    peg.compiler.passes.check.reportUndefinedRules,
    peg.compiler.passes.check.reportDuplicateRules
  ],
  transform: [
    peg.compiler.passes.transform.removeProxyRules
  ],
  generate: [
    peg.compiler.passes.generate.generateBytecode,
    peg.compiler.passes.generate.generateJS
  ]
};

const source = peg.compiler.compile(ast, customPasses, {
  output: "source",
  format: "commonjs"
});

Compilation Options

Configuration options for the compilation process.

/**
 * Compilation options interface
 */
interface CompileOptions {
  /** Rules parser can start from (default: [first rule]) */
  allowedStartRules?: string[];
  
  /** Enable result caching (default: false) */
  cache?: boolean;
  
  /** Module dependencies map (default: {}) */
  dependencies?: { [key: string]: string };
  
  /** Global variable name for globals/umd format (default: null) */
  exportVar?: string;
  
  /** Output format (default: "bare") */
  format?: "amd" | "bare" | "commonjs" | "globals" | "umd";
  
  /** Optimization target (default: "speed") */
  optimize?: "speed" | "size";
  
  /** Output type (default: "parser") */
  output?: "parser" | "source";
  
  /** Enable tracing (default: false) */
  trace?: boolean;
}

Compilation Passes

Check Passes

Validation passes that detect semantic errors in grammar ASTs.

/**
 * Validation passes for semantic analysis
 */
const checkPasses = {
  /**
   * Report references to undefined rules
   * @param ast - Grammar AST to analyze
   * @param options - Compilation options
   * @throws {GrammarError} If undefined rules found
   */
  reportUndefinedRules: function(ast, options),
  
  /**
   * Report duplicate rule definitions
   * @param ast - Grammar AST to analyze  
   * @param options - Compilation options
   * @throws {GrammarError} If duplicate rules found
   */
  reportDuplicateRules: function(ast, options),
  
  /**
   * Report duplicate labels within rules
   * @param ast - Grammar AST to analyze
   * @param options - Compilation options  
   * @throws {GrammarError} If duplicate labels found
   */
  reportDuplicateLabels: function(ast, options),
  
  /**
   * Report infinite recursion patterns
   * @param ast - Grammar AST to analyze
   * @param options - Compilation options
   * @throws {GrammarError} If infinite recursion detected
   */
  reportInfiniteRecursion: function(ast, options),
  
  /**
   * Report infinite repetition patterns  
   * @param ast - Grammar AST to analyze
   * @param options - Compilation options
   * @throws {GrammarError} If infinite repetition detected
   */
  reportInfiniteRepetition: function(ast, options)
};

Check Pass Example:

// These passes will throw GrammarError if issues are found
try {
  peg.compiler.passes.check.reportUndefinedRules(ast, options);
  peg.compiler.passes.check.reportDuplicateRules(ast, options);
} catch (error) {
  if (error.name === "GrammarError") {
    console.error("Grammar validation error:", error.message);
  }
}

Transform Passes

AST transformation passes that optimize or modify the syntax tree.

/**
 * AST transformation passes
 */
const transformPasses = {
  /**
   * Remove proxy rules that just forward to other rules
   * Optimizes the AST by eliminating unnecessary indirection
   * @param ast - Grammar AST to transform
   * @param options - Compilation options
   */
  removeProxyRules: function(ast, options)
};

Transform Pass Example:

// Grammar with proxy rule:
// start = expression
// expression = number
// number = [0-9]+

// After removeProxyRules:
// start = number  
// number = [0-9]+
// (expression rule removed as it just forwards to number)

peg.compiler.passes.transform.removeProxyRules(ast, options);

Generate Passes

Code generation passes that produce executable parser code.

/**
 * Code generation passes
 */
const generatePasses = {
  /**
   * Generate intermediate bytecode representation
   * First stage of code generation process
   * @param ast - Grammar AST to compile
   * @param options - Compilation options
   */
  generateBytecode: function(ast, options),
  
  /**
   * Generate final JavaScript parser code
   * Second stage that converts bytecode to JavaScript
   * @param ast - Grammar AST with bytecode
   * @param options - Compilation options  
   */
  generateJS: function(ast, options)
};

Generate Pass Example:

// Two-stage generation process
peg.compiler.passes.generate.generateBytecode(ast, options);
peg.compiler.passes.generate.generateJS(ast, options);

// After generation, ast.code contains the JavaScript parser source
console.log(ast.code); // Generated parser code

AST Visitor System

Visitor Builder

Creates visitor functions for traversing and manipulating ASTs.

/**
 * AST visitor builder
 */
const visitor = {
  /**
   * Build visitor function from handler map
   * @param functions - Map of node types to handler functions
   * @returns Visitor function for traversing ASTs
   */
  build: function(functions): (node: ASTNode) => any
};

/**
 * Visitor function interface
 */
interface VisitorFunction {
  (node: ASTNode, ...extraArgs: any[]): any;
}

/**
 * Handler function map for different node types
 */
interface VisitorHandlers {
  grammar?: (node: GrammarNode, ...args: any[]) => any;
  initializer?: (node: InitializerNode, ...args: any[]) => any;
  rule?: (node: RuleNode, ...args: any[]) => any;
  choice?: (node: ChoiceNode, ...args: any[]) => any;
  sequence?: (node: SequenceNode, ...args: any[]) => any;
  labeled?: (node: LabeledNode, ...args: any[]) => any;
  action?: (node: ActionNode, ...args: any[]) => any;
  optional?: (node: OptionalNode, ...args: any[]) => any;
  zero_or_more?: (node: ZeroOrMoreNode, ...args: any[]) => any;
  one_or_more?: (node: OneOrMoreNode, ...args: any[]) => any;
  simple_and?: (node: SimpleAndNode, ...args: any[]) => any;
  simple_not?: (node: SimpleNotNode, ...args: any[]) => any;
  semantic_and?: (node: SemanticAndNode, ...args: any[]) => any;
  semantic_not?: (node: SemanticNotNode, ...args: any[]) => any;
  rule_ref?: (node: RuleRefNode, ...args: any[]) => any;
  literal?: (node: LiteralNode, ...args: any[]) => any;
  class?: (node: ClassNode, ...args: any[]) => any;
  any?: (node: AnyNode, ...args: any[]) => any;
  text?: (node: TextNode, ...args: any[]) => any;
  named?: (node: NamedNode, ...args: any[]) => any;
  group?: (node: GroupNode, ...args: any[]) => any;
}

Visitor Usage Examples:

// Create a visitor that counts rule references
const countRefs = peg.compiler.visitor.build({
  rule_ref: function(node, counts) {
    counts[node.name] = (counts[node.name] || 0) + 1;
  }
});

const refCounts = {};
countRefs(ast, refCounts);
console.log(refCounts); // { ruleName: count, ... }

// Create a visitor that collects all string literals
const collectLiterals = peg.compiler.visitor.build({
  literal: function(node, literals) {
    literals.push(node.value);
  }
});

const literals = [];
collectLiterals(ast, literals);
console.log(literals); // ["string1", "string2", ...]

// Create a visitor that transforms nodes
const transformRefs = peg.compiler.visitor.build({
  rule_ref: function(node) {
    // Transform rule references to uppercase
    node.name = node.name.toUpperCase();
  }
});

transformRefs(ast); // Modifies AST in place

Default Visitor Behavior

The visitor builder provides default traversal behavior for all node types:

// Default handlers automatically traverse child nodes
// You only need to override specific node types you want to handle

const visitor = peg.compiler.visitor.build({
  // Only handle literals, all other nodes traversed automatically
  literal: function(node) {
    console.log("Found literal:", node.value);
  }
});

Custom Compilation Passes

Creating Custom Passes

You can create custom compilation passes for specialized validation or transformation:

// Custom validation pass
function reportLongRuleNames(ast, options) {
  const visitor = peg.compiler.visitor.build({
    rule: function(node) {
      if (node.name.length > 20) {
        throw new peg.GrammarError(
          `Rule name too long: ${node.name}`,
          node.location
        );
      }
    }
  });
  
  visitor(ast);
}

// Custom transformation pass  
function prefixRuleNames(ast, options) {
  const prefix = options.rulePrefix || "rule_";
  
  const visitor = peg.compiler.visitor.build({
    rule: function(node) {
      if (!node.name.startsWith(prefix)) {
        node.name = prefix + node.name;
      }
    },
    rule_ref: function(node) {
      if (!node.name.startsWith(prefix)) {
        node.name = prefix + node.name;
      }
    }
  });
  
  visitor(ast);
}

// Use custom passes
const customPasses = {
  check: [
    peg.compiler.passes.check.reportUndefinedRules,
    reportLongRuleNames  // Custom validation
  ],
  transform: [
    prefixRuleNames,     // Custom transformation
    peg.compiler.passes.transform.removeProxyRules
  ],
  generate: [
    peg.compiler.passes.generate.generateBytecode,
    peg.compiler.passes.generate.generateJS
  ]
};

const parser = peg.compiler.compile(ast, customPasses, {
  rulePrefix: "my_",
  output: "parser"
});

Install with Tessl CLI

npx tessl i tessl/npm-pegjs

docs

cli-tool.md

compiler-system.md

grammar-parsing.md

index.md

parser-generation.md

utility-modules.md

tile.json