CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-acorn-loose

Error-tolerant ECMAScript parser that parses any text into an ESTree syntax tree with reasonable approximations for syntax errors

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

Acorn Loose

Acorn Loose provides an error-tolerant ECMAScript parser that can parse any text into an ESTree syntax tree, making reasonable approximations when encountering syntax errors. It serves as a fallback parser when the regular acorn parser encounters syntax errors, using whitespace as significant for recovery from missing brackets.

Package Information

  • Package Name: acorn-loose
  • Package Type: npm
  • Language: JavaScript
  • Installation: npm install acorn-loose

Core Imports

import { parse, LooseParser, isDummy, dummyValue } from "acorn-loose";

For CommonJS:

const { parse, LooseParser, isDummy, dummyValue } = require("acorn-loose");

Basic Usage

import { parse, isDummy, dummyValue } from "acorn-loose";

// Parse malformed JavaScript code that would fail with regular acorn
const ast = parse("1 / * 4 )[2]", {
  ecmaVersion: 2020,
  locations: true
});

// Walk the AST to find dummy placeholder nodes
function walkAST(node) {
  if (!node || typeof node !== 'object') return;
  
  if (isDummy(node)) {
    console.log(`Found dummy node "${dummyValue}" where parser couldn't recover:`, node);
    return;
  }
  
  // Recursively walk child nodes
  for (let key in node) {
    if (Array.isArray(node[key])) {
      node[key].forEach(walkAST);
    } else if (node[key] && typeof node[key] === 'object' && node[key].type) {
      walkAST(node[key]);
    }
  }
}

walkAST(ast);
console.log("Parsed AST:", JSON.stringify(ast, null, 2));

Architecture

Acorn Loose is built around several key components:

  • Error Recovery System: Comprehensive tokenization error handling that attempts to continue parsing after syntax errors
  • Dummy Node Insertion: Places placeholder nodes with name "✖" (dummyValue) when parsing fails completely
  • Indentation-Based Recovery: Uses whitespace indentation to infer missing brackets and delimiters - maintains a context stack of indentation levels to determine when blocks should close
  • Lookahead Tokenization: Maintains a buffer of future tokens to enable better error recovery decisions
  • Plugin Compatibility: Maintains compatibility with the acorn parser's plugin system while extending error tolerance
  • Statement & Expression Parsing: Separate parsing modules for statements and expressions with comprehensive error tolerance

Capabilities

Parse Function

Main parsing function that accepts any input and returns an ESTree-compliant AST, inserting dummy nodes where syntax errors prevent normal parsing.

/**
 * Parses input string using the loose parser with error tolerance
 * @param {string} input - The JavaScript code to parse
 * @param {Object} options - Parsing options (same as acorn parser options)
 * @returns {Program} ESTree Program node representing the parsed code
 */
function parse(input, options);

Usage Examples:

import { parse } from "acorn-loose";

// Parse valid code (works like regular acorn)
const validAst = parse("const x = 1;", { ecmaVersion: 2020 });

// Parse invalid code with error recovery
const invalidAst = parse("const x = 1 +;", { ecmaVersion: 2020 });
// Parser will insert dummy nodes to complete the expression

// Parse with locations for error reporting
const astWithLoc = parse("function() {", { 
  ecmaVersion: 2020, 
  locations: true 
});

LooseParser Class

The main loose parser class for advanced usage, plugin development, and error-tolerant parsing with full control. Most users should use the standalone parse() function instead.

/**
 * Error-tolerant parser class with plugin support
 */
class LooseParser {
  /**
   * Creates a new parser instance
   * @param {string} input - Input code to parse
   * @param {Object} options - Parser options
   */
  constructor(input, options);

  /**
   * Parses the input and returns the AST
   * @returns {Program} The parsed program AST
   */
  parse();

  /**
   * Creates a new AST node at current position
   * @returns {Node} Empty AST node with position information
   */
  startNode();

  /**
   * Stores current parser position for later use
   * @returns {number|Array} Position information (number or [start, loc] if locations enabled)
   */
  storeCurrentPos();

  /**
   * Creates a new AST node at specific position
   * @param {number|Array} pos - Position from storeCurrentPos()
   * @returns {Node} Empty AST node at specified position
   */
  startNodeAt(pos);

  /**
   * Completes an AST node with the specified type
   * @param {Node} node - The node to complete
   * @param {string} type - The AST node type
   * @returns {Node} The completed node
   */
  finishNode(node, type);

  /**
   * Creates a dummy placeholder node when parsing fails
   * @param {string} type - The type of dummy node to create
   * @returns {Node} Dummy node with placeholder values
   */
  dummyNode(type);

  /**
   * Creates a dummy identifier node with name "✖"
   * @returns {Identifier} Dummy identifier node
   */
  dummyIdent();

  /**
   * Creates a dummy string literal node
   * @returns {Literal} Dummy string literal node
   */
  dummyString();

  /**
   * Consumes a token if it matches the expected type
   * @param {TokenType} type - Expected token type
   * @returns {boolean} True if token was consumed
   */
  eat(type);

  /**
   * Expects and consumes a token, with error recovery if not found
   * @param {TokenType} type - Expected token type
   * @returns {boolean} True if token was found and consumed (may advance multiple tokens for recovery)
   */
  expect(type);

  /**
   * Checks if current token is a contextual keyword
   * @param {string} name - The contextual keyword to check
   * @returns {boolean} True if current token matches the contextual keyword
   */
  isContextual(name);

  /**
   * Consumes a contextual keyword if present
   * @param {string} name - The contextual keyword to consume
   * @returns {boolean} True if contextual keyword was consumed
   */
  eatContextual(name);

  /**
   * Checks if a semicolon can be inserted at current position
   * @returns {boolean} True if semicolon insertion is allowed
   */
  canInsertSemicolon();

  /**
   * Handles semicolon consumption (equivalent to eat(tt.semi))
   * @returns {boolean} True if semicolon was consumed
   */
  semicolon();

  /**
   * Pushes current indentation level onto context stack
   */
  pushCx();

  /**
   * Pops indentation level from context stack and restores it
   */
  popCx();

  /**
   * Advances to the next token with line tracking and context management
   */
  next();

  /**
   * Looks ahead at future tokens without consuming them
   * @param {number} n - Number of tokens to look ahead (1-based index)
   * @returns {Token} The token at position n ahead of current position
   */
  lookAhead(n);

  /**
   * Resets tokenizer to a specific position for error recovery
   * @param {number} pos - Character position in input to reset to
   */
  resetTo(pos);

  /**
   * Extends the parser instance with additional functionality
   * @param {string} name - Method name to extend/replace
   * @param {Function} f - Function that receives current method and returns new method
   */
  extend(name, f);

  /**
   * Extends the parser class with plugins
   * @param {...Function} plugins - Plugin functions to apply
   * @returns {Function} New parser class constructor
   */
  static extend(...plugins);

  /**
   * Static parse method that creates parser instance and parses
   * @param {string} input - Input code to parse
   * @param {Object} options - Parser options
   * @returns {Program} Parsed program AST
   */
  static parse(input, options);

  /**
   * Base parser class used for tokenization (can be changed for custom parsers)
   */
  static BaseParser;
}

Usage Examples:

import { LooseParser } from "acorn-loose";

// Use parser class directly
const parser = new LooseParser("const x = 1;", { ecmaVersion: 2020 });
const ast = parser.parse();

// Use static parse method
const ast2 = LooseParser.parse("const y = 2;", { ecmaVersion: 2020 });

// Extend with plugins
const ExtendedParser = LooseParser.extend(somePlugin);
const ast3 = ExtendedParser.parse("const z = 3;", { ecmaVersion: 2020 });

Dummy Node Detection

Utility function to check if a node is a dummy placeholder inserted by the parser during error recovery.

/**
 * Checks if a node is a dummy placeholder node
 * @param {Node} node - AST node to check
 * @returns {boolean} True if the node is a dummy placeholder
 */
function isDummy(node);

/**
 * The dummy value used as placeholder text in dummy nodes
 * @type {string}
 */
const dummyValue = "✖";

Usage Examples:

import { parse, isDummy, dummyValue } from "acorn-loose";

const ast = parse("const x = 1 +;", { ecmaVersion: 2020 });

// Walk the AST and find dummy nodes
function findDummyNodes(node) {
  if (isDummy(node)) {
    console.log(`Found dummy node with value "${dummyValue}" at:`, node.loc);
    return;
  }
  
  // Recursively check child nodes
  for (let key in node) {
    if (node[key] && typeof node[key] === 'object') {
      if (Array.isArray(node[key])) {
        node[key].forEach(findDummyNodes);
      } else if (node[key].type) {
        findDummyNodes(node[key]);
      }
    }
  }
}

findDummyNodes(ast);

Error Recovery Strategy

The parser employs several error recovery mechanisms:

  • Token Error Recovery: When tokenization fails, attempts to skip problematic characters and continue
  • Dummy Node Insertion: Creates placeholder nodes with name "✖" when parsing cannot continue
  • Whitespace-based Recovery: Uses indentation to infer missing brackets and delimiters
  • Context Preservation: Maintains parsing state across errors to provide meaningful approximations
  • Comprehensive Error Handling: Handles unterminated strings, invalid regexes, malformed numbers, and syntax errors

Plugin System

Acorn Loose supports the same plugin system as the regular acorn parser:

import { LooseParser } from "acorn-loose";

// Create extended parser with plugins
const ExtendedParser = LooseParser.extend(plugin1, plugin2);

// Parse with extended functionality
const ast = ExtendedParser.parse(code, options);

// Change the base parser (affects all loose parser instances)
LooseParser.BaseParser = CustomAcornParser;

Options

Accepts all standard acorn parser options:

  • ecmaVersion: ECMAScript version to parse (3, 5, 6, 2015-2023, "latest")
  • sourceType: "script" or "module"
  • allowReturnOutsideFunction: Allow return statements outside functions
  • allowHashBang: Allow hashbang comments
  • locations: Include location information in AST nodes
  • ranges: Include range information in AST nodes
  • preserveParens: Preserve parentheses in AST
  • allowAwaitOutsideFunction: Allow await outside async functions

Types

// Token types (from acorn)
interface Token {
  type: TokenType;
  value: any;
  start: number;
  end: number;
  loc?: SourceLocation;
}

interface SourceLocation {
  start: Position;
  end: Position;
}

interface Position {
  line: number;
  column: number;
}

// Parser options (extends acorn options)
interface Options {
  ecmaVersion?: 3 | 5 | 6 | 2015 | 2016 | 2017 | 2018 | 2019 | 2020 | 2021 | 2022 | 2023 | "latest";
  sourceType?: "script" | "module";
  allowReturnOutsideFunction?: boolean;
  allowHashBang?: boolean;
  allowAwaitOutsideFunction?: boolean;
  locations?: boolean;
  ranges?: boolean;
  preserveParens?: boolean;
  onComment?: Function;
  onToken?: Function;
}

// AST node types follow ESTree specification
interface Node {
  type: string;
  start: number;
  end: number;
  loc?: SourceLocation;
  range?: [number, number];
}

interface Program extends Node {
  type: "Program";
  body: Statement[];
  sourceType: "script" | "module";
}

interface Identifier extends Node {
  type: "Identifier";
  name: string;
}

interface Literal extends Node {
  type: "Literal";
  value: any;
  raw: string;
}
Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/acorn-loose@8.5.x
Publish Source
CLI
Badge
tessl/npm-acorn-loose badge