0
# Compiler System
1
2
Multi-pass compiler architecture for transforming grammar ASTs into executable parser code with validation, optimization, and code generation capabilities.
3
4
## Capabilities
5
6
### Main Compiler Interface
7
8
Compiles grammar ASTs into executable parsers through configurable compilation passes.
9
10
```javascript { .api }
11
/**
12
* Compiler module interface
13
*/
14
const compiler = {
15
/**
16
* Compile grammar AST into executable parser
17
* @param ast - Grammar AST from parser
18
* @param passes - Compilation passes organized by stage
19
* @param options - Compilation options
20
* @returns Generated parser object or source code string
21
* @throws {GrammarError} If AST contains semantic errors
22
*/
23
compile: function(ast, passes, options),
24
25
/**
26
* AST visitor builder for traversing syntax trees
27
*/
28
visitor: {
29
build: function(functions)
30
},
31
32
/**
33
* Built-in compilation passes organized by stage
34
*/
35
passes: {
36
check: Object, // Validation passes
37
transform: Object, // AST transformation passes
38
generate: Object // Code generation passes
39
}
40
};
41
```
42
43
**Usage Examples:**
44
45
```javascript
46
const peg = require("pegjs");
47
48
// Parse grammar into AST
49
const ast = peg.parser.parse("start = 'hello'");
50
51
// Compile with default passes
52
const parser = peg.compiler.compile(
53
ast,
54
peg.compiler.passes,
55
{ output: "parser" }
56
);
57
58
// Compile with custom pass configuration
59
const customPasses = {
60
check: [
61
peg.compiler.passes.check.reportUndefinedRules,
62
peg.compiler.passes.check.reportDuplicateRules
63
],
64
transform: [
65
peg.compiler.passes.transform.removeProxyRules
66
],
67
generate: [
68
peg.compiler.passes.generate.generateBytecode,
69
peg.compiler.passes.generate.generateJS
70
]
71
};
72
73
const source = peg.compiler.compile(ast, customPasses, {
74
output: "source",
75
format: "commonjs"
76
});
77
```
78
79
### Compilation Options
80
81
Configuration options for the compilation process.
82
83
```javascript { .api }
84
/**
85
* Compilation options interface
86
*/
87
interface CompileOptions {
88
/** Rules parser can start from (default: [first rule]) */
89
allowedStartRules?: string[];
90
91
/** Enable result caching (default: false) */
92
cache?: boolean;
93
94
/** Module dependencies map (default: {}) */
95
dependencies?: { [key: string]: string };
96
97
/** Global variable name for globals/umd format (default: null) */
98
exportVar?: string;
99
100
/** Output format (default: "bare") */
101
format?: "amd" | "bare" | "commonjs" | "globals" | "umd";
102
103
/** Optimization target (default: "speed") */
104
optimize?: "speed" | "size";
105
106
/** Output type (default: "parser") */
107
output?: "parser" | "source";
108
109
/** Enable tracing (default: false) */
110
trace?: boolean;
111
}
112
```
113
114
## Compilation Passes
115
116
### Check Passes
117
118
Validation passes that detect semantic errors in grammar ASTs.
119
120
```javascript { .api }
121
/**
122
* Validation passes for semantic analysis
123
*/
124
const checkPasses = {
125
/**
126
* Report references to undefined rules
127
* @param ast - Grammar AST to analyze
128
* @param options - Compilation options
129
* @throws {GrammarError} If undefined rules found
130
*/
131
reportUndefinedRules: function(ast, options),
132
133
/**
134
* Report duplicate rule definitions
135
* @param ast - Grammar AST to analyze
136
* @param options - Compilation options
137
* @throws {GrammarError} If duplicate rules found
138
*/
139
reportDuplicateRules: function(ast, options),
140
141
/**
142
* Report duplicate labels within rules
143
* @param ast - Grammar AST to analyze
144
* @param options - Compilation options
145
* @throws {GrammarError} If duplicate labels found
146
*/
147
reportDuplicateLabels: function(ast, options),
148
149
/**
150
* Report infinite recursion patterns
151
* @param ast - Grammar AST to analyze
152
* @param options - Compilation options
153
* @throws {GrammarError} If infinite recursion detected
154
*/
155
reportInfiniteRecursion: function(ast, options),
156
157
/**
158
* Report infinite repetition patterns
159
* @param ast - Grammar AST to analyze
160
* @param options - Compilation options
161
* @throws {GrammarError} If infinite repetition detected
162
*/
163
reportInfiniteRepetition: function(ast, options)
164
};
165
```
166
167
**Check Pass Example:**
168
169
```javascript
170
// These passes will throw GrammarError if issues are found
171
try {
172
peg.compiler.passes.check.reportUndefinedRules(ast, options);
173
peg.compiler.passes.check.reportDuplicateRules(ast, options);
174
} catch (error) {
175
if (error.name === "GrammarError") {
176
console.error("Grammar validation error:", error.message);
177
}
178
}
179
```
180
181
### Transform Passes
182
183
AST transformation passes that optimize or modify the syntax tree.
184
185
```javascript { .api }
186
/**
187
* AST transformation passes
188
*/
189
const transformPasses = {
190
/**
191
* Remove proxy rules that just forward to other rules
192
* Optimizes the AST by eliminating unnecessary indirection
193
* @param ast - Grammar AST to transform
194
* @param options - Compilation options
195
*/
196
removeProxyRules: function(ast, options)
197
};
198
```
199
200
**Transform Pass Example:**
201
202
```javascript
203
// Grammar with proxy rule:
204
// start = expression
205
// expression = number
206
// number = [0-9]+
207
208
// After removeProxyRules:
209
// start = number
210
// number = [0-9]+
211
// (expression rule removed as it just forwards to number)
212
213
peg.compiler.passes.transform.removeProxyRules(ast, options);
214
```
215
216
### Generate Passes
217
218
Code generation passes that produce executable parser code.
219
220
```javascript { .api }
221
/**
222
* Code generation passes
223
*/
224
const generatePasses = {
225
/**
226
* Generate intermediate bytecode representation
227
* First stage of code generation process
228
* @param ast - Grammar AST to compile
229
* @param options - Compilation options
230
*/
231
generateBytecode: function(ast, options),
232
233
/**
234
* Generate final JavaScript parser code
235
* Second stage that converts bytecode to JavaScript
236
* @param ast - Grammar AST with bytecode
237
* @param options - Compilation options
238
*/
239
generateJS: function(ast, options)
240
};
241
```
242
243
**Generate Pass Example:**
244
245
```javascript
246
// Two-stage generation process
247
peg.compiler.passes.generate.generateBytecode(ast, options);
248
peg.compiler.passes.generate.generateJS(ast, options);
249
250
// After generation, ast.code contains the JavaScript parser source
251
console.log(ast.code); // Generated parser code
252
```
253
254
## AST Visitor System
255
256
### Visitor Builder
257
258
Creates visitor functions for traversing and manipulating ASTs.
259
260
```javascript { .api }
261
/**
262
* AST visitor builder
263
*/
264
const visitor = {
265
/**
266
* Build visitor function from handler map
267
* @param functions - Map of node types to handler functions
268
* @returns Visitor function for traversing ASTs
269
*/
270
build: function(functions): (node: ASTNode) => any
271
};
272
273
/**
274
* Visitor function interface
275
*/
276
interface VisitorFunction {
277
(node: ASTNode, ...extraArgs: any[]): any;
278
}
279
280
/**
281
* Handler function map for different node types
282
*/
283
interface VisitorHandlers {
284
grammar?: (node: GrammarNode, ...args: any[]) => any;
285
initializer?: (node: InitializerNode, ...args: any[]) => any;
286
rule?: (node: RuleNode, ...args: any[]) => any;
287
choice?: (node: ChoiceNode, ...args: any[]) => any;
288
sequence?: (node: SequenceNode, ...args: any[]) => any;
289
labeled?: (node: LabeledNode, ...args: any[]) => any;
290
action?: (node: ActionNode, ...args: any[]) => any;
291
optional?: (node: OptionalNode, ...args: any[]) => any;
292
zero_or_more?: (node: ZeroOrMoreNode, ...args: any[]) => any;
293
one_or_more?: (node: OneOrMoreNode, ...args: any[]) => any;
294
simple_and?: (node: SimpleAndNode, ...args: any[]) => any;
295
simple_not?: (node: SimpleNotNode, ...args: any[]) => any;
296
semantic_and?: (node: SemanticAndNode, ...args: any[]) => any;
297
semantic_not?: (node: SemanticNotNode, ...args: any[]) => any;
298
rule_ref?: (node: RuleRefNode, ...args: any[]) => any;
299
literal?: (node: LiteralNode, ...args: any[]) => any;
300
class?: (node: ClassNode, ...args: any[]) => any;
301
any?: (node: AnyNode, ...args: any[]) => any;
302
text?: (node: TextNode, ...args: any[]) => any;
303
named?: (node: NamedNode, ...args: any[]) => any;
304
group?: (node: GroupNode, ...args: any[]) => any;
305
}
306
```
307
308
**Visitor Usage Examples:**
309
310
```javascript
311
// Create a visitor that counts rule references
312
const countRefs = peg.compiler.visitor.build({
313
rule_ref: function(node, counts) {
314
counts[node.name] = (counts[node.name] || 0) + 1;
315
}
316
});
317
318
const refCounts = {};
319
countRefs(ast, refCounts);
320
console.log(refCounts); // { ruleName: count, ... }
321
322
// Create a visitor that collects all string literals
323
const collectLiterals = peg.compiler.visitor.build({
324
literal: function(node, literals) {
325
literals.push(node.value);
326
}
327
});
328
329
const literals = [];
330
collectLiterals(ast, literals);
331
console.log(literals); // ["string1", "string2", ...]
332
333
// Create a visitor that transforms nodes
334
const transformRefs = peg.compiler.visitor.build({
335
rule_ref: function(node) {
336
// Transform rule references to uppercase
337
node.name = node.name.toUpperCase();
338
}
339
});
340
341
transformRefs(ast); // Modifies AST in place
342
```
343
344
### Default Visitor Behavior
345
346
The visitor builder provides default traversal behavior for all node types:
347
348
```javascript
349
// Default handlers automatically traverse child nodes
350
// You only need to override specific node types you want to handle
351
352
const visitor = peg.compiler.visitor.build({
353
// Only handle literals, all other nodes traversed automatically
354
literal: function(node) {
355
console.log("Found literal:", node.value);
356
}
357
});
358
```
359
360
## Custom Compilation Passes
361
362
### Creating Custom Passes
363
364
You can create custom compilation passes for specialized validation or transformation:
365
366
```javascript
367
// Custom validation pass
368
function reportLongRuleNames(ast, options) {
369
const visitor = peg.compiler.visitor.build({
370
rule: function(node) {
371
if (node.name.length > 20) {
372
throw new peg.GrammarError(
373
`Rule name too long: ${node.name}`,
374
node.location
375
);
376
}
377
}
378
});
379
380
visitor(ast);
381
}
382
383
// Custom transformation pass
384
function prefixRuleNames(ast, options) {
385
const prefix = options.rulePrefix || "rule_";
386
387
const visitor = peg.compiler.visitor.build({
388
rule: function(node) {
389
if (!node.name.startsWith(prefix)) {
390
node.name = prefix + node.name;
391
}
392
},
393
rule_ref: function(node) {
394
if (!node.name.startsWith(prefix)) {
395
node.name = prefix + node.name;
396
}
397
}
398
});
399
400
visitor(ast);
401
}
402
403
// Use custom passes
404
const customPasses = {
405
check: [
406
peg.compiler.passes.check.reportUndefinedRules,
407
reportLongRuleNames // Custom validation
408
],
409
transform: [
410
prefixRuleNames, // Custom transformation
411
peg.compiler.passes.transform.removeProxyRules
412
],
413
generate: [
414
peg.compiler.passes.generate.generateBytecode,
415
peg.compiler.passes.generate.generateJS
416
]
417
};
418
419
const parser = peg.compiler.compile(ast, customPasses, {
420
rulePrefix: "my_",
421
output: "parser"
422
});
423
```