0
# AST Compilation
1
2
Advanced compilation system with multiple passes for transforming, validating, and optimizing grammar ASTs. The `compiler` namespace provides low-level access to Peggy's compilation pipeline and pass system.
3
4
## Capabilities
5
6
### Compile Function
7
8
Compiles grammar ASTs into parser objects or source code through a multi-stage pass system.
9
10
```typescript { .api }
11
namespace compiler {
12
/**
13
* Compiles AST to parser object (default)
14
* @param ast - Grammar AST from parser
15
* @param stages - Compilation stages with passes
16
* @param options - Build options
17
* @returns Generated parser object
18
* @throws {GrammarError} If AST contains semantic errors
19
*/
20
function compile(
21
ast: ast.Grammar,
22
stages: Stages,
23
options?: ParserBuildOptions
24
): Parser;
25
26
/**
27
* Compiles AST to source code string
28
* @param ast - Grammar AST from parser
29
* @param stages - Compilation stages with passes
30
* @param options - Source build options
31
* @returns Generated parser source code
32
*/
33
function compile(
34
ast: ast.Grammar,
35
stages: Stages,
36
options: SourceBuildOptions<"source">
37
): string;
38
39
/**
40
* Compiles AST to SourceNode with source map
41
* @param ast - Grammar AST from parser
42
* @param stages - Compilation stages with passes
43
* @param options - Source build options
44
* @returns SourceNode for code and source map generation
45
*/
46
function compile(
47
ast: ast.Grammar,
48
stages: Stages,
49
options: SourceBuildOptions<"source-and-map">
50
): SourceNode;
51
}
52
```
53
54
**Usage Examples:**
55
56
```typescript
57
import { parser, compiler } from "peggy";
58
59
// Parse grammar to AST
60
const grammar = `start = "hello" " " name:[a-z]+ { return "Hello, " + name.join(""); }`;
61
const ast = parser.parse(grammar, { reservedWords: [] });
62
63
// Compile AST to parser using default passes
64
const parser1 = compiler.compile(ast, compiler.passes);
65
66
// Compile to source code
67
const sourceCode = compiler.compile(ast, compiler.passes, {
68
output: "source",
69
format: "commonjs"
70
});
71
72
// Compile with custom options
73
const cachedParser = compiler.compile(ast, compiler.passes, {
74
cache: true,
75
allowedStartRules: ["start"],
76
trace: true
77
});
78
```
79
80
### Compilation Stages
81
82
Multi-stage compilation pipeline with different types of passes for grammar processing.
83
84
```typescript { .api }
85
namespace compiler {
86
/**
87
* Compilation stages with their passes
88
*/
89
interface Stages {
90
/** Preparation passes for linking and setup */
91
prepare: Pass[];
92
/** Validation passes for error checking */
93
check: Pass[];
94
/** AST transformation passes */
95
transform: Pass[];
96
/** Code generation passes */
97
generate: Pass[];
98
}
99
100
/**
101
* Default compilation stages
102
*/
103
let passes: Stages;
104
105
/**
106
* Compilation pass function
107
* @param ast - Grammar AST to process (may be modified)
108
* @param options - Build options from generate() call
109
* @param session - Session for error/warning reporting
110
*/
111
type Pass = (
112
ast: ast.Grammar,
113
options: ParserBuildOptions,
114
session: Session
115
) => void;
116
}
117
```
118
119
**Default Passes:**
120
121
The default `compiler.passes` object includes these built-in passes:
122
123
```typescript
124
// Prepare stage
125
const prepare = [
126
"addImportedRules", // Add rules from imported grammars
127
"reportInfiniteRecursion" // Detect infinite recursion
128
];
129
130
// Check stage
131
const check = [
132
"reportUndefinedRules", // Check for undefined rule references
133
"reportDuplicateRules", // Check for duplicate rule definitions
134
"reportDuplicateLabels", // Check for duplicate labels in rules
135
"reportInfiniteRepetition", // Check for infinite repetition
136
"reportIncorrectPlucking", // Check for incorrect @ usage
137
"reportDuplicateImports" // Check for duplicate import statements
138
];
139
140
// Transform stage
141
const transform = [
142
"fixLibraryNumbers", // Fix library reference numbers
143
"removeProxyRules", // Remove unnecessary proxy rules
144
"mergeCharacterClasses", // Optimize character classes
145
"removeUnusedRules", // Remove unreferenced rules
146
"inferenceMatchResult" // Infer match results for expressions
147
];
148
149
// Generate stage
150
const generate = [
151
"generateBytecode", // Generate bytecode for expressions
152
"generateJS" // Generate JavaScript parser code
153
];
154
```
155
156
### Compilation Session
157
158
Session object for pass execution that provides error reporting and diagnostic capabilities.
159
160
```typescript { .api }
161
namespace compiler {
162
/**
163
* Compilation session for pass execution
164
*/
165
interface Session {
166
/** All problems registered during compilation */
167
problems: Problem[];
168
169
/**
170
* Report compilation error
171
* @param message - Error description
172
* @param location - Source location if applicable
173
* @param notes - Additional diagnostic information
174
*/
175
error(
176
message: string,
177
location?: LocationRange,
178
notes?: DiagnosticNote[]
179
): void;
180
181
/**
182
* Report compilation warning
183
* @param message - Warning description
184
* @param location - Source location if applicable
185
* @param notes - Additional diagnostic information
186
*/
187
warning(
188
message: string,
189
location?: LocationRange,
190
notes?: DiagnosticNote[]
191
): void;
192
193
/**
194
* Report informational message
195
* @param message - Information description
196
* @param location - Source location if applicable
197
* @param notes - Additional diagnostic information
198
*/
199
info(
200
message: string,
201
location?: LocationRange,
202
notes?: DiagnosticNote[]
203
): void;
204
}
205
206
/**
207
* Problem reported during compilation
208
*/
209
type Problem = [
210
severity: "error" | "info" | "warning",
211
message: string,
212
location?: LocationRange,
213
diagnostics?: DiagnosticNote[]
214
];
215
216
/**
217
* Additional diagnostic note with location
218
*/
219
interface DiagnosticNote {
220
message: string;
221
location: LocationRange;
222
}
223
}
224
```
225
226
**Custom Pass Example:**
227
228
```typescript
229
import { parser, compiler } from "peggy";
230
231
// Custom pass that logs rule information
232
const logRulesPass = (ast, options, session) => {
233
session.info(`Processing ${ast.rules.length} rules`);
234
235
ast.rules.forEach(rule => {
236
if (rule.name.startsWith("_")) {
237
session.warning(`Private rule detected: ${rule.name}`, rule.nameLocation);
238
}
239
});
240
};
241
242
// Create custom stages with additional pass
243
const customStages = {
244
...compiler.passes,
245
check: [...compiler.passes.check, logRulesPass]
246
};
247
248
// Use custom stages
249
const ast = parser.parse(grammar, { reservedWords: [] });
250
const parser1 = compiler.compile(ast, customStages, {
251
info: (stage, message) => console.log(`[${stage}] ${message}`),
252
warning: (stage, message, location) => console.warn(`Warning: ${message}`)
253
});
254
```
255
256
### AST Visitor System
257
258
Visitor pattern implementation for traversing and manipulating grammar ASTs.
259
260
```typescript { .api }
261
namespace compiler {
262
namespace visitor {
263
/**
264
* Visitor function builder
265
* @param functions - Object with visitor functions for node types
266
* @returns Visitor function for AST traversal
267
*/
268
function build<F extends NodeTypes>(functions: F): Visitor<F>;
269
270
/**
271
* Visitor function for AST nodes
272
*/
273
interface Visitor<F extends NodeTypes> {
274
/**
275
* Visit AST node with type-specific visitor
276
* @param node - AST node to visit
277
* @param args - Additional arguments for visitor
278
* @returns Result from visitor function
279
*/
280
<T extends keyof NodeTypes>(
281
node: ast.Node<T>,
282
...args: any[]
283
): ReturnType<AnyFunction & F[T]>;
284
}
285
286
/**
287
* Visitor functions for different node types
288
*/
289
interface NodeTypes {
290
/** Visit grammar root */
291
grammar?(node: ast.Grammar, ...args: any[]): any;
292
/** Visit import statement */
293
grammar_import?(node: ast.GrammarImport, ...args: any[]): any;
294
/** Visit top-level initializer */
295
top_level_initializer?(node: ast.TopLevelInitializer, ...args: any[]): any;
296
/** Visit per-parse initializer */
297
initializer?(node: ast.Initializer, ...args: any[]): any;
298
/** Visit rule */
299
rule?(node: ast.Rule, ...args: any[]): any;
300
/** Visit named expression */
301
named?(node: ast.Named, ...args: any[]): any;
302
/** Visit choice expression */
303
choice?(node: ast.Choice, ...args: any[]): any;
304
/** Visit action expression */
305
action?(node: ast.Action, ...args: any[]): any;
306
/** Visit sequence expression */
307
sequence?(node: ast.Sequence, ...args: any[]): any;
308
/** Visit labeled expression */
309
labeled?(node: ast.Labeled, ...args: any[]): any;
310
/** Visit text expression */
311
text?(node: ast.Prefixed, ...args: any[]): any;
312
/** Visit positive lookahead */
313
simple_and?(node: ast.Prefixed, ...args: any[]): any;
314
/** Visit negative lookahead */
315
simple_not?(node: ast.Prefixed, ...args: any[]): any;
316
/** Visit optional expression */
317
optional?(node: ast.Suffixed, ...args: any[]): any;
318
/** Visit zero-or-more expression */
319
zero_or_more?(node: ast.Suffixed, ...args: any[]): any;
320
/** Visit one-or-more expression */
321
one_or_more?(node: ast.Suffixed, ...args: any[]): any;
322
/** Visit repeated expression */
323
repeated?(node: ast.Repeated, ...args: any[]): any;
324
/** Visit group expression */
325
group?(node: ast.Group, ...args: any[]): any;
326
/** Visit semantic AND predicate */
327
semantic_and?(node: ast.SemanticPredicate, ...args: any[]): any;
328
/** Visit semantic NOT predicate */
329
semantic_not?(node: ast.SemanticPredicate, ...args: any[]): any;
330
/** Visit rule reference */
331
rule_ref?(node: ast.RuleReference, ...args: any[]): any;
332
/** Visit library reference */
333
library_ref?(node: ast.LibraryReference, ...args: any[]): any;
334
/** Visit literal */
335
literal?(node: ast.Literal, ...args: any[]): any;
336
/** Visit character class */
337
class?(node: ast.CharacterClass, ...args: any[]): any;
338
/** Visit any-character */
339
any?(node: ast.Any, ...args: any[]): any;
340
}
341
}
342
}
343
```
344
345
**Visitor Usage Example:**
346
347
```typescript
348
import { parser, compiler } from "peggy";
349
350
// Create visitor that counts different expression types
351
const expressionCounter = compiler.visitor.build({
352
rule(node, counts) {
353
counts.rules++;
354
return this(node.expression, counts);
355
},
356
357
literal(node, counts) {
358
counts.literals++;
359
},
360
361
class(node, counts) {
362
counts.classes++;
363
},
364
365
rule_ref(node, counts) {
366
counts.references++;
367
},
368
369
choice(node, counts) {
370
counts.choices++;
371
node.alternatives.forEach(alt => this(alt, counts));
372
},
373
374
sequence(node, counts) {
375
counts.sequences++;
376
node.elements.forEach(elem => this(elem, counts));
377
}
378
});
379
380
// Use visitor on parsed grammar
381
const ast = parser.parse(complexGrammar, { reservedWords: [] });
382
const counts = {
383
rules: 0,
384
literals: 0,
385
classes: 0,
386
references: 0,
387
choices: 0,
388
sequences: 0
389
};
390
391
expressionCounter(ast, counts);
392
console.log("Expression counts:", counts);
393
```
394
395
### AST Manipulation with Visitors
396
397
The visitor system allows efficient traversal and manipulation of grammar ASTs with type-safe node visiting.
398
399
```typescript { .api }
400
namespace compiler {
401
namespace visitor {
402
/**
403
* Build visitor function from node type handlers
404
* @param functions - Object mapping node types to visitor functions
405
* @returns Visitor function for AST traversal
406
*/
407
function build<F extends NodeTypes>(functions: F): Visitor<F>;
408
409
/**
410
* Visitor function interface
411
*/
412
interface Visitor<F extends NodeTypes> {
413
/**
414
* Visit AST node with appropriate handler
415
* @param node - AST node to visit
416
* @param args - Additional arguments for visitor
417
* @returns Result from node handler
418
*/
419
<T extends keyof NodeTypes>(
420
node: ast.Node<T>,
421
...args: any[]
422
): ReturnType<AnyFunction & F[T]>;
423
}
424
425
/**
426
* Node type visitor functions
427
*/
428
interface NodeTypes {
429
/** Visit grammar root - default: visit imports, initializers, rules */
430
grammar?(node: ast.Grammar, ...args: any[]): any;
431
/** Visit import statement - default: do nothing */
432
grammar_import?(node: ast.GrammarImport, ...args: any[]): any;
433
/** Visit top-level initializer - default: do nothing */
434
top_level_initializer?(node: ast.TopLevelInitializer, ...args: any[]): any;
435
/** Visit per-parse initializer - default: do nothing */
436
initializer?(node: ast.Initializer, ...args: any[]): any;
437
/** Visit rule - default: visit expression */
438
rule?(node: ast.Rule, ...args: any[]): any;
439
/** Visit named expression - default: visit expression */
440
named?(node: ast.Named, ...args: any[]): any;
441
/** Visit choice - default: visit all alternatives */
442
choice?(node: ast.Choice, ...args: any[]): any;
443
/** Visit action - default: visit expression */
444
action?(node: ast.Action, ...args: any[]): any;
445
/** Visit sequence - default: visit all elements */
446
sequence?(node: ast.Sequence, ...args: any[]): any;
447
/** Visit labeled expression - default: visit expression */
448
labeled?(node: ast.Labeled, ...args: any[]): any;
449
/** Visit text operator - default: visit expression */
450
text?(node: ast.Prefixed, ...args: any[]): any;
451
/** Visit positive lookahead - default: visit expression */
452
simple_and?(node: ast.Prefixed, ...args: any[]): any;
453
/** Visit negative lookahead - default: visit expression */
454
simple_not?(node: ast.Prefixed, ...args: any[]): any;
455
/** Visit optional - default: visit expression */
456
optional?(node: ast.Suffixed, ...args: any[]): any;
457
/** Visit zero-or-more - default: visit expression */
458
zero_or_more?(node: ast.Suffixed, ...args: any[]): any;
459
/** Visit one-or-more - default: visit expression */
460
one_or_more?(node: ast.Suffixed, ...args: any[]): any;
461
/** Visit repeated - default: visit delimiter then expression */
462
repeated?(node: ast.Repeated, ...args: any[]): any;
463
/** Visit group - default: visit expression */
464
group?(node: ast.Group, ...args: any[]): any;
465
/** Visit semantic AND predicate - default: do nothing */
466
semantic_and?(node: ast.SemanticPredicate, ...args: any[]): any;
467
/** Visit semantic NOT predicate - default: do nothing */
468
semantic_not?(node: ast.SemanticPredicate, ...args: any[]): any;
469
/** Visit rule reference - default: do nothing */
470
rule_ref?(node: ast.RuleReference, ...args: any[]): any;
471
/** Visit library reference - default: do nothing */
472
library_ref?(node: ast.LibraryReference, ...args: any[]): any;
473
/** Visit literal - default: do nothing */
474
literal?(node: ast.Literal, ...args: any[]): any;
475
/** Visit character class - default: do nothing */
476
class?(node: ast.CharacterClass, ...args: any[]): any;
477
/** Visit any-character - default: do nothing */
478
any?(node: ast.Any, ...args: any[]): any;
479
}
480
481
type AnyFunction = (...args: any[]) => any;
482
}
483
}
484
```
485
486
### Advanced Compilation Options
487
488
Extended options for controlling specific aspects of the compilation process.
489
490
```typescript { .api }
491
/**
492
* Stage name type
493
*/
494
type Stage = keyof Stages;
495
496
/**
497
* Diagnostic callback for compilation messages
498
*/
499
type DiagnosticCallback = (
500
stage: Stage,
501
message: string,
502
location?: LocationRange,
503
notes?: DiagnosticNote[]
504
) => void;
505
506
/**
507
* Parser dependencies mapping for module systems
508
*/
509
interface Dependencies {
510
[variable: string]: string;
511
}
512
513
/**
514
* Source output format types
515
*/
516
type SourceOutputs = "parser" | "source-and-map" | "source-with-inline-map" | "source" | "ast";
517
```
518
519
**Advanced Compilation Example:**
520
521
```typescript
522
import { parser, compiler, GrammarError } from "peggy";
523
524
try {
525
const ast = parser.parse(grammar, { reservedWords: [] });
526
527
// Custom compilation with detailed diagnostics
528
const result = compiler.compile(ast, compiler.passes, {
529
output: "source-and-map",
530
format: "es",
531
dependencies: {
532
"validator": "./validator.js"
533
},
534
grammarSource: "my-grammar.peggy",
535
cache: true,
536
trace: true,
537
allowedStartRules: ["*"], // All rules as start rules
538
error: (stage, message, location, notes) => {
539
console.error(`[${stage}] ERROR: ${message}`);
540
if (location) {
541
console.error(` at line ${location.start.line}, column ${location.start.column}`);
542
}
543
notes?.forEach(note => {
544
console.error(` note: ${note.message}`);
545
});
546
},
547
warning: (stage, message, location) => {
548
console.warn(`[${stage}] WARNING: ${message}`);
549
},
550
info: (stage, message) => {
551
console.log(`[${stage}] ${message}`);
552
}
553
});
554
555
if (typeof result === "string") {
556
console.log("Generated source code");
557
} else {
558
// SourceNode
559
const { code, map } = result.toStringWithSourceMap({
560
file: "generated-parser.js"
561
});
562
console.log("Generated code with source map");
563
}
564
565
} catch (error) {
566
if (error instanceof GrammarError) {
567
console.error("Compilation failed:");
568
error.problems.forEach(([severity, message, location]) => {
569
console.error(`${severity.toUpperCase()}: ${message}`);
570
});
571
}
572
}
573
```