esperanto-source 0.6.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,831 @@
1
+ /*
2
+ Copyright (C) 2012-2013 Yusuke Suzuki <utatane.tea@gmail.com>
3
+ Copyright (C) 2012 Ariya Hidayat <ariya.hidayat@gmail.com>
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ * Redistributions of source code must retain the above copyright
9
+ notice, this list of conditions and the following disclaimer.
10
+ * Redistributions in binary form must reproduce the above copyright
11
+ notice, this list of conditions and the following disclaimer in the
12
+ documentation and/or other materials provided with the distribution.
13
+
14
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
+ ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
18
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24
+ */
25
+ /*jslint vars:false, bitwise:true*/
26
+ /*jshint indent:4*/
27
+ /*global exports:true, define:true*/
28
+ (function (root, factory) {
29
+ 'use strict';
30
+
31
+ // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js,
32
+ // and plain browser loading,
33
+ if (typeof define === 'function' && define.amd) {
34
+ define(['exports'], factory);
35
+ } else if (typeof exports !== 'undefined') {
36
+ factory(exports);
37
+ } else {
38
+ factory((root.estraverse = {}));
39
+ }
40
+ }(this, function (exports) {
41
+ 'use strict';
42
+
43
+ var Syntax,
44
+ isArray,
45
+ VisitorOption,
46
+ VisitorKeys,
47
+ objectCreate,
48
+ objectKeys,
49
+ BREAK,
50
+ SKIP,
51
+ REMOVE;
52
+
53
+ function ignoreJSHintError() { }
54
+
55
+ isArray = Array.isArray;
56
+ if (!isArray) {
57
+ isArray = function isArray(array) {
58
+ return Object.prototype.toString.call(array) === '[object Array]';
59
+ };
60
+ }
61
+
62
+ function deepCopy(obj) {
63
+ var ret = {}, key, val;
64
+ for (key in obj) {
65
+ if (obj.hasOwnProperty(key)) {
66
+ val = obj[key];
67
+ if (typeof val === 'object' && val !== null) {
68
+ ret[key] = deepCopy(val);
69
+ } else {
70
+ ret[key] = val;
71
+ }
72
+ }
73
+ }
74
+ return ret;
75
+ }
76
+
77
+ function shallowCopy(obj) {
78
+ var ret = {}, key;
79
+ for (key in obj) {
80
+ if (obj.hasOwnProperty(key)) {
81
+ ret[key] = obj[key];
82
+ }
83
+ }
84
+ return ret;
85
+ }
86
+ ignoreJSHintError(shallowCopy);
87
+
88
+ // based on LLVM libc++ upper_bound / lower_bound
89
+ // MIT License
90
+
91
+ function upperBound(array, func) {
92
+ var diff, len, i, current;
93
+
94
+ len = array.length;
95
+ i = 0;
96
+
97
+ while (len) {
98
+ diff = len >>> 1;
99
+ current = i + diff;
100
+ if (func(array[current])) {
101
+ len = diff;
102
+ } else {
103
+ i = current + 1;
104
+ len -= diff + 1;
105
+ }
106
+ }
107
+ return i;
108
+ }
109
+
110
+ function lowerBound(array, func) {
111
+ var diff, len, i, current;
112
+
113
+ len = array.length;
114
+ i = 0;
115
+
116
+ while (len) {
117
+ diff = len >>> 1;
118
+ current = i + diff;
119
+ if (func(array[current])) {
120
+ i = current + 1;
121
+ len -= diff + 1;
122
+ } else {
123
+ len = diff;
124
+ }
125
+ }
126
+ return i;
127
+ }
128
+ ignoreJSHintError(lowerBound);
129
+
130
+ objectCreate = Object.create || (function () {
131
+ function F() { }
132
+
133
+ return function (o) {
134
+ F.prototype = o;
135
+ return new F();
136
+ };
137
+ })();
138
+
139
+ objectKeys = Object.keys || function (o) {
140
+ var keys = [], key;
141
+ for (key in o) {
142
+ keys.push(key);
143
+ }
144
+ return keys;
145
+ };
146
+
147
+ function extend(to, from) {
148
+ objectKeys(from).forEach(function (key) {
149
+ to[key] = from[key];
150
+ });
151
+ return to;
152
+ }
153
+
154
+ Syntax = {
155
+ AssignmentExpression: 'AssignmentExpression',
156
+ ArrayExpression: 'ArrayExpression',
157
+ ArrayPattern: 'ArrayPattern',
158
+ ArrowFunctionExpression: 'ArrowFunctionExpression',
159
+ BlockStatement: 'BlockStatement',
160
+ BinaryExpression: 'BinaryExpression',
161
+ BreakStatement: 'BreakStatement',
162
+ CallExpression: 'CallExpression',
163
+ CatchClause: 'CatchClause',
164
+ ClassBody: 'ClassBody',
165
+ ClassDeclaration: 'ClassDeclaration',
166
+ ClassExpression: 'ClassExpression',
167
+ ComprehensionBlock: 'ComprehensionBlock', // CAUTION: It's deferred to ES7.
168
+ ComprehensionExpression: 'ComprehensionExpression', // CAUTION: It's deferred to ES7.
169
+ ConditionalExpression: 'ConditionalExpression',
170
+ ContinueStatement: 'ContinueStatement',
171
+ DebuggerStatement: 'DebuggerStatement',
172
+ DirectiveStatement: 'DirectiveStatement',
173
+ DoWhileStatement: 'DoWhileStatement',
174
+ EmptyStatement: 'EmptyStatement',
175
+ ExportBatchSpecifier: 'ExportBatchSpecifier',
176
+ ExportDeclaration: 'ExportDeclaration',
177
+ ExportSpecifier: 'ExportSpecifier',
178
+ ExpressionStatement: 'ExpressionStatement',
179
+ ForStatement: 'ForStatement',
180
+ ForInStatement: 'ForInStatement',
181
+ ForOfStatement: 'ForOfStatement',
182
+ FunctionDeclaration: 'FunctionDeclaration',
183
+ FunctionExpression: 'FunctionExpression',
184
+ GeneratorExpression: 'GeneratorExpression', // CAUTION: It's deferred to ES7.
185
+ Identifier: 'Identifier',
186
+ IfStatement: 'IfStatement',
187
+ ImportDeclaration: 'ImportDeclaration',
188
+ ImportDefaultSpecifier: 'ImportDefaultSpecifier',
189
+ ImportNamespaceSpecifier: 'ImportNamespaceSpecifier',
190
+ ImportSpecifier: 'ImportSpecifier',
191
+ Literal: 'Literal',
192
+ LabeledStatement: 'LabeledStatement',
193
+ LogicalExpression: 'LogicalExpression',
194
+ MemberExpression: 'MemberExpression',
195
+ MethodDefinition: 'MethodDefinition',
196
+ ModuleSpecifier: 'ModuleSpecifier',
197
+ NewExpression: 'NewExpression',
198
+ ObjectExpression: 'ObjectExpression',
199
+ ObjectPattern: 'ObjectPattern',
200
+ Program: 'Program',
201
+ Property: 'Property',
202
+ ReturnStatement: 'ReturnStatement',
203
+ SequenceExpression: 'SequenceExpression',
204
+ SpreadElement: 'SpreadElement',
205
+ SwitchStatement: 'SwitchStatement',
206
+ SwitchCase: 'SwitchCase',
207
+ TaggedTemplateExpression: 'TaggedTemplateExpression',
208
+ TemplateElement: 'TemplateElement',
209
+ TemplateLiteral: 'TemplateLiteral',
210
+ ThisExpression: 'ThisExpression',
211
+ ThrowStatement: 'ThrowStatement',
212
+ TryStatement: 'TryStatement',
213
+ UnaryExpression: 'UnaryExpression',
214
+ UpdateExpression: 'UpdateExpression',
215
+ VariableDeclaration: 'VariableDeclaration',
216
+ VariableDeclarator: 'VariableDeclarator',
217
+ WhileStatement: 'WhileStatement',
218
+ WithStatement: 'WithStatement',
219
+ YieldExpression: 'YieldExpression'
220
+ };
221
+
222
+ VisitorKeys = {
223
+ AssignmentExpression: ['left', 'right'],
224
+ ArrayExpression: ['elements'],
225
+ ArrayPattern: ['elements'],
226
+ ArrowFunctionExpression: ['params', 'defaults', 'rest', 'body'],
227
+ BlockStatement: ['body'],
228
+ BinaryExpression: ['left', 'right'],
229
+ BreakStatement: ['label'],
230
+ CallExpression: ['callee', 'arguments'],
231
+ CatchClause: ['param', 'body'],
232
+ ClassBody: ['body'],
233
+ ClassDeclaration: ['id', 'body', 'superClass'],
234
+ ClassExpression: ['id', 'body', 'superClass'],
235
+ ComprehensionBlock: ['left', 'right'], // CAUTION: It's deferred to ES7.
236
+ ComprehensionExpression: ['blocks', 'filter', 'body'], // CAUTION: It's deferred to ES7.
237
+ ConditionalExpression: ['test', 'consequent', 'alternate'],
238
+ ContinueStatement: ['label'],
239
+ DebuggerStatement: [],
240
+ DirectiveStatement: [],
241
+ DoWhileStatement: ['body', 'test'],
242
+ EmptyStatement: [],
243
+ ExportBatchSpecifier: [],
244
+ ExportDeclaration: ['declaration', 'specifiers', 'source'],
245
+ ExportSpecifier: ['id', 'name'],
246
+ ExpressionStatement: ['expression'],
247
+ ForStatement: ['init', 'test', 'update', 'body'],
248
+ ForInStatement: ['left', 'right', 'body'],
249
+ ForOfStatement: ['left', 'right', 'body'],
250
+ FunctionDeclaration: ['id', 'params', 'defaults', 'rest', 'body'],
251
+ FunctionExpression: ['id', 'params', 'defaults', 'rest', 'body'],
252
+ GeneratorExpression: ['blocks', 'filter', 'body'], // CAUTION: It's deferred to ES7.
253
+ Identifier: [],
254
+ IfStatement: ['test', 'consequent', 'alternate'],
255
+ ImportDeclaration: ['specifiers', 'source'],
256
+ ImportDefaultSpecifier: ['id'],
257
+ ImportNamespaceSpecifier: ['id'],
258
+ ImportSpecifier: ['id', 'name'],
259
+ Literal: [],
260
+ LabeledStatement: ['label', 'body'],
261
+ LogicalExpression: ['left', 'right'],
262
+ MemberExpression: ['object', 'property'],
263
+ MethodDefinition: ['key', 'value'],
264
+ ModuleSpecifier: [],
265
+ NewExpression: ['callee', 'arguments'],
266
+ ObjectExpression: ['properties'],
267
+ ObjectPattern: ['properties'],
268
+ Program: ['body'],
269
+ Property: ['key', 'value'],
270
+ ReturnStatement: ['argument'],
271
+ SequenceExpression: ['expressions'],
272
+ SpreadElement: ['argument'],
273
+ SwitchStatement: ['discriminant', 'cases'],
274
+ SwitchCase: ['test', 'consequent'],
275
+ TaggedTemplateExpression: ['tag', 'quasi'],
276
+ TemplateElement: [],
277
+ TemplateLiteral: ['quasis', 'expressions'],
278
+ ThisExpression: [],
279
+ ThrowStatement: ['argument'],
280
+ TryStatement: ['block', 'handlers', 'handler', 'guardedHandlers', 'finalizer'],
281
+ UnaryExpression: ['argument'],
282
+ UpdateExpression: ['argument'],
283
+ VariableDeclaration: ['declarations'],
284
+ VariableDeclarator: ['id', 'init'],
285
+ WhileStatement: ['test', 'body'],
286
+ WithStatement: ['object', 'body'],
287
+ YieldExpression: ['argument']
288
+ };
289
+
290
+ // unique id
291
+ BREAK = {};
292
+ SKIP = {};
293
+ REMOVE = {};
294
+
295
+ VisitorOption = {
296
+ Break: BREAK,
297
+ Skip: SKIP,
298
+ Remove: REMOVE
299
+ };
300
+
301
+ function Reference(parent, key) {
302
+ this.parent = parent;
303
+ this.key = key;
304
+ }
305
+
306
+ Reference.prototype.replace = function replace(node) {
307
+ this.parent[this.key] = node;
308
+ };
309
+
310
+ Reference.prototype.remove = function remove() {
311
+ if (isArray(this.parent)) {
312
+ this.parent.splice(this.key, 1);
313
+ return true;
314
+ } else {
315
+ this.replace(null);
316
+ return false;
317
+ }
318
+ };
319
+
320
+ function Element(node, path, wrap, ref) {
321
+ this.node = node;
322
+ this.path = path;
323
+ this.wrap = wrap;
324
+ this.ref = ref;
325
+ }
326
+
327
+ function Controller() { }
328
+
329
+ // API:
330
+ // return property path array from root to current node
331
+ Controller.prototype.path = function path() {
332
+ var i, iz, j, jz, result, element;
333
+
334
+ function addToPath(result, path) {
335
+ if (isArray(path)) {
336
+ for (j = 0, jz = path.length; j < jz; ++j) {
337
+ result.push(path[j]);
338
+ }
339
+ } else {
340
+ result.push(path);
341
+ }
342
+ }
343
+
344
+ // root node
345
+ if (!this.__current.path) {
346
+ return null;
347
+ }
348
+
349
+ // first node is sentinel, second node is root element
350
+ result = [];
351
+ for (i = 2, iz = this.__leavelist.length; i < iz; ++i) {
352
+ element = this.__leavelist[i];
353
+ addToPath(result, element.path);
354
+ }
355
+ addToPath(result, this.__current.path);
356
+ return result;
357
+ };
358
+
359
+ // API:
360
+ // return array of parent elements
361
+ Controller.prototype.parents = function parents() {
362
+ var i, iz, result;
363
+
364
+ // first node is sentinel
365
+ result = [];
366
+ for (i = 1, iz = this.__leavelist.length; i < iz; ++i) {
367
+ result.push(this.__leavelist[i].node);
368
+ }
369
+
370
+ return result;
371
+ };
372
+
373
+ // API:
374
+ // return current node
375
+ Controller.prototype.current = function current() {
376
+ return this.__current.node;
377
+ };
378
+
379
+ Controller.prototype.__execute = function __execute(callback, element) {
380
+ var previous, result;
381
+
382
+ result = undefined;
383
+
384
+ previous = this.__current;
385
+ this.__current = element;
386
+ this.__state = null;
387
+ if (callback) {
388
+ result = callback.call(this, element.node, this.__leavelist[this.__leavelist.length - 1].node);
389
+ }
390
+ this.__current = previous;
391
+
392
+ return result;
393
+ };
394
+
395
+ // API:
396
+ // notify control skip / break
397
+ Controller.prototype.notify = function notify(flag) {
398
+ this.__state = flag;
399
+ };
400
+
401
+ // API:
402
+ // skip child nodes of current node
403
+ Controller.prototype.skip = function () {
404
+ this.notify(SKIP);
405
+ };
406
+
407
+ // API:
408
+ // break traversals
409
+ Controller.prototype['break'] = function () {
410
+ this.notify(BREAK);
411
+ };
412
+
413
+ // API:
414
+ // remove node
415
+ Controller.prototype.remove = function () {
416
+ this.notify(REMOVE);
417
+ };
418
+
419
+ Controller.prototype.__initialize = function(root, visitor) {
420
+ this.visitor = visitor;
421
+ this.root = root;
422
+ this.__worklist = [];
423
+ this.__leavelist = [];
424
+ this.__current = null;
425
+ this.__state = null;
426
+ this.__fallback = visitor.fallback === 'iteration';
427
+ this.__keys = VisitorKeys;
428
+ if (visitor.keys) {
429
+ this.__keys = extend(objectCreate(this.__keys), visitor.keys);
430
+ }
431
+ };
432
+
433
+ function isNode(node) {
434
+ if (node == null) {
435
+ return false;
436
+ }
437
+ return typeof node === 'object' && typeof node.type === 'string';
438
+ }
439
+
440
+ function isProperty(nodeType, key) {
441
+ return (nodeType === Syntax.ObjectExpression || nodeType === Syntax.ObjectPattern) && 'properties' === key;
442
+ }
443
+
444
+ Controller.prototype.traverse = function traverse(root, visitor) {
445
+ var worklist,
446
+ leavelist,
447
+ element,
448
+ node,
449
+ nodeType,
450
+ ret,
451
+ key,
452
+ current,
453
+ current2,
454
+ candidates,
455
+ candidate,
456
+ sentinel;
457
+
458
+ this.__initialize(root, visitor);
459
+
460
+ sentinel = {};
461
+
462
+ // reference
463
+ worklist = this.__worklist;
464
+ leavelist = this.__leavelist;
465
+
466
+ // initialize
467
+ worklist.push(new Element(root, null, null, null));
468
+ leavelist.push(new Element(null, null, null, null));
469
+
470
+ while (worklist.length) {
471
+ element = worklist.pop();
472
+
473
+ if (element === sentinel) {
474
+ element = leavelist.pop();
475
+
476
+ ret = this.__execute(visitor.leave, element);
477
+
478
+ if (this.__state === BREAK || ret === BREAK) {
479
+ return;
480
+ }
481
+ continue;
482
+ }
483
+
484
+ if (element.node) {
485
+
486
+ ret = this.__execute(visitor.enter, element);
487
+
488
+ if (this.__state === BREAK || ret === BREAK) {
489
+ return;
490
+ }
491
+
492
+ worklist.push(sentinel);
493
+ leavelist.push(element);
494
+
495
+ if (this.__state === SKIP || ret === SKIP) {
496
+ continue;
497
+ }
498
+
499
+ node = element.node;
500
+ nodeType = element.wrap || node.type;
501
+ candidates = this.__keys[nodeType];
502
+ if (!candidates) {
503
+ if (this.__fallback) {
504
+ candidates = objectKeys(node);
505
+ } else {
506
+ throw new Error('Unknown node type ' + nodeType + '.');
507
+ }
508
+ }
509
+
510
+ current = candidates.length;
511
+ while ((current -= 1) >= 0) {
512
+ key = candidates[current];
513
+ candidate = node[key];
514
+ if (!candidate) {
515
+ continue;
516
+ }
517
+
518
+ if (isArray(candidate)) {
519
+ current2 = candidate.length;
520
+ while ((current2 -= 1) >= 0) {
521
+ if (!candidate[current2]) {
522
+ continue;
523
+ }
524
+ if (isProperty(nodeType, candidates[current])) {
525
+ element = new Element(candidate[current2], [key, current2], 'Property', null);
526
+ } else if (isNode(candidate[current2])) {
527
+ element = new Element(candidate[current2], [key, current2], null, null);
528
+ } else {
529
+ continue;
530
+ }
531
+ worklist.push(element);
532
+ }
533
+ } else if (isNode(candidate)) {
534
+ worklist.push(new Element(candidate, key, null, null));
535
+ }
536
+ }
537
+ }
538
+ }
539
+ };
540
+
541
+ Controller.prototype.replace = function replace(root, visitor) {
542
+ function removeElem(element) {
543
+ var i,
544
+ key,
545
+ nextElem,
546
+ parent;
547
+
548
+ if (element.ref.remove()) {
549
+ // When the reference is an element of an array.
550
+ key = element.ref.key;
551
+ parent = element.ref.parent;
552
+
553
+ // If removed from array, then decrease following items' keys.
554
+ i = worklist.length;
555
+ while (i--) {
556
+ nextElem = worklist[i];
557
+ if (nextElem.ref && nextElem.ref.parent === parent) {
558
+ if (nextElem.ref.key < key) {
559
+ break;
560
+ }
561
+ --nextElem.ref.key;
562
+ }
563
+ }
564
+ }
565
+ }
566
+
567
+ var worklist,
568
+ leavelist,
569
+ node,
570
+ nodeType,
571
+ target,
572
+ element,
573
+ current,
574
+ current2,
575
+ candidates,
576
+ candidate,
577
+ sentinel,
578
+ outer,
579
+ key;
580
+
581
+ this.__initialize(root, visitor);
582
+
583
+ sentinel = {};
584
+
585
+ // reference
586
+ worklist = this.__worklist;
587
+ leavelist = this.__leavelist;
588
+
589
+ // initialize
590
+ outer = {
591
+ root: root
592
+ };
593
+ element = new Element(root, null, null, new Reference(outer, 'root'));
594
+ worklist.push(element);
595
+ leavelist.push(element);
596
+
597
+ while (worklist.length) {
598
+ element = worklist.pop();
599
+
600
+ if (element === sentinel) {
601
+ element = leavelist.pop();
602
+
603
+ target = this.__execute(visitor.leave, element);
604
+
605
+ // node may be replaced with null,
606
+ // so distinguish between undefined and null in this place
607
+ if (target !== undefined && target !== BREAK && target !== SKIP && target !== REMOVE) {
608
+ // replace
609
+ element.ref.replace(target);
610
+ }
611
+
612
+ if (this.__state === REMOVE || target === REMOVE) {
613
+ removeElem(element);
614
+ }
615
+
616
+ if (this.__state === BREAK || target === BREAK) {
617
+ return outer.root;
618
+ }
619
+ continue;
620
+ }
621
+
622
+ target = this.__execute(visitor.enter, element);
623
+
624
+ // node may be replaced with null,
625
+ // so distinguish between undefined and null in this place
626
+ if (target !== undefined && target !== BREAK && target !== SKIP && target !== REMOVE) {
627
+ // replace
628
+ element.ref.replace(target);
629
+ element.node = target;
630
+ }
631
+
632
+ if (this.__state === REMOVE || target === REMOVE) {
633
+ removeElem(element);
634
+ element.node = null;
635
+ }
636
+
637
+ if (this.__state === BREAK || target === BREAK) {
638
+ return outer.root;
639
+ }
640
+
641
+ // node may be null
642
+ node = element.node;
643
+ if (!node) {
644
+ continue;
645
+ }
646
+
647
+ worklist.push(sentinel);
648
+ leavelist.push(element);
649
+
650
+ if (this.__state === SKIP || target === SKIP) {
651
+ continue;
652
+ }
653
+
654
+ nodeType = element.wrap || node.type;
655
+ candidates = this.__keys[nodeType];
656
+ if (!candidates) {
657
+ if (this.__fallback) {
658
+ candidates = objectKeys(node);
659
+ } else {
660
+ throw new Error('Unknown node type ' + nodeType + '.');
661
+ }
662
+ }
663
+
664
+ current = candidates.length;
665
+ while ((current -= 1) >= 0) {
666
+ key = candidates[current];
667
+ candidate = node[key];
668
+ if (!candidate) {
669
+ continue;
670
+ }
671
+
672
+ if (isArray(candidate)) {
673
+ current2 = candidate.length;
674
+ while ((current2 -= 1) >= 0) {
675
+ if (!candidate[current2]) {
676
+ continue;
677
+ }
678
+ if (isProperty(nodeType, candidates[current])) {
679
+ element = new Element(candidate[current2], [key, current2], 'Property', new Reference(candidate, current2));
680
+ } else if (isNode(candidate[current2])) {
681
+ element = new Element(candidate[current2], [key, current2], null, new Reference(candidate, current2));
682
+ } else {
683
+ continue;
684
+ }
685
+ worklist.push(element);
686
+ }
687
+ } else if (isNode(candidate)) {
688
+ worklist.push(new Element(candidate, key, null, new Reference(node, key)));
689
+ }
690
+ }
691
+ }
692
+
693
+ return outer.root;
694
+ };
695
+
696
+ function traverse(root, visitor) {
697
+ var controller = new Controller();
698
+ return controller.traverse(root, visitor);
699
+ }
700
+
701
+ function replace(root, visitor) {
702
+ var controller = new Controller();
703
+ return controller.replace(root, visitor);
704
+ }
705
+
706
+ function extendCommentRange(comment, tokens) {
707
+ var target;
708
+
709
+ target = upperBound(tokens, function search(token) {
710
+ return token.range[0] > comment.range[0];
711
+ });
712
+
713
+ comment.extendedRange = [comment.range[0], comment.range[1]];
714
+
715
+ if (target !== tokens.length) {
716
+ comment.extendedRange[1] = tokens[target].range[0];
717
+ }
718
+
719
+ target -= 1;
720
+ if (target >= 0) {
721
+ comment.extendedRange[0] = tokens[target].range[1];
722
+ }
723
+
724
+ return comment;
725
+ }
726
+
727
+ function attachComments(tree, providedComments, tokens) {
728
+ // At first, we should calculate extended comment ranges.
729
+ var comments = [], comment, len, i, cursor;
730
+
731
+ if (!tree.range) {
732
+ throw new Error('attachComments needs range information');
733
+ }
734
+
735
+ // tokens array is empty, we attach comments to tree as 'leadingComments'
736
+ if (!tokens.length) {
737
+ if (providedComments.length) {
738
+ for (i = 0, len = providedComments.length; i < len; i += 1) {
739
+ comment = deepCopy(providedComments[i]);
740
+ comment.extendedRange = [0, tree.range[0]];
741
+ comments.push(comment);
742
+ }
743
+ tree.leadingComments = comments;
744
+ }
745
+ return tree;
746
+ }
747
+
748
+ for (i = 0, len = providedComments.length; i < len; i += 1) {
749
+ comments.push(extendCommentRange(deepCopy(providedComments[i]), tokens));
750
+ }
751
+
752
+ // This is based on John Freeman's implementation.
753
+ cursor = 0;
754
+ traverse(tree, {
755
+ enter: function (node) {
756
+ var comment;
757
+
758
+ while (cursor < comments.length) {
759
+ comment = comments[cursor];
760
+ if (comment.extendedRange[1] > node.range[0]) {
761
+ break;
762
+ }
763
+
764
+ if (comment.extendedRange[1] === node.range[0]) {
765
+ if (!node.leadingComments) {
766
+ node.leadingComments = [];
767
+ }
768
+ node.leadingComments.push(comment);
769
+ comments.splice(cursor, 1);
770
+ } else {
771
+ cursor += 1;
772
+ }
773
+ }
774
+
775
+ // already out of owned node
776
+ if (cursor === comments.length) {
777
+ return VisitorOption.Break;
778
+ }
779
+
780
+ if (comments[cursor].extendedRange[0] > node.range[1]) {
781
+ return VisitorOption.Skip;
782
+ }
783
+ }
784
+ });
785
+
786
+ cursor = 0;
787
+ traverse(tree, {
788
+ leave: function (node) {
789
+ var comment;
790
+
791
+ while (cursor < comments.length) {
792
+ comment = comments[cursor];
793
+ if (node.range[1] < comment.extendedRange[0]) {
794
+ break;
795
+ }
796
+
797
+ if (node.range[1] === comment.extendedRange[0]) {
798
+ if (!node.trailingComments) {
799
+ node.trailingComments = [];
800
+ }
801
+ node.trailingComments.push(comment);
802
+ comments.splice(cursor, 1);
803
+ } else {
804
+ cursor += 1;
805
+ }
806
+ }
807
+
808
+ // already out of owned node
809
+ if (cursor === comments.length) {
810
+ return VisitorOption.Break;
811
+ }
812
+
813
+ if (comments[cursor].extendedRange[0] > node.range[1]) {
814
+ return VisitorOption.Skip;
815
+ }
816
+ }
817
+ });
818
+
819
+ return tree;
820
+ }
821
+
822
+ exports.version = '1.7.1';
823
+ exports.Syntax = Syntax;
824
+ exports.traverse = traverse;
825
+ exports.replace = replace;
826
+ exports.attachComments = attachComments;
827
+ exports.VisitorKeys = VisitorKeys;
828
+ exports.VisitorOption = VisitorOption;
829
+ exports.Controller = Controller;
830
+ }));
831
+ /* vim: set sw=4 ts=4 et tw=80 : */