Simple, fast, powerful parser toolkit for JavaScript using the Earley parsing algorithm
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Random text generation from grammars for testing, fuzzing, and example generation.
Generate random text that matches a given grammar, useful for testing parsers and creating example inputs.
/**
* Generate random text from a compiled grammar or Grammar instance
* @param grammar - Compiled grammar object (from nearleyc) or Grammar instance
* @param start - Start symbol name (optional, uses grammar default)
* @param depth - Maximum recursion depth (optional, null for unbounded)
* @returns Random text string matching the grammar
*/
function Unparse(grammar: object | Grammar, start?: string, depth?: number | null): string;Usage Examples:
const Unparse = require("nearley/lib/unparse");
// Load compiled grammar
const grammar = require("./arithmetic-grammar.js");
// Generate random arithmetic expressions
const randomExpr1 = Unparse(grammar);
console.log("Random expression:", randomExpr1);
// Example output: "42 + 17 * 3"
// Generate with specific start symbol
const randomExpr2 = Unparse(grammar, "factor");
console.log("Random factor:", randomExpr2);
// Example output: "123"
// Generate with depth limit to avoid infinite recursion
const boundedExpr = Unparse(grammar, "expr", 5);
console.log("Bounded expression:", boundedExpr);
// Example output: "8 + 2" (shorter due to depth limit)Generate text without depth restrictions (may not terminate for recursive grammars).
/**
* Generate unbounded random text (use with caution)
* @param grammar - Grammar instance or compiled grammar object
* @param start - Start symbol name
* @returns Random text string (may be very long or not terminate)
*/
function genRandom(grammar: Grammar, start: string): string;Usage Examples:
const { genRandom } = require("nearley/lib/unparse");
// Use only with grammars that naturally terminate
const terminalGrammar = require("./finite-grammar.js");
try {
// Generate without limits (risky with recursive grammars)
const result = genRandom(terminalGrammar, "sentence");
console.log("Generated:", result);
} catch (error) {
console.error("Generation failed:", error.message);
}Generate text with explicit depth bounds to guarantee termination.
/**
* Generate bounded random text with depth limit
* @param grammar - Grammar instance or compiled grammar object
* @param start - Start symbol name
* @param depth - Maximum recursion depth
* @returns Random text string within depth bounds
*/
function genBounded(grammar: Grammar, start: string, depth: number): string;Usage Examples:
const { genBounded } = require("nearley/lib/unparse");
const grammar = require("./recursive-grammar.js");
// Generate with various depth limits
for (let depth = 1; depth <= 5; depth++) {
const result = genBounded(grammar, "expr", depth);
console.log(`Depth ${depth}: ${result}`);
}
// Example output:
// Depth 1: "x"
// Depth 2: "x + y"
// Depth 3: "(x + y) * z"
// Depth 4: "((x + y) * z) - (a + b)"
// Depth 5: "(((x + y) * z) - (a + b)) / ((c * d) + e)"Use text generation for systematic testing of parsers.
Usage Examples:
const nearley = require("nearley");
const Unparse = require("nearley/lib/unparse");
// Test parser with generated inputs
function fuzzTestParser(grammarFile, numTests = 100) {
const compiledGrammar = require(grammarFile);
const grammar = nearley.Grammar.fromCompiled(compiledGrammar);
let passed = 0;
let failed = 0;
for (let i = 0; i < numTests; i++) {
// Generate random input
const input = Unparse(compiledGrammar, null, 10);
try {
// Test if parser can parse generated input
const parser = new nearley.Parser(grammar);
parser.feed(input);
if (parser.results.length > 0) {
passed++;
console.log(`✓ Test ${i + 1}: "${input}"`);
} else {
failed++;
console.log(`✗ Test ${i + 1}: No parse for "${input}"`);
}
} catch (error) {
failed++;
console.log(`✗ Test ${i + 1}: Parse error for "${input}": ${error.message}`);
}
}
console.log(`\nResults: ${passed} passed, ${failed} failed`);
return { passed, failed };
}
// Run fuzz test
fuzzTestParser("./json-grammar.js", 50);Generate example inputs for documentation and tutorials.
Usage Examples:
const Unparse = require("nearley/lib/unparse");
// Generate examples for documentation
function generateExamples(grammarFile, count = 10) {
const grammar = require(grammarFile);
const examples = new Set(); // Use Set to avoid duplicates
// Generate unique examples
while (examples.size < count) {
const example = Unparse(grammar, null, 8);
examples.add(example);
}
return Array.from(examples).sort();
}
// Generate examples for different complexity levels
function generateTieredExamples(grammarFile) {
const grammar = require(grammarFile);
const tiers = {
simple: [],
medium: [],
complex: []
};
// Generate examples at different depth levels
for (let i = 0; i < 20; i++) {
tiers.simple.push(Unparse(grammar, null, 2));
tiers.medium.push(Unparse(grammar, null, 5));
tiers.complex.push(Unparse(grammar, null, 10));
}
// Remove duplicates and sort
Object.keys(tiers).forEach(tier => {
tiers[tier] = [...new Set(tiers[tier])].sort();
});
return tiers;
}
// Generate examples for arithmetic grammar
const examples = generateTieredExamples("./arithmetic-grammar.js");
console.log("Simple examples:", examples.simple.slice(0, 5));
console.log("Medium examples:", examples.medium.slice(0, 5));
console.log("Complex examples:", examples.complex.slice(0, 5));Generate text for grammars that use external lexers like moo.
Usage Examples:
const Unparse = require("nearley/lib/unparse");
const moo = require("moo");
// For grammars with lexers, ensure the generated text produces valid tokens
function generateWithLexer(grammarFile) {
const compiledGrammar = require(grammarFile);
// Generate text
const generated = Unparse(compiledGrammar, null, 6);
console.log("Generated text:", generated);
// Verify it can be lexed (if lexer is available)
if (compiledGrammar.Lexer) {
const lexer = compiledGrammar.Lexer;
lexer.reset(generated);
console.log("Tokens:");
let token;
while ((token = lexer.next())) {
console.log(` ${token.type}: "${token.value}"`);
}
}
return generated;
}
// Test with a moo-based grammar
generateWithLexer("./lexer-grammar.js");Use generation to test coverage of grammar rules.
Usage Examples:
const Unparse = require("nearley/lib/unparse");
// Test which grammar rules are being exercised
function testGrammarCoverage(grammarFile, numTests = 1000) {
const grammar = require(grammarFile);
const ruleCounts = new Map();
// Track rule usage during generation
for (let i = 0; i < numTests; i++) {
const generated = Unparse(grammar, null, 8);
// Parse the generated text to see which rules fired
const nearley = require("nearley");
const grammarObj = nearley.Grammar.fromCompiled(grammar);
const parser = new nearley.Parser(grammarObj);
try {
parser.feed(generated);
// Analyze parse table to count rule usage
if (parser.results.length > 0) {
// This is a simplified analysis - actual implementation
// would need to traverse parse trees to count rule usage
console.log(`Generated: "${generated}"`);
}
} catch (error) {
// Skip failed generations
}
}
return ruleCounts;
}
// Analyze grammar coverage
testGrammarCoverage("./my-grammar.js", 100);