or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

acorn-loose.mdacorn-walk.mdacorn.mdindex.md
tile.json

acorn-loose.mddocs/

Acorn Loose Parser

The Acorn Loose Parser provides error-tolerant parsing of JavaScript code, attempting to recover from syntax errors and produce usable ASTs even from malformed or incomplete source code. This is particularly useful for code editors, linters, and other tools that need to work with potentially broken JavaScript.

Core Imports

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

For CommonJS:

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

Capabilities

Loose Parse Function

Parses JavaScript code with error recovery, handling syntax errors gracefully.

/**
 * Parses JavaScript source code with error tolerance and recovery
 * @param input - JavaScript source code string (potentially malformed)
 * @param options - Parser configuration options (same as main acorn parser)
 * @returns Program node representing the parsed code with error recovery
 */
function parse(input: string, options: Options): Program;

Usage Example:

import { parse } from "acorn-loose";

// This code has syntax errors but can still be parsed
const brokenCode = `
  function incomplete() {
    let x = 
    return x + 1
  // missing closing brace and semicolons
  
  let y = "unterminated string
  console.log(y)
`;

const ast = parse(brokenCode, { ecmaVersion: 2020 });

console.log(ast.type); // "Program"
console.log(ast.body.length); // Still produces a parseable AST

Loose Parser Class

The LooseParser class extends the main Acorn Parser with error recovery capabilities.

/**
 * LooseParser class with error-tolerant parsing capabilities
 * Extends the main Parser class with recovery mechanisms
 */
const LooseParser: typeof Parser;

The LooseParser class provides the same interface as the main Parser but with different internal behavior for handling errors:

  • Return outside functions: Allowed without error
  • Label consistency: Not enforced (breaks to non-existent labels won't error)
  • Syntax errors: Recovers by inserting dummy nodes or skipping problematic tokens
  • Incomplete constructs: Attempts to complete missing syntax elements

Usage Example:

import { LooseParser } from "acorn-loose";

const brokenCode = "function test() { let x = ; return x }";
const ast = LooseParser.parse(brokenCode, { ecmaVersion: 2020 });

// The parser recovers from the incomplete assignment
console.log(ast.body[0].body.body[0].declarations[0].init); // May be a dummy node

Dummy Node Detection

Identifies dummy nodes inserted during error recovery.

/**
 * Checks if a node is a dummy node inserted by the loose parser
 * @param node - AST node to check
 * @returns true if the node is a dummy placeholder, false otherwise
 */
function isDummy(node: Node): boolean;

Dummy nodes are created with the name "✖" and are inserted when the parser cannot determine what should be present at a particular location.

Usage Example:

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

const brokenCode = "let x = ; console.log(x);";
const ast = parse(brokenCode, { ecmaVersion: 2020 });

// Find all dummy nodes
simple(ast, {
  Identifier(node) {
    if (isDummy(node)) {
      console.log("Found dummy node at position", node.start);
    }
  }
});

Error Recovery Mechanisms

The loose parser employs several strategies to recover from syntax errors:

1. Dummy Value Insertion

When an expression is expected but not found, a dummy identifier with the name "✖" is inserted.

// Input: "let x = ;"
// Recovers to: "let x = ✖;"

2. Missing Semicolon Recovery

Automatically inserts missing semicolons where needed.

// Input: "let x = 5 let y = 10"
// Recovers by treating as: "let x = 5; let y = 10;"

3. Incomplete Function Recovery

Handles functions with missing closing braces or incomplete parameter lists.

// Input: "function test(a, b { return a + b"
// Recovers by adding missing closing parenthesis and brace

4. Bracket Balancing

Attempts to balance unmatched brackets and parentheses.

// Input: "array[index"
// Recovers by adding missing closing bracket: "array[index]"

5. Statement Recovery

Recovers from incomplete statements by inserting necessary tokens or skipping problematic sections.

Configuration Options

The loose parser accepts the same options as the main acorn parser, with some default differences:

interface LooseOptions extends Options {
  // Default tabSize is set to 4 for the loose parser
  // All other options inherited from main Options interface
}

Default Differences:

  • tabSize: Defaults to 4 (vs undefined in main parser)
  • More permissive handling of reserved words and strict mode violations
  • Automatic semicolon insertion is more aggressive

Common Usage Patterns

Code Editor Integration

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

function analyzeCode(sourceCode) {
  const ast = parse(sourceCode, { 
    ecmaVersion: "latest",
    sourceType: "module",
    locations: true
  });
  
  const errors = [];
  const functions = [];
  
  simple(ast, {
    Identifier(node) {
      if (isDummy(node)) {
        errors.push({
          type: "syntax-error",
          position: node.start,
          message: "Unexpected syntax"
        });
      }
    },
    
    FunctionDeclaration(node) {
      functions.push({
        name: node.id?.name || "anonymous",
        start: node.start,
        end: node.end
      });
    }
  });
  
  return { ast, errors, functions };
}

Linting with Error Recovery

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

function lintCode(code) {
  const ast = parse(code, { ecmaVersion: 2022 });
  const issues = [];
  
  ancestor(ast, {
    VariableDeclaration(node, state, ancestors) {
      // Check for unused variables, even in broken code
      if (node.kind === "var") {
        issues.push({
          type: "prefer-const-let",
          node,
          message: "Prefer 'const' or 'let' over 'var'"
        });
      }
    },
    
    Identifier(node, state, ancestors) {
      if (isDummy(node)) {
        issues.push({
          type: "syntax-error",
          node,
          message: "Syntax error: incomplete expression"
        });
      }
    }
  });
  
  return issues;
}

Progressive Parsing

import { parse as strictParse } from "acorn";
import { parse as looseParse } from "acorn-loose";

function parseWithFallback(code, options) {
  try {
    // Try strict parsing first
    return {
      ast: strictParse(code, options),
      hasErrors: false
    };
  } catch (error) {
    // Fall back to loose parsing
    return {
      ast: looseParse(code, options),
      hasErrors: true,
      error: error.message
    };
  }
}

const result = parseWithFallback("let x = ;", { ecmaVersion: 2020 });
if (result.hasErrors) {
  console.log("Parsed with errors:", result.error);
}

Template Processing

import { parse } from "acorn-loose";

function processTemplate(templateCode) {
  // Template might have placeholders that cause syntax errors
  const code = templateCode.replace(/\{\{(\w+)\}\}/g, "PLACEHOLDER_$1");
  
  const ast = parse(code, { 
    ecmaVersion: 2020,
    allowReturnOutsideFunction: true
  });
  
  // Process the AST even if original template was malformed
  return ast;
}

const template = "function process() { return {{value}} + 1; }";
const ast = processTemplate(template);

Differences from Main Parser

FeatureMain ParserLoose Parser
Syntax errorsThrows exceptionsRecovers with dummy nodes
Return outside functionsError (unless option set)Allowed
Label consistencyEnforcedNot enforced
Missing semicolonsMay errorAuto-inserted
Incomplete constructsErrorRecovery attempted
PerformanceFasterSlightly slower due to recovery
Use caseProduction parsingDevelopment tools

Error Recovery Limitations

While the loose parser is very tolerant, some limitations exist:

  1. Deeply nested errors: Multiple syntax errors in nested structures may not recover perfectly
  2. Semantic correctness: The parser doesn't verify semantic correctness, only syntactic structure
  3. Complex expressions: Very complex malformed expressions may result in unexpected AST structures
  4. Performance: Error recovery adds overhead compared to the main parser

Best Practices

  1. Use for development tools: Ideal for editors, linters, and analyzers that need to work with incomplete code
  2. Check for dummy nodes: Always verify parsed results using isDummy() when accuracy is important
  3. Combine with strict parsing: Use as a fallback after trying the main parser first
  4. Handle edge cases: Be prepared for unusual AST structures in recovered code
  5. Report errors: Surface syntax issues to users when dummy nodes are found