0
# Text Generation
1
2
Random text generation from grammars for testing, fuzzing, and example generation.
3
4
## Capabilities
5
6
### Unparse Function
7
8
Generate random text that matches a given grammar, useful for testing parsers and creating example inputs.
9
10
```javascript { .api }
11
/**
12
* Generate random text from a compiled grammar or Grammar instance
13
* @param grammar - Compiled grammar object (from nearleyc) or Grammar instance
14
* @param start - Start symbol name (optional, uses grammar default)
15
* @param depth - Maximum recursion depth (optional, null for unbounded)
16
* @returns Random text string matching the grammar
17
*/
18
function Unparse(grammar: object | Grammar, start?: string, depth?: number | null): string;
19
```
20
21
**Usage Examples:**
22
23
```javascript
24
const Unparse = require("nearley/lib/unparse");
25
26
// Load compiled grammar
27
const grammar = require("./arithmetic-grammar.js");
28
29
// Generate random arithmetic expressions
30
const randomExpr1 = Unparse(grammar);
31
console.log("Random expression:", randomExpr1);
32
// Example output: "42 + 17 * 3"
33
34
// Generate with specific start symbol
35
const randomExpr2 = Unparse(grammar, "factor");
36
console.log("Random factor:", randomExpr2);
37
// Example output: "123"
38
39
// Generate with depth limit to avoid infinite recursion
40
const boundedExpr = Unparse(grammar, "expr", 5);
41
console.log("Bounded expression:", boundedExpr);
42
// Example output: "8 + 2" (shorter due to depth limit)
43
```
44
45
### Unbounded Generation
46
47
Generate text without depth restrictions (may not terminate for recursive grammars).
48
49
```javascript { .api }
50
/**
51
* Generate unbounded random text (use with caution)
52
* @param grammar - Grammar instance or compiled grammar object
53
* @param start - Start symbol name
54
* @returns Random text string (may be very long or not terminate)
55
*/
56
function genRandom(grammar: Grammar, start: string): string;
57
```
58
59
**Usage Examples:**
60
61
```javascript
62
const { genRandom } = require("nearley/lib/unparse");
63
64
// Use only with grammars that naturally terminate
65
const terminalGrammar = require("./finite-grammar.js");
66
67
try {
68
// Generate without limits (risky with recursive grammars)
69
const result = genRandom(terminalGrammar, "sentence");
70
console.log("Generated:", result);
71
} catch (error) {
72
console.error("Generation failed:", error.message);
73
}
74
```
75
76
### Bounded Generation
77
78
Generate text with explicit depth bounds to guarantee termination.
79
80
```javascript { .api }
81
/**
82
* Generate bounded random text with depth limit
83
* @param grammar - Grammar instance or compiled grammar object
84
* @param start - Start symbol name
85
* @param depth - Maximum recursion depth
86
* @returns Random text string within depth bounds
87
*/
88
function genBounded(grammar: Grammar, start: string, depth: number): string;
89
```
90
91
**Usage Examples:**
92
93
```javascript
94
const { genBounded } = require("nearley/lib/unparse");
95
96
const grammar = require("./recursive-grammar.js");
97
98
// Generate with various depth limits
99
for (let depth = 1; depth <= 5; depth++) {
100
const result = genBounded(grammar, "expr", depth);
101
console.log(`Depth ${depth}: ${result}`);
102
}
103
104
// Example output:
105
// Depth 1: "x"
106
// Depth 2: "x + y"
107
// Depth 3: "(x + y) * z"
108
// Depth 4: "((x + y) * z) - (a + b)"
109
// Depth 5: "(((x + y) * z) - (a + b)) / ((c * d) + e)"
110
```
111
112
### Testing and Fuzzing
113
114
Use text generation for systematic testing of parsers.
115
116
**Usage Examples:**
117
118
```javascript
119
const nearley = require("nearley");
120
const Unparse = require("nearley/lib/unparse");
121
122
// Test parser with generated inputs
123
function fuzzTestParser(grammarFile, numTests = 100) {
124
const compiledGrammar = require(grammarFile);
125
const grammar = nearley.Grammar.fromCompiled(compiledGrammar);
126
127
let passed = 0;
128
let failed = 0;
129
130
for (let i = 0; i < numTests; i++) {
131
// Generate random input
132
const input = Unparse(compiledGrammar, null, 10);
133
134
try {
135
// Test if parser can parse generated input
136
const parser = new nearley.Parser(grammar);
137
parser.feed(input);
138
139
if (parser.results.length > 0) {
140
passed++;
141
console.log(`✓ Test ${i + 1}: "${input}"`);
142
} else {
143
failed++;
144
console.log(`✗ Test ${i + 1}: No parse for "${input}"`);
145
}
146
} catch (error) {
147
failed++;
148
console.log(`✗ Test ${i + 1}: Parse error for "${input}": ${error.message}`);
149
}
150
}
151
152
console.log(`\nResults: ${passed} passed, ${failed} failed`);
153
return { passed, failed };
154
}
155
156
// Run fuzz test
157
fuzzTestParser("./json-grammar.js", 50);
158
```
159
160
### Example Generation
161
162
Generate example inputs for documentation and tutorials.
163
164
**Usage Examples:**
165
166
```javascript
167
const Unparse = require("nearley/lib/unparse");
168
169
// Generate examples for documentation
170
function generateExamples(grammarFile, count = 10) {
171
const grammar = require(grammarFile);
172
const examples = new Set(); // Use Set to avoid duplicates
173
174
// Generate unique examples
175
while (examples.size < count) {
176
const example = Unparse(grammar, null, 8);
177
examples.add(example);
178
}
179
180
return Array.from(examples).sort();
181
}
182
183
// Generate examples for different complexity levels
184
function generateTieredExamples(grammarFile) {
185
const grammar = require(grammarFile);
186
187
const tiers = {
188
simple: [],
189
medium: [],
190
complex: []
191
};
192
193
// Generate examples at different depth levels
194
for (let i = 0; i < 20; i++) {
195
tiers.simple.push(Unparse(grammar, null, 2));
196
tiers.medium.push(Unparse(grammar, null, 5));
197
tiers.complex.push(Unparse(grammar, null, 10));
198
}
199
200
// Remove duplicates and sort
201
Object.keys(tiers).forEach(tier => {
202
tiers[tier] = [...new Set(tiers[tier])].sort();
203
});
204
205
return tiers;
206
}
207
208
// Generate examples for arithmetic grammar
209
const examples = generateTieredExamples("./arithmetic-grammar.js");
210
console.log("Simple examples:", examples.simple.slice(0, 5));
211
console.log("Medium examples:", examples.medium.slice(0, 5));
212
console.log("Complex examples:", examples.complex.slice(0, 5));
213
```
214
215
### Working with Lexer-based Grammars
216
217
Generate text for grammars that use external lexers like moo.
218
219
**Usage Examples:**
220
221
```javascript
222
const Unparse = require("nearley/lib/unparse");
223
const moo = require("moo");
224
225
// For grammars with lexers, ensure the generated text produces valid tokens
226
function generateWithLexer(grammarFile) {
227
const compiledGrammar = require(grammarFile);
228
229
// Generate text
230
const generated = Unparse(compiledGrammar, null, 6);
231
console.log("Generated text:", generated);
232
233
// Verify it can be lexed (if lexer is available)
234
if (compiledGrammar.Lexer) {
235
const lexer = compiledGrammar.Lexer;
236
lexer.reset(generated);
237
238
console.log("Tokens:");
239
let token;
240
while ((token = lexer.next())) {
241
console.log(` ${token.type}: "${token.value}"`);
242
}
243
}
244
245
return generated;
246
}
247
248
// Test with a moo-based grammar
249
generateWithLexer("./lexer-grammar.js");
250
```
251
252
### Debugging Grammar Coverage
253
254
Use generation to test coverage of grammar rules.
255
256
**Usage Examples:**
257
258
```javascript
259
const Unparse = require("nearley/lib/unparse");
260
261
// Test which grammar rules are being exercised
262
function testGrammarCoverage(grammarFile, numTests = 1000) {
263
const grammar = require(grammarFile);
264
const ruleCounts = new Map();
265
266
// Track rule usage during generation
267
for (let i = 0; i < numTests; i++) {
268
const generated = Unparse(grammar, null, 8);
269
270
// Parse the generated text to see which rules fired
271
const nearley = require("nearley");
272
const grammarObj = nearley.Grammar.fromCompiled(grammar);
273
const parser = new nearley.Parser(grammarObj);
274
275
try {
276
parser.feed(generated);
277
278
// Analyze parse table to count rule usage
279
if (parser.results.length > 0) {
280
// This is a simplified analysis - actual implementation
281
// would need to traverse parse trees to count rule usage
282
console.log(`Generated: "${generated}"`);
283
}
284
} catch (error) {
285
// Skip failed generations
286
}
287
}
288
289
return ruleCounts;
290
}
291
292
// Analyze grammar coverage
293
testGrammarCoverage("./my-grammar.js", 100);
294
```