ohm-js 16.3.4 → 16.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,2 @@
1
- var {makeRecipe} = require('../src/makeRecipe');
2
- module.exports = makeRecipe(["grammar",{"source":"BuiltInRules {\n\n alnum (an alpha-numeric character)\n = letter\n | digit\n\n letter (a letter)\n = lower\n | upper\n | unicodeLtmo\n\n digit (a digit)\n = \"0\"..\"9\"\n\n hexDigit (a hexadecimal digit)\n = digit\n | \"a\"..\"f\"\n | \"A\"..\"F\"\n\n ListOf<elem, sep>\n = NonemptyListOf<elem, sep>\n | EmptyListOf<elem, sep>\n\n NonemptyListOf<elem, sep>\n = elem (sep elem)*\n\n EmptyListOf<elem, sep>\n = /* nothing */\n\n listOf<elem, sep>\n = nonemptyListOf<elem, sep>\n | emptyListOf<elem, sep>\n\n nonemptyListOf<elem, sep>\n = elem (sep elem)*\n\n emptyListOf<elem, sep>\n = /* nothing */\n\n // Allows a syntactic rule application within a lexical context.\n applySyntactic<app> = app\n}"},"BuiltInRules",null,null,{"alnum":["define",{"sourceInterval":[18,78]},"an alpha-numeric character",[],["alt",{"sourceInterval":[60,78]},["app",{"sourceInterval":[60,66]},"letter",[]],["app",{"sourceInterval":[73,78]},"digit",[]]]],"letter":["define",{"sourceInterval":[82,142]},"a letter",[],["alt",{"sourceInterval":[107,142]},["app",{"sourceInterval":[107,112]},"lower",[]],["app",{"sourceInterval":[119,124]},"upper",[]],["app",{"sourceInterval":[131,142]},"unicodeLtmo",[]]]],"digit":["define",{"sourceInterval":[146,177]},"a digit",[],["range",{"sourceInterval":[169,177]},"0","9"]],"hexDigit":["define",{"sourceInterval":[181,254]},"a hexadecimal digit",[],["alt",{"sourceInterval":[219,254]},["app",{"sourceInterval":[219,224]},"digit",[]],["range",{"sourceInterval":[231,239]},"a","f"],["range",{"sourceInterval":[246,254]},"A","F"]]],"ListOf":["define",{"sourceInterval":[258,336]},null,["elem","sep"],["alt",{"sourceInterval":[282,336]},["app",{"sourceInterval":[282,307]},"NonemptyListOf",[["param",{"sourceInterval":[297,301]},0],["param",{"sourceInterval":[303,306]},1]]],["app",{"sourceInterval":[314,336]},"EmptyListOf",[["param",{"sourceInterval":[326,330]},0],["param",{"sourceInterval":[332,335]},1]]]]],"NonemptyListOf":["define",{"sourceInterval":[340,388]},null,["elem","sep"],["seq",{"sourceInterval":[372,388]},["param",{"sourceInterval":[372,376]},0],["star",{"sourceInterval":[377,388]},["seq",{"sourceInterval":[378,386]},["param",{"sourceInterval":[378,381]},1],["param",{"sourceInterval":[382,386]},0]]]]],"EmptyListOf":["define",{"sourceInterval":[392,434]},null,["elem","sep"],["seq",{"sourceInterval":[438,438]}]],"listOf":["define",{"sourceInterval":[438,516]},null,["elem","sep"],["alt",{"sourceInterval":[462,516]},["app",{"sourceInterval":[462,487]},"nonemptyListOf",[["param",{"sourceInterval":[477,481]},0],["param",{"sourceInterval":[483,486]},1]]],["app",{"sourceInterval":[494,516]},"emptyListOf",[["param",{"sourceInterval":[506,510]},0],["param",{"sourceInterval":[512,515]},1]]]]],"nonemptyListOf":["define",{"sourceInterval":[520,568]},null,["elem","sep"],["seq",{"sourceInterval":[552,568]},["param",{"sourceInterval":[552,556]},0],["star",{"sourceInterval":[557,568]},["seq",{"sourceInterval":[558,566]},["param",{"sourceInterval":[558,561]},1],["param",{"sourceInterval":[562,566]},0]]]]],"emptyListOf":["define",{"sourceInterval":[572,682]},null,["elem","sep"],["seq",{"sourceInterval":[685,685]}]],"applySyntactic":["define",{"sourceInterval":[685,710]},null,["app"],["param",{"sourceInterval":[707,710]},0]]}]);
1
+ import {makeRecipe} from '../src/makeRecipe.js';
2
+ export default makeRecipe(["grammar",{"source":"BuiltInRules {\n\n alnum (an alpha-numeric character)\n = letter\n | digit\n\n letter (a letter)\n = lower\n | upper\n | unicodeLtmo\n\n digit (a digit)\n = \"0\"..\"9\"\n\n hexDigit (a hexadecimal digit)\n = digit\n | \"a\"..\"f\"\n | \"A\"..\"F\"\n\n ListOf<elem, sep>\n = NonemptyListOf<elem, sep>\n | EmptyListOf<elem, sep>\n\n NonemptyListOf<elem, sep>\n = elem (sep elem)*\n\n EmptyListOf<elem, sep>\n = /* nothing */\n\n listOf<elem, sep>\n = nonemptyListOf<elem, sep>\n | emptyListOf<elem, sep>\n\n nonemptyListOf<elem, sep>\n = elem (sep elem)*\n\n emptyListOf<elem, sep>\n = /* nothing */\n\n // Allows a syntactic rule application within a lexical context.\n applySyntactic<app> = app\n}"},"BuiltInRules",null,null,{"alnum":["define",{"sourceInterval":[18,78]},"an alpha-numeric character",[],["alt",{"sourceInterval":[60,78]},["app",{"sourceInterval":[60,66]},"letter",[]],["app",{"sourceInterval":[73,78]},"digit",[]]]],"letter":["define",{"sourceInterval":[82,142]},"a letter",[],["alt",{"sourceInterval":[107,142]},["app",{"sourceInterval":[107,112]},"lower",[]],["app",{"sourceInterval":[119,124]},"upper",[]],["app",{"sourceInterval":[131,142]},"unicodeLtmo",[]]]],"digit":["define",{"sourceInterval":[146,177]},"a digit",[],["range",{"sourceInterval":[169,177]},"0","9"]],"hexDigit":["define",{"sourceInterval":[181,254]},"a hexadecimal digit",[],["alt",{"sourceInterval":[219,254]},["app",{"sourceInterval":[219,224]},"digit",[]],["range",{"sourceInterval":[231,239]},"a","f"],["range",{"sourceInterval":[246,254]},"A","F"]]],"ListOf":["define",{"sourceInterval":[258,336]},null,["elem","sep"],["alt",{"sourceInterval":[282,336]},["app",{"sourceInterval":[282,307]},"NonemptyListOf",[["param",{"sourceInterval":[297,301]},0],["param",{"sourceInterval":[303,306]},1]]],["app",{"sourceInterval":[314,336]},"EmptyListOf",[["param",{"sourceInterval":[326,330]},0],["param",{"sourceInterval":[332,335]},1]]]]],"NonemptyListOf":["define",{"sourceInterval":[340,388]},null,["elem","sep"],["seq",{"sourceInterval":[372,388]},["param",{"sourceInterval":[372,376]},0],["star",{"sourceInterval":[377,388]},["seq",{"sourceInterval":[378,386]},["param",{"sourceInterval":[378,381]},1],["param",{"sourceInterval":[382,386]},0]]]]],"EmptyListOf":["define",{"sourceInterval":[392,434]},null,["elem","sep"],["seq",{"sourceInterval":[438,438]}]],"listOf":["define",{"sourceInterval":[438,516]},null,["elem","sep"],["alt",{"sourceInterval":[462,516]},["app",{"sourceInterval":[462,487]},"nonemptyListOf",[["param",{"sourceInterval":[477,481]},0],["param",{"sourceInterval":[483,486]},1]]],["app",{"sourceInterval":[494,516]},"emptyListOf",[["param",{"sourceInterval":[506,510]},0],["param",{"sourceInterval":[512,515]},1]]]]],"nonemptyListOf":["define",{"sourceInterval":[520,568]},null,["elem","sep"],["seq",{"sourceInterval":[552,568]},["param",{"sourceInterval":[552,556]},0],["star",{"sourceInterval":[557,568]},["seq",{"sourceInterval":[558,566]},["param",{"sourceInterval":[558,561]},1],["param",{"sourceInterval":[562,566]},0]]]]],"emptyListOf":["define",{"sourceInterval":[572,682]},null,["elem","sep"],["seq",{"sourceInterval":[685,685]}]],"applySyntactic":["define",{"sourceInterval":[685,710]},null,["app"],["param",{"sourceInterval":[707,710]},0]]}]);
@@ -0,0 +1,653 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ // --------------------------------------------------------------------
6
+ // Private Stuff
7
+ // --------------------------------------------------------------------
8
+
9
+ // Helpers
10
+
11
+ const escapeStringFor = {};
12
+ for (let c = 0; c < 128; c++) {
13
+ escapeStringFor[c] = String.fromCharCode(c);
14
+ }
15
+ escapeStringFor["'".charCodeAt(0)] = "\\'";
16
+ escapeStringFor['"'.charCodeAt(0)] = '\\"';
17
+ escapeStringFor['\\'.charCodeAt(0)] = '\\\\';
18
+ escapeStringFor['\b'.charCodeAt(0)] = '\\b';
19
+ escapeStringFor['\f'.charCodeAt(0)] = '\\f';
20
+ escapeStringFor['\n'.charCodeAt(0)] = '\\n';
21
+ escapeStringFor['\r'.charCodeAt(0)] = '\\r';
22
+ escapeStringFor['\t'.charCodeAt(0)] = '\\t';
23
+ escapeStringFor['\u000b'.charCodeAt(0)] = '\\v';
24
+
25
+ // --------------------------------------------------------------------
26
+ // Exports
27
+ // --------------------------------------------------------------------
28
+
29
+ function abstract(optMethodName) {
30
+ const methodName = optMethodName || '';
31
+ return function() {
32
+ throw new Error(
33
+ 'this method ' +
34
+ methodName +
35
+ ' is abstract! ' +
36
+ '(it has no implementation in class ' +
37
+ this.constructor.name +
38
+ ')',
39
+ );
40
+ };
41
+ }
42
+
43
+ function assert(cond, message) {
44
+ if (!cond) {
45
+ throw new Error(message || 'Assertion failed');
46
+ }
47
+ }
48
+
49
+ // Define a lazily-computed, non-enumerable property named `propName`
50
+ // on the object `obj`. `getterFn` will be called to compute the value the
51
+ // first time the property is accessed.
52
+ function defineLazyProperty(obj, propName, getterFn) {
53
+ let memo;
54
+ Object.defineProperty(obj, propName, {
55
+ get() {
56
+ if (!memo) {
57
+ memo = getterFn.call(this);
58
+ }
59
+ return memo;
60
+ },
61
+ });
62
+ }
63
+
64
+ function clone(obj) {
65
+ if (obj) {
66
+ return Object.assign({}, obj);
67
+ }
68
+ return obj;
69
+ }
70
+
71
+ function repeatFn(fn, n) {
72
+ const arr = [];
73
+ while (n-- > 0) {
74
+ arr.push(fn());
75
+ }
76
+ return arr;
77
+ }
78
+
79
+ function repeatStr(str, n) {
80
+ return new Array(n + 1).join(str);
81
+ }
82
+
83
+ function repeat(x, n) {
84
+ return repeatFn(() => x, n);
85
+ }
86
+
87
+ function getDuplicates(array) {
88
+ const duplicates = [];
89
+ for (let idx = 0; idx < array.length; idx++) {
90
+ const x = array[idx];
91
+ if (array.lastIndexOf(x) !== idx && duplicates.indexOf(x) < 0) {
92
+ duplicates.push(x);
93
+ }
94
+ }
95
+ return duplicates;
96
+ }
97
+
98
+ function copyWithoutDuplicates(array) {
99
+ const noDuplicates = [];
100
+ array.forEach(entry => {
101
+ if (noDuplicates.indexOf(entry) < 0) {
102
+ noDuplicates.push(entry);
103
+ }
104
+ });
105
+ return noDuplicates;
106
+ }
107
+
108
+ function isSyntactic(ruleName) {
109
+ const firstChar = ruleName[0];
110
+ return firstChar === firstChar.toUpperCase();
111
+ }
112
+
113
+ function isLexical(ruleName) {
114
+ return !isSyntactic(ruleName);
115
+ }
116
+
117
+ function padLeft(str, len, optChar) {
118
+ const ch = optChar || ' ';
119
+ if (str.length < len) {
120
+ return repeatStr(ch, len - str.length) + str;
121
+ }
122
+ return str;
123
+ }
124
+
125
+ // StringBuffer
126
+
127
+ function StringBuffer() {
128
+ this.strings = [];
129
+ }
130
+
131
+ StringBuffer.prototype.append = function(str) {
132
+ this.strings.push(str);
133
+ };
134
+
135
+ StringBuffer.prototype.contents = function() {
136
+ return this.strings.join('');
137
+ };
138
+
139
+ const escapeUnicode = str => String.fromCodePoint(parseInt(str, 16));
140
+
141
+ function unescapeCodePoint(s) {
142
+ if (s.charAt(0) === '\\') {
143
+ switch (s.charAt(1)) {
144
+ case 'b':
145
+ return '\b';
146
+ case 'f':
147
+ return '\f';
148
+ case 'n':
149
+ return '\n';
150
+ case 'r':
151
+ return '\r';
152
+ case 't':
153
+ return '\t';
154
+ case 'v':
155
+ return '\v';
156
+ case 'x':
157
+ return escapeUnicode(s.slice(2, 4));
158
+ case 'u':
159
+ return s.charAt(2) === '{' ?
160
+ escapeUnicode(s.slice(3, -1)) :
161
+ escapeUnicode(s.slice(2, 6));
162
+ default:
163
+ return s.charAt(1);
164
+ }
165
+ } else {
166
+ return s;
167
+ }
168
+ }
169
+
170
+ // Helper for producing a description of an unknown object in a safe way.
171
+ // Especially useful for error messages where an unexpected type of object was encountered.
172
+ function unexpectedObjToString(obj) {
173
+ if (obj == null) {
174
+ return String(obj);
175
+ }
176
+ const baseToString = Object.prototype.toString.call(obj);
177
+ try {
178
+ let typeName;
179
+ if (obj.constructor && obj.constructor.name) {
180
+ typeName = obj.constructor.name;
181
+ } else if (baseToString.indexOf('[object ') === 0) {
182
+ typeName = baseToString.slice(8, -1); // Extract e.g. "Array" from "[object Array]".
183
+ } else {
184
+ typeName = typeof obj;
185
+ }
186
+ return typeName + ': ' + JSON.stringify(String(obj));
187
+ } catch (e) {
188
+ return baseToString;
189
+ }
190
+ }
191
+
192
+ var common = /*#__PURE__*/Object.freeze({
193
+ __proto__: null,
194
+ abstract: abstract,
195
+ assert: assert,
196
+ defineLazyProperty: defineLazyProperty,
197
+ clone: clone,
198
+ repeatFn: repeatFn,
199
+ repeatStr: repeatStr,
200
+ repeat: repeat,
201
+ getDuplicates: getDuplicates,
202
+ copyWithoutDuplicates: copyWithoutDuplicates,
203
+ isSyntactic: isSyntactic,
204
+ isLexical: isLexical,
205
+ padLeft: padLeft,
206
+ StringBuffer: StringBuffer,
207
+ unescapeCodePoint: unescapeCodePoint,
208
+ unexpectedObjToString: unexpectedObjToString
209
+ });
210
+
211
+ // Helpers
212
+
213
+ function getProp(name, thing, fn) {
214
+ return fn(thing[name]);
215
+ }
216
+
217
+ function mapProp(name, thing, fn) {
218
+ return thing[name].map(fn);
219
+ }
220
+
221
+ // Returns a function that will walk a single property of a node.
222
+ // `descriptor` is a string indicating the property name, optionally ending
223
+ // with '[]' (e.g., 'children[]').
224
+ function getPropWalkFn(descriptor) {
225
+ const parts = descriptor.split(/ ?\[\]/);
226
+ if (parts.length === 2) {
227
+ return mapProp.bind(null, parts[0]);
228
+ }
229
+ return getProp.bind(null, descriptor);
230
+ }
231
+
232
+ function getProps(walkFns, thing, fn) {
233
+ return walkFns.map(walkFn => walkFn(thing, fn));
234
+ }
235
+
236
+ function getWalkFn(shape) {
237
+ if (typeof shape === 'string') {
238
+ return getProps.bind(null, [getPropWalkFn(shape)]);
239
+ } else if (Array.isArray(shape)) {
240
+ return getProps.bind(null, shape.map(getPropWalkFn));
241
+ } else {
242
+ assert(typeof shape === 'function', 'Expected a string, Array, or function');
243
+ assert(shape.length === 2, 'Expected a function of arity 2, got ' + shape.length);
244
+ return shape;
245
+ }
246
+ }
247
+
248
+ function isRestrictedIdentifier(str) {
249
+ return /^[a-zA-Z_][0-9a-zA-Z_]*$/.test(str);
250
+ }
251
+
252
+ function trim(s) {
253
+ return s.trim();
254
+ }
255
+
256
+ function parseSignature(sig) {
257
+ const parts = sig.split(/[()]/).map(trim);
258
+ if (parts.length === 3 && parts[2] === '') {
259
+ const name = parts[0];
260
+ let params = [];
261
+ if (parts[1].length > 0) {
262
+ params = parts[1].split(',').map(trim);
263
+ }
264
+ if (isRestrictedIdentifier(name) && params.every(isRestrictedIdentifier)) {
265
+ return {name, formals: params};
266
+ }
267
+ }
268
+ throw new Error('Invalid operation signature: ' + sig);
269
+ }
270
+
271
+ /*
272
+ A VisitorFamily contains a set of recursive operations that are defined over some kind of
273
+ tree structure. The `config` parameter specifies how to walk the tree:
274
+ - 'getTag' is function which, given a node in the tree, returns the node's 'tag' (type)
275
+ - 'shapes' an object that maps from a tag to a value that describes how to recursively
276
+ evaluate the operation for nodes of that type. The value can be:
277
+ * a string indicating the property name that holds that node's only child
278
+ * an Array of property names (or an empty array indicating a leaf type), or
279
+ * a function taking two arguments (node, fn), and returning an Array which is the result
280
+ of apply `fn` to each of the node's children.
281
+ */
282
+ class VisitorFamily {
283
+ constructor(config) {
284
+ this._shapes = config.shapes;
285
+ this._getTag = config.getTag;
286
+
287
+ this.Adapter = function(thing, family) {
288
+ this._adaptee = thing;
289
+ this._family = family;
290
+ };
291
+ this.Adapter.prototype.valueOf = function() {
292
+ throw new Error('heeey!');
293
+ };
294
+ this.operations = {};
295
+
296
+ this._arities = Object.create(null);
297
+ this._getChildren = Object.create(null);
298
+
299
+ Object.keys(this._shapes).forEach(k => {
300
+ const shape = this._shapes[k];
301
+ this._getChildren[k] = getWalkFn(shape);
302
+
303
+ // A function means the arity isn't fixed, so don't put an entry in the arity map.
304
+ if (typeof shape !== 'function') {
305
+ this._arities[k] = Array.isArray(shape) ? shape.length : 1;
306
+ }
307
+ });
308
+ this._wrap = thing => new this.Adapter(thing, this);
309
+ }
310
+
311
+ wrap(thing) {
312
+ return this._wrap(thing);
313
+ }
314
+
315
+ _checkActionDict(dict) {
316
+ Object.keys(dict).forEach(k => {
317
+ assert(k in this._getChildren, "Unrecognized action name '" + k + "'");
318
+ const action = dict[k];
319
+ assert(
320
+ typeof action === 'function',
321
+ "Key '" + k + "': expected function, got " + action,
322
+ );
323
+ if (k in this._arities) {
324
+ const expected = this._arities[k];
325
+ const actual = dict[k].length;
326
+ assert(
327
+ actual === expected,
328
+ "Action '" + k + "' has the wrong arity: expected " + expected + ', got ' + actual,
329
+ );
330
+ }
331
+ });
332
+ }
333
+
334
+ addOperation(signature, actions) {
335
+ const sig = parseSignature(signature);
336
+ const {name} = sig;
337
+ this._checkActionDict(actions);
338
+ this.operations[name] = {
339
+ name,
340
+ formals: sig.formals,
341
+ actions,
342
+ };
343
+
344
+ const family = this;
345
+ this.Adapter.prototype[name] = function(...args) {
346
+ const tag = family._getTag(this._adaptee);
347
+ assert(tag in family._getChildren, "getTag returned unrecognized tag '" + tag + "'");
348
+ assert(tag in actions, "No action for '" + tag + "' in operation '" + name + "'");
349
+
350
+ // Create an "arguments object" from the arguments that were passed to this
351
+ // operation / attribute.
352
+ const argsObj = Object.create(null);
353
+ for (const [i, val] of Object.entries(args)) {
354
+ argsObj[sig.formals[i]] = val;
355
+ }
356
+
357
+ const oldArgs = this.args;
358
+ this.args = argsObj;
359
+ const ans = actions[tag].apply(
360
+ this,
361
+ family._getChildren[tag](this._adaptee, family._wrap),
362
+ );
363
+ this.args = oldArgs;
364
+ return ans;
365
+ };
366
+ return this;
367
+ }
368
+ }
369
+
370
+ function handleListOf(child) {
371
+ return child.toAST(this.args.mapping);
372
+ }
373
+
374
+ function handleEmptyListOf() {
375
+ return [];
376
+ }
377
+
378
+ function handleNonemptyListOf(first, sep, rest) {
379
+ return [first.toAST(this.args.mapping)].concat(rest.toAST(this.args.mapping));
380
+ }
381
+
382
+ const defaultMapping = {
383
+ listOf: handleListOf,
384
+ ListOf: handleListOf,
385
+
386
+ emptyListOf: handleEmptyListOf,
387
+ EmptyListOf: handleEmptyListOf,
388
+
389
+ nonemptyListOf: handleNonemptyListOf,
390
+ NonemptyListOf: handleNonemptyListOf,
391
+ };
392
+
393
+ const defaultOperation = {
394
+ _terminal() {
395
+ return this.sourceString;
396
+ },
397
+
398
+ _nonterminal(...children) {
399
+ const {ctorName} = this._node;
400
+ const {mapping} = this.args;
401
+
402
+ // without customization
403
+ if (!Object.prototype.hasOwnProperty.call(mapping, ctorName)) {
404
+ // lexical rule
405
+ if (this.isLexical()) {
406
+ return this.sourceString;
407
+ }
408
+
409
+ // singular node (e.g. only surrounded by literals or lookaheads)
410
+ const realChildren = children.filter(child => !child.isTerminal());
411
+ if (realChildren.length === 1) {
412
+ return realChildren[0].toAST(mapping);
413
+ }
414
+
415
+ // rest: terms with multiple children
416
+ }
417
+ // direct forward
418
+ if (typeof mapping[ctorName] === 'number') {
419
+ return children[mapping[ctorName]].toAST(mapping);
420
+ }
421
+
422
+ // named/mapped children or unnamed children ('0', '1', '2', ...)
423
+ const propMap = mapping[ctorName] || children;
424
+ const node = {
425
+ type: ctorName,
426
+ };
427
+ // eslint-disable-next-line guard-for-in
428
+ for (const prop in propMap) {
429
+ const mappedProp = mapping[ctorName] && mapping[ctorName][prop];
430
+ if (typeof mappedProp === 'number') {
431
+ // direct forward
432
+ node[prop] = children[mappedProp].toAST(mapping);
433
+ } else if (
434
+ typeof mappedProp === 'string' ||
435
+ typeof mappedProp === 'boolean' ||
436
+ mappedProp === null
437
+ ) {
438
+ // primitive value
439
+ node[prop] = mappedProp;
440
+ } else if (typeof mappedProp === 'object' && mappedProp instanceof Number) {
441
+ // primitive number (must be unboxed)
442
+ node[prop] = Number(mappedProp);
443
+ } else if (typeof mappedProp === 'function') {
444
+ // computed value
445
+ node[prop] = mappedProp.call(this, children);
446
+ } else if (mappedProp === undefined) {
447
+ if (children[prop] && !children[prop].isTerminal()) {
448
+ node[prop] = children[prop].toAST(mapping);
449
+ } else {
450
+ // delete predefined 'type' properties, like 'type', if explicitely removed
451
+ delete node[prop];
452
+ }
453
+ }
454
+ }
455
+ return node;
456
+ },
457
+
458
+ _iter(...children) {
459
+ if (this._node.isOptional()) {
460
+ if (this.numChildren === 0) {
461
+ return null;
462
+ } else {
463
+ return children[0].toAST(this.args.mapping);
464
+ }
465
+ }
466
+
467
+ return children.map(c => c.toAST(this.args.mapping));
468
+ },
469
+ };
470
+
471
+ // Returns a plain JavaScript object that includes an abstract syntax tree (AST)
472
+ // for the given match result `res` containg a concrete syntax tree (CST) and grammar.
473
+ // The optional `mapping` parameter can be used to customize how the nodes of the CST
474
+ // are mapped to the AST (see /doc/extras.md#toastmatchresult-mapping).
475
+ function toAST(res, mapping) {
476
+ if (typeof res.failed !== 'function' || res.failed()) {
477
+ throw new Error('toAST() expects a succesful MatchResult as first parameter');
478
+ }
479
+
480
+ mapping = Object.assign({}, defaultMapping, mapping);
481
+ const operation = Object.assign({}, defaultOperation);
482
+ for (const termName in mapping) {
483
+ if (typeof mapping[termName] === 'function') {
484
+ operation[termName] = mapping[termName];
485
+ delete mapping[termName];
486
+ }
487
+ }
488
+ const g = res._cst.grammar;
489
+ const s = g.createSemantics().addOperation('toAST(mapping)', operation);
490
+ return s(res).toAST(mapping);
491
+ }
492
+
493
+ // Returns a semantics containg the toAST(mapping) operation for the given grammar g.
494
+ function semanticsForToAST(g) {
495
+ if (typeof g.createSemantics !== 'function') {
496
+ throw new Error('semanticsToAST() expects a Grammar as parameter');
497
+ }
498
+
499
+ return g.createSemantics().addOperation('toAST(mapping)', defaultOperation);
500
+ }
501
+
502
+ // --------------------------------------------------------------------
503
+ // Private stuff
504
+ // --------------------------------------------------------------------
505
+
506
+ // Given an array of numbers `arr`, return an array of the numbers as strings,
507
+ // right-justified and padded to the same length.
508
+ function padNumbersToEqualLength(arr) {
509
+ let maxLen = 0;
510
+ const strings = arr.map(n => {
511
+ const str = n.toString();
512
+ maxLen = Math.max(maxLen, str.length);
513
+ return str;
514
+ });
515
+ return strings.map(s => padLeft(s, maxLen));
516
+ }
517
+
518
+ // Produce a new string that would be the result of copying the contents
519
+ // of the string `src` onto `dest` at offset `offest`.
520
+ function strcpy(dest, src, offset) {
521
+ const origDestLen = dest.length;
522
+ const start = dest.slice(0, offset);
523
+ const end = dest.slice(offset + src.length);
524
+ return (start + src + end).substr(0, origDestLen);
525
+ }
526
+
527
+ // Casts the underlying lineAndCol object to a formatted message string,
528
+ // highlighting `ranges`.
529
+ function lineAndColumnToMessage(...ranges) {
530
+ const lineAndCol = this;
531
+ const {offset} = lineAndCol;
532
+ const {repeatStr} = common;
533
+
534
+ const sb = new StringBuffer();
535
+ sb.append('Line ' + lineAndCol.lineNum + ', col ' + lineAndCol.colNum + ':\n');
536
+
537
+ // An array of the previous, current, and next line numbers as strings of equal length.
538
+ const lineNumbers = padNumbersToEqualLength([
539
+ lineAndCol.prevLine == null ? 0 : lineAndCol.lineNum - 1,
540
+ lineAndCol.lineNum,
541
+ lineAndCol.nextLine == null ? 0 : lineAndCol.lineNum + 1,
542
+ ]);
543
+
544
+ // Helper for appending formatting input lines to the buffer.
545
+ const appendLine = (num, content, prefix) => {
546
+ sb.append(prefix + lineNumbers[num] + ' | ' + content + '\n');
547
+ };
548
+
549
+ // Include the previous line for context if possible.
550
+ if (lineAndCol.prevLine != null) {
551
+ appendLine(0, lineAndCol.prevLine, ' ');
552
+ }
553
+ // Line that the error occurred on.
554
+ appendLine(1, lineAndCol.line, '> ');
555
+
556
+ // Build up the line that points to the offset and possible indicates one or more ranges.
557
+ // Start with a blank line, and indicate each range by overlaying a string of `~` chars.
558
+ const lineLen = lineAndCol.line.length;
559
+ let indicationLine = repeatStr(' ', lineLen + 1);
560
+ for (let i = 0; i < ranges.length; ++i) {
561
+ let startIdx = ranges[i][0];
562
+ let endIdx = ranges[i][1];
563
+ assert(startIdx >= 0 && startIdx <= endIdx, 'range start must be >= 0 and <= end');
564
+
565
+ const lineStartOffset = offset - lineAndCol.colNum + 1;
566
+ startIdx = Math.max(0, startIdx - lineStartOffset);
567
+ endIdx = Math.min(endIdx - lineStartOffset, lineLen);
568
+
569
+ indicationLine = strcpy(indicationLine, repeatStr('~', endIdx - startIdx), startIdx);
570
+ }
571
+ const gutterWidth = 2 + lineNumbers[1].length + 3;
572
+ sb.append(repeatStr(' ', gutterWidth));
573
+ indicationLine = strcpy(indicationLine, '^', lineAndCol.colNum - 1);
574
+ sb.append(indicationLine.replace(/ +$/, '') + '\n');
575
+
576
+ // Include the next line for context if possible.
577
+ if (lineAndCol.nextLine != null) {
578
+ appendLine(2, lineAndCol.nextLine, ' ');
579
+ }
580
+ return sb.contents();
581
+ }
582
+
583
+ // Return an object with the line and column information for the given
584
+ // offset in `str`.
585
+ function getLineAndColumn(str, offset) {
586
+ let lineNum = 1;
587
+ let colNum = 1;
588
+
589
+ let currOffset = 0;
590
+ let lineStartOffset = 0;
591
+
592
+ let nextLine = null;
593
+ let prevLine = null;
594
+ let prevLineStartOffset = -1;
595
+
596
+ while (currOffset < offset) {
597
+ const c = str.charAt(currOffset++);
598
+ if (c === '\n') {
599
+ lineNum++;
600
+ colNum = 1;
601
+ prevLineStartOffset = lineStartOffset;
602
+ lineStartOffset = currOffset;
603
+ } else if (c !== '\r') {
604
+ colNum++;
605
+ }
606
+ }
607
+
608
+ // Find the end of the target line.
609
+ let lineEndOffset = str.indexOf('\n', lineStartOffset);
610
+ if (lineEndOffset === -1) {
611
+ lineEndOffset = str.length;
612
+ } else {
613
+ // Get the next line.
614
+ const nextLineEndOffset = str.indexOf('\n', lineEndOffset + 1);
615
+ nextLine =
616
+ nextLineEndOffset === -1 ?
617
+ str.slice(lineEndOffset) :
618
+ str.slice(lineEndOffset, nextLineEndOffset);
619
+ // Strip leading and trailing EOL char(s).
620
+ nextLine = nextLine.replace(/^\r?\n/, '').replace(/\r$/, '');
621
+ }
622
+
623
+ // Get the previous line.
624
+ if (prevLineStartOffset >= 0) {
625
+ // Strip trailing EOL char(s).
626
+ prevLine = str.slice(prevLineStartOffset, lineStartOffset).replace(/\r?\n$/, '');
627
+ }
628
+
629
+ // Get the target line, stripping a trailing carriage return if necessary.
630
+ const line = str.slice(lineStartOffset, lineEndOffset).replace(/\r$/, '');
631
+
632
+ return {
633
+ offset,
634
+ lineNum,
635
+ colNum,
636
+ line,
637
+ prevLine,
638
+ nextLine,
639
+ toString: lineAndColumnToMessage,
640
+ };
641
+ }
642
+
643
+ // Return a nicely-formatted string describing the line and column for the
644
+ // given offset in `str` highlighting `ranges`.
645
+ function getLineAndColumnMessage(str, offset, ...ranges) {
646
+ return getLineAndColumn(str, offset).toString(...ranges);
647
+ }
648
+
649
+ exports.VisitorFamily = VisitorFamily;
650
+ exports.getLineAndColumn = getLineAndColumn;
651
+ exports.getLineAndColumnMessage = getLineAndColumnMessage;
652
+ exports.semanticsForToAST = semanticsForToAST;
653
+ exports.toAST = toAST;