Projekt

Obecné

Profil

Stáhnout (25.6 KB) Statistiky
| Větev: | Revize:
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*/
28
(function clone(exports) {
29
    'use strict';
30

    
31
    var Syntax,
32
        VisitorOption,
33
        VisitorKeys,
34
        BREAK,
35
        SKIP,
36
        REMOVE;
37

    
38
    function deepCopy(obj) {
39
        var ret = {}, key, val;
40
        for (key in obj) {
41
            if (obj.hasOwnProperty(key)) {
42
                val = obj[key];
43
                if (typeof val === 'object' && val !== null) {
44
                    ret[key] = deepCopy(val);
45
                } else {
46
                    ret[key] = val;
47
                }
48
            }
49
        }
50
        return ret;
51
    }
52

    
53
    // based on LLVM libc++ upper_bound / lower_bound
54
    // MIT License
55

    
56
    function upperBound(array, func) {
57
        var diff, len, i, current;
58

    
59
        len = array.length;
60
        i = 0;
61

    
62
        while (len) {
63
            diff = len >>> 1;
64
            current = i + diff;
65
            if (func(array[current])) {
66
                len = diff;
67
            } else {
68
                i = current + 1;
69
                len -= diff + 1;
70
            }
71
        }
72
        return i;
73
    }
74

    
75
    Syntax = {
76
        AssignmentExpression: 'AssignmentExpression',
77
        AssignmentPattern: 'AssignmentPattern',
78
        ArrayExpression: 'ArrayExpression',
79
        ArrayPattern: 'ArrayPattern',
80
        ArrowFunctionExpression: 'ArrowFunctionExpression',
81
        AwaitExpression: 'AwaitExpression', // CAUTION: It's deferred to ES7.
82
        BlockStatement: 'BlockStatement',
83
        BinaryExpression: 'BinaryExpression',
84
        BreakStatement: 'BreakStatement',
85
        CallExpression: 'CallExpression',
86
        CatchClause: 'CatchClause',
87
        ClassBody: 'ClassBody',
88
        ClassDeclaration: 'ClassDeclaration',
89
        ClassExpression: 'ClassExpression',
90
        ComprehensionBlock: 'ComprehensionBlock',  // CAUTION: It's deferred to ES7.
91
        ComprehensionExpression: 'ComprehensionExpression',  // CAUTION: It's deferred to ES7.
92
        ConditionalExpression: 'ConditionalExpression',
93
        ContinueStatement: 'ContinueStatement',
94
        DebuggerStatement: 'DebuggerStatement',
95
        DirectiveStatement: 'DirectiveStatement',
96
        DoWhileStatement: 'DoWhileStatement',
97
        EmptyStatement: 'EmptyStatement',
98
        ExportAllDeclaration: 'ExportAllDeclaration',
99
        ExportDefaultDeclaration: 'ExportDefaultDeclaration',
100
        ExportNamedDeclaration: 'ExportNamedDeclaration',
101
        ExportSpecifier: 'ExportSpecifier',
102
        ExpressionStatement: 'ExpressionStatement',
103
        ForStatement: 'ForStatement',
104
        ForInStatement: 'ForInStatement',
105
        ForOfStatement: 'ForOfStatement',
106
        FunctionDeclaration: 'FunctionDeclaration',
107
        FunctionExpression: 'FunctionExpression',
108
        GeneratorExpression: 'GeneratorExpression',  // CAUTION: It's deferred to ES7.
109
        Identifier: 'Identifier',
110
        IfStatement: 'IfStatement',
111
        ImportExpression: 'ImportExpression',
112
        ImportDeclaration: 'ImportDeclaration',
113
        ImportDefaultSpecifier: 'ImportDefaultSpecifier',
114
        ImportNamespaceSpecifier: 'ImportNamespaceSpecifier',
115
        ImportSpecifier: 'ImportSpecifier',
116
        Literal: 'Literal',
117
        LabeledStatement: 'LabeledStatement',
118
        LogicalExpression: 'LogicalExpression',
119
        MemberExpression: 'MemberExpression',
120
        MetaProperty: 'MetaProperty',
121
        MethodDefinition: 'MethodDefinition',
122
        ModuleSpecifier: 'ModuleSpecifier',
123
        NewExpression: 'NewExpression',
124
        ObjectExpression: 'ObjectExpression',
125
        ObjectPattern: 'ObjectPattern',
126
        Program: 'Program',
127
        Property: 'Property',
128
        RestElement: 'RestElement',
129
        ReturnStatement: 'ReturnStatement',
130
        SequenceExpression: 'SequenceExpression',
131
        SpreadElement: 'SpreadElement',
132
        Super: 'Super',
133
        SwitchStatement: 'SwitchStatement',
134
        SwitchCase: 'SwitchCase',
135
        TaggedTemplateExpression: 'TaggedTemplateExpression',
136
        TemplateElement: 'TemplateElement',
137
        TemplateLiteral: 'TemplateLiteral',
138
        ThisExpression: 'ThisExpression',
139
        ThrowStatement: 'ThrowStatement',
140
        TryStatement: 'TryStatement',
141
        UnaryExpression: 'UnaryExpression',
142
        UpdateExpression: 'UpdateExpression',
143
        VariableDeclaration: 'VariableDeclaration',
144
        VariableDeclarator: 'VariableDeclarator',
145
        WhileStatement: 'WhileStatement',
146
        WithStatement: 'WithStatement',
147
        YieldExpression: 'YieldExpression'
148
    };
149

    
150
    VisitorKeys = {
151
        AssignmentExpression: ['left', 'right'],
152
        AssignmentPattern: ['left', 'right'],
153
        ArrayExpression: ['elements'],
154
        ArrayPattern: ['elements'],
155
        ArrowFunctionExpression: ['params', 'body'],
156
        AwaitExpression: ['argument'], // CAUTION: It's deferred to ES7.
157
        BlockStatement: ['body'],
158
        BinaryExpression: ['left', 'right'],
159
        BreakStatement: ['label'],
160
        CallExpression: ['callee', 'arguments'],
161
        CatchClause: ['param', 'body'],
162
        ClassBody: ['body'],
163
        ClassDeclaration: ['id', 'superClass', 'body'],
164
        ClassExpression: ['id', 'superClass', 'body'],
165
        ComprehensionBlock: ['left', 'right'],  // CAUTION: It's deferred to ES7.
166
        ComprehensionExpression: ['blocks', 'filter', 'body'],  // CAUTION: It's deferred to ES7.
167
        ConditionalExpression: ['test', 'consequent', 'alternate'],
168
        ContinueStatement: ['label'],
169
        DebuggerStatement: [],
170
        DirectiveStatement: [],
171
        DoWhileStatement: ['body', 'test'],
172
        EmptyStatement: [],
173
        ExportAllDeclaration: ['source'],
174
        ExportDefaultDeclaration: ['declaration'],
175
        ExportNamedDeclaration: ['declaration', 'specifiers', 'source'],
176
        ExportSpecifier: ['exported', 'local'],
177
        ExpressionStatement: ['expression'],
178
        ForStatement: ['init', 'test', 'update', 'body'],
179
        ForInStatement: ['left', 'right', 'body'],
180
        ForOfStatement: ['left', 'right', 'body'],
181
        FunctionDeclaration: ['id', 'params', 'body'],
182
        FunctionExpression: ['id', 'params', 'body'],
183
        GeneratorExpression: ['blocks', 'filter', 'body'],  // CAUTION: It's deferred to ES7.
184
        Identifier: [],
185
        IfStatement: ['test', 'consequent', 'alternate'],
186
        ImportExpression: ['source'],
187
        ImportDeclaration: ['specifiers', 'source'],
188
        ImportDefaultSpecifier: ['local'],
189
        ImportNamespaceSpecifier: ['local'],
190
        ImportSpecifier: ['imported', 'local'],
191
        Literal: [],
192
        LabeledStatement: ['label', 'body'],
193
        LogicalExpression: ['left', 'right'],
194
        MemberExpression: ['object', 'property'],
195
        MetaProperty: ['meta', 'property'],
196
        MethodDefinition: ['key', 'value'],
197
        ModuleSpecifier: [],
198
        NewExpression: ['callee', 'arguments'],
199
        ObjectExpression: ['properties'],
200
        ObjectPattern: ['properties'],
201
        Program: ['body'],
202
        Property: ['key', 'value'],
203
        RestElement: [ 'argument' ],
204
        ReturnStatement: ['argument'],
205
        SequenceExpression: ['expressions'],
206
        SpreadElement: ['argument'],
207
        Super: [],
208
        SwitchStatement: ['discriminant', 'cases'],
209
        SwitchCase: ['test', 'consequent'],
210
        TaggedTemplateExpression: ['tag', 'quasi'],
211
        TemplateElement: [],
212
        TemplateLiteral: ['quasis', 'expressions'],
213
        ThisExpression: [],
214
        ThrowStatement: ['argument'],
215
        TryStatement: ['block', 'handler', 'finalizer'],
216
        UnaryExpression: ['argument'],
217
        UpdateExpression: ['argument'],
218
        VariableDeclaration: ['declarations'],
219
        VariableDeclarator: ['id', 'init'],
220
        WhileStatement: ['test', 'body'],
221
        WithStatement: ['object', 'body'],
222
        YieldExpression: ['argument']
223
    };
224

    
225
    // unique id
226
    BREAK = {};
227
    SKIP = {};
228
    REMOVE = {};
229

    
230
    VisitorOption = {
231
        Break: BREAK,
232
        Skip: SKIP,
233
        Remove: REMOVE
234
    };
235

    
236
    function Reference(parent, key) {
237
        this.parent = parent;
238
        this.key = key;
239
    }
240

    
241
    Reference.prototype.replace = function replace(node) {
242
        this.parent[this.key] = node;
243
    };
244

    
245
    Reference.prototype.remove = function remove() {
246
        if (Array.isArray(this.parent)) {
247
            this.parent.splice(this.key, 1);
248
            return true;
249
        } else {
250
            this.replace(null);
251
            return false;
252
        }
253
    };
254

    
255
    function Element(node, path, wrap, ref) {
256
        this.node = node;
257
        this.path = path;
258
        this.wrap = wrap;
259
        this.ref = ref;
260
    }
261

    
262
    function Controller() { }
263

    
264
    // API:
265
    // return property path array from root to current node
266
    Controller.prototype.path = function path() {
267
        var i, iz, j, jz, result, element;
268

    
269
        function addToPath(result, path) {
270
            if (Array.isArray(path)) {
271
                for (j = 0, jz = path.length; j < jz; ++j) {
272
                    result.push(path[j]);
273
                }
274
            } else {
275
                result.push(path);
276
            }
277
        }
278

    
279
        // root node
280
        if (!this.__current.path) {
281
            return null;
282
        }
283

    
284
        // first node is sentinel, second node is root element
285
        result = [];
286
        for (i = 2, iz = this.__leavelist.length; i < iz; ++i) {
287
            element = this.__leavelist[i];
288
            addToPath(result, element.path);
289
        }
290
        addToPath(result, this.__current.path);
291
        return result;
292
    };
293

    
294
    // API:
295
    // return type of current node
296
    Controller.prototype.type = function () {
297
        var node = this.current();
298
        return node.type || this.__current.wrap;
299
    };
300

    
301
    // API:
302
    // return array of parent elements
303
    Controller.prototype.parents = function parents() {
304
        var i, iz, result;
305

    
306
        // first node is sentinel
307
        result = [];
308
        for (i = 1, iz = this.__leavelist.length; i < iz; ++i) {
309
            result.push(this.__leavelist[i].node);
310
        }
311

    
312
        return result;
313
    };
314

    
315
    // API:
316
    // return current node
317
    Controller.prototype.current = function current() {
318
        return this.__current.node;
319
    };
320

    
321
    Controller.prototype.__execute = function __execute(callback, element) {
322
        var previous, result;
323

    
324
        result = undefined;
325

    
326
        previous  = this.__current;
327
        this.__current = element;
328
        this.__state = null;
329
        if (callback) {
330
            result = callback.call(this, element.node, this.__leavelist[this.__leavelist.length - 1].node);
331
        }
332
        this.__current = previous;
333

    
334
        return result;
335
    };
336

    
337
    // API:
338
    // notify control skip / break
339
    Controller.prototype.notify = function notify(flag) {
340
        this.__state = flag;
341
    };
342

    
343
    // API:
344
    // skip child nodes of current node
345
    Controller.prototype.skip = function () {
346
        this.notify(SKIP);
347
    };
348

    
349
    // API:
350
    // break traversals
351
    Controller.prototype['break'] = function () {
352
        this.notify(BREAK);
353
    };
354

    
355
    // API:
356
    // remove node
357
    Controller.prototype.remove = function () {
358
        this.notify(REMOVE);
359
    };
360

    
361
    Controller.prototype.__initialize = function(root, visitor) {
362
        this.visitor = visitor;
363
        this.root = root;
364
        this.__worklist = [];
365
        this.__leavelist = [];
366
        this.__current = null;
367
        this.__state = null;
368
        this.__fallback = null;
369
        if (visitor.fallback === 'iteration') {
370
            this.__fallback = Object.keys;
371
        } else if (typeof visitor.fallback === 'function') {
372
            this.__fallback = visitor.fallback;
373
        }
374

    
375
        this.__keys = VisitorKeys;
376
        if (visitor.keys) {
377
            this.__keys = Object.assign(Object.create(this.__keys), visitor.keys);
378
        }
379
    };
380

    
381
    function isNode(node) {
382
        if (node == null) {
383
            return false;
384
        }
385
        return typeof node === 'object' && typeof node.type === 'string';
386
    }
387

    
388
    function isProperty(nodeType, key) {
389
        return (nodeType === Syntax.ObjectExpression || nodeType === Syntax.ObjectPattern) && 'properties' === key;
390
    }
391

    
392
    Controller.prototype.traverse = function traverse(root, visitor) {
393
        var worklist,
394
            leavelist,
395
            element,
396
            node,
397
            nodeType,
398
            ret,
399
            key,
400
            current,
401
            current2,
402
            candidates,
403
            candidate,
404
            sentinel;
405

    
406
        this.__initialize(root, visitor);
407

    
408
        sentinel = {};
409

    
410
        // reference
411
        worklist = this.__worklist;
412
        leavelist = this.__leavelist;
413

    
414
        // initialize
415
        worklist.push(new Element(root, null, null, null));
416
        leavelist.push(new Element(null, null, null, null));
417

    
418
        while (worklist.length) {
419
            element = worklist.pop();
420

    
421
            if (element === sentinel) {
422
                element = leavelist.pop();
423

    
424
                ret = this.__execute(visitor.leave, element);
425

    
426
                if (this.__state === BREAK || ret === BREAK) {
427
                    return;
428
                }
429
                continue;
430
            }
431

    
432
            if (element.node) {
433

    
434
                ret = this.__execute(visitor.enter, element);
435

    
436
                if (this.__state === BREAK || ret === BREAK) {
437
                    return;
438
                }
439

    
440
                worklist.push(sentinel);
441
                leavelist.push(element);
442

    
443
                if (this.__state === SKIP || ret === SKIP) {
444
                    continue;
445
                }
446

    
447
                node = element.node;
448
                nodeType = node.type || element.wrap;
449
                candidates = this.__keys[nodeType];
450
                if (!candidates) {
451
                    if (this.__fallback) {
452
                        candidates = this.__fallback(node);
453
                    } else {
454
                        throw new Error('Unknown node type ' + nodeType + '.');
455
                    }
456
                }
457

    
458
                current = candidates.length;
459
                while ((current -= 1) >= 0) {
460
                    key = candidates[current];
461
                    candidate = node[key];
462
                    if (!candidate) {
463
                        continue;
464
                    }
465

    
466
                    if (Array.isArray(candidate)) {
467
                        current2 = candidate.length;
468
                        while ((current2 -= 1) >= 0) {
469
                            if (!candidate[current2]) {
470
                                continue;
471
                            }
472
                            if (isProperty(nodeType, candidates[current])) {
473
                                element = new Element(candidate[current2], [key, current2], 'Property', null);
474
                            } else if (isNode(candidate[current2])) {
475
                                element = new Element(candidate[current2], [key, current2], null, null);
476
                            } else {
477
                                continue;
478
                            }
479
                            worklist.push(element);
480
                        }
481
                    } else if (isNode(candidate)) {
482
                        worklist.push(new Element(candidate, key, null, null));
483
                    }
484
                }
485
            }
486
        }
487
    };
488

    
489
    Controller.prototype.replace = function replace(root, visitor) {
490
        var worklist,
491
            leavelist,
492
            node,
493
            nodeType,
494
            target,
495
            element,
496
            current,
497
            current2,
498
            candidates,
499
            candidate,
500
            sentinel,
501
            outer,
502
            key;
503

    
504
        function removeElem(element) {
505
            var i,
506
                key,
507
                nextElem,
508
                parent;
509

    
510
            if (element.ref.remove()) {
511
                // When the reference is an element of an array.
512
                key = element.ref.key;
513
                parent = element.ref.parent;
514

    
515
                // If removed from array, then decrease following items' keys.
516
                i = worklist.length;
517
                while (i--) {
518
                    nextElem = worklist[i];
519
                    if (nextElem.ref && nextElem.ref.parent === parent) {
520
                        if  (nextElem.ref.key < key) {
521
                            break;
522
                        }
523
                        --nextElem.ref.key;
524
                    }
525
                }
526
            }
527
        }
528

    
529
        this.__initialize(root, visitor);
530

    
531
        sentinel = {};
532

    
533
        // reference
534
        worklist = this.__worklist;
535
        leavelist = this.__leavelist;
536

    
537
        // initialize
538
        outer = {
539
            root: root
540
        };
541
        element = new Element(root, null, null, new Reference(outer, 'root'));
542
        worklist.push(element);
543
        leavelist.push(element);
544

    
545
        while (worklist.length) {
546
            element = worklist.pop();
547

    
548
            if (element === sentinel) {
549
                element = leavelist.pop();
550

    
551
                target = this.__execute(visitor.leave, element);
552

    
553
                // node may be replaced with null,
554
                // so distinguish between undefined and null in this place
555
                if (target !== undefined && target !== BREAK && target !== SKIP && target !== REMOVE) {
556
                    // replace
557
                    element.ref.replace(target);
558
                }
559

    
560
                if (this.__state === REMOVE || target === REMOVE) {
561
                    removeElem(element);
562
                }
563

    
564
                if (this.__state === BREAK || target === BREAK) {
565
                    return outer.root;
566
                }
567
                continue;
568
            }
569

    
570
            target = this.__execute(visitor.enter, element);
571

    
572
            // node may be replaced with null,
573
            // so distinguish between undefined and null in this place
574
            if (target !== undefined && target !== BREAK && target !== SKIP && target !== REMOVE) {
575
                // replace
576
                element.ref.replace(target);
577
                element.node = target;
578
            }
579

    
580
            if (this.__state === REMOVE || target === REMOVE) {
581
                removeElem(element);
582
                element.node = null;
583
            }
584

    
585
            if (this.__state === BREAK || target === BREAK) {
586
                return outer.root;
587
            }
588

    
589
            // node may be null
590
            node = element.node;
591
            if (!node) {
592
                continue;
593
            }
594

    
595
            worklist.push(sentinel);
596
            leavelist.push(element);
597

    
598
            if (this.__state === SKIP || target === SKIP) {
599
                continue;
600
            }
601

    
602
            nodeType = node.type || element.wrap;
603
            candidates = this.__keys[nodeType];
604
            if (!candidates) {
605
                if (this.__fallback) {
606
                    candidates = this.__fallback(node);
607
                } else {
608
                    throw new Error('Unknown node type ' + nodeType + '.');
609
                }
610
            }
611

    
612
            current = candidates.length;
613
            while ((current -= 1) >= 0) {
614
                key = candidates[current];
615
                candidate = node[key];
616
                if (!candidate) {
617
                    continue;
618
                }
619

    
620
                if (Array.isArray(candidate)) {
621
                    current2 = candidate.length;
622
                    while ((current2 -= 1) >= 0) {
623
                        if (!candidate[current2]) {
624
                            continue;
625
                        }
626
                        if (isProperty(nodeType, candidates[current])) {
627
                            element = new Element(candidate[current2], [key, current2], 'Property', new Reference(candidate, current2));
628
                        } else if (isNode(candidate[current2])) {
629
                            element = new Element(candidate[current2], [key, current2], null, new Reference(candidate, current2));
630
                        } else {
631
                            continue;
632
                        }
633
                        worklist.push(element);
634
                    }
635
                } else if (isNode(candidate)) {
636
                    worklist.push(new Element(candidate, key, null, new Reference(node, key)));
637
                }
638
            }
639
        }
640

    
641
        return outer.root;
642
    };
643

    
644
    function traverse(root, visitor) {
645
        var controller = new Controller();
646
        return controller.traverse(root, visitor);
647
    }
648

    
649
    function replace(root, visitor) {
650
        var controller = new Controller();
651
        return controller.replace(root, visitor);
652
    }
653

    
654
    function extendCommentRange(comment, tokens) {
655
        var target;
656

    
657
        target = upperBound(tokens, function search(token) {
658
            return token.range[0] > comment.range[0];
659
        });
660

    
661
        comment.extendedRange = [comment.range[0], comment.range[1]];
662

    
663
        if (target !== tokens.length) {
664
            comment.extendedRange[1] = tokens[target].range[0];
665
        }
666

    
667
        target -= 1;
668
        if (target >= 0) {
669
            comment.extendedRange[0] = tokens[target].range[1];
670
        }
671

    
672
        return comment;
673
    }
674

    
675
    function attachComments(tree, providedComments, tokens) {
676
        // At first, we should calculate extended comment ranges.
677
        var comments = [], comment, len, i, cursor;
678

    
679
        if (!tree.range) {
680
            throw new Error('attachComments needs range information');
681
        }
682

    
683
        // tokens array is empty, we attach comments to tree as 'leadingComments'
684
        if (!tokens.length) {
685
            if (providedComments.length) {
686
                for (i = 0, len = providedComments.length; i < len; i += 1) {
687
                    comment = deepCopy(providedComments[i]);
688
                    comment.extendedRange = [0, tree.range[0]];
689
                    comments.push(comment);
690
                }
691
                tree.leadingComments = comments;
692
            }
693
            return tree;
694
        }
695

    
696
        for (i = 0, len = providedComments.length; i < len; i += 1) {
697
            comments.push(extendCommentRange(deepCopy(providedComments[i]), tokens));
698
        }
699

    
700
        // This is based on John Freeman's implementation.
701
        cursor = 0;
702
        traverse(tree, {
703
            enter: function (node) {
704
                var comment;
705

    
706
                while (cursor < comments.length) {
707
                    comment = comments[cursor];
708
                    if (comment.extendedRange[1] > node.range[0]) {
709
                        break;
710
                    }
711

    
712
                    if (comment.extendedRange[1] === node.range[0]) {
713
                        if (!node.leadingComments) {
714
                            node.leadingComments = [];
715
                        }
716
                        node.leadingComments.push(comment);
717
                        comments.splice(cursor, 1);
718
                    } else {
719
                        cursor += 1;
720
                    }
721
                }
722

    
723
                // already out of owned node
724
                if (cursor === comments.length) {
725
                    return VisitorOption.Break;
726
                }
727

    
728
                if (comments[cursor].extendedRange[0] > node.range[1]) {
729
                    return VisitorOption.Skip;
730
                }
731
            }
732
        });
733

    
734
        cursor = 0;
735
        traverse(tree, {
736
            leave: function (node) {
737
                var comment;
738

    
739
                while (cursor < comments.length) {
740
                    comment = comments[cursor];
741
                    if (node.range[1] < comment.extendedRange[0]) {
742
                        break;
743
                    }
744

    
745
                    if (node.range[1] === comment.extendedRange[0]) {
746
                        if (!node.trailingComments) {
747
                            node.trailingComments = [];
748
                        }
749
                        node.trailingComments.push(comment);
750
                        comments.splice(cursor, 1);
751
                    } else {
752
                        cursor += 1;
753
                    }
754
                }
755

    
756
                // already out of owned node
757
                if (cursor === comments.length) {
758
                    return VisitorOption.Break;
759
                }
760

    
761
                if (comments[cursor].extendedRange[0] > node.range[1]) {
762
                    return VisitorOption.Skip;
763
                }
764
            }
765
        });
766

    
767
        return tree;
768
    }
769

    
770
    exports.version = require('./package.json').version;
771
    exports.Syntax = Syntax;
772
    exports.traverse = traverse;
773
    exports.replace = replace;
774
    exports.attachComments = attachComments;
775
    exports.VisitorKeys = VisitorKeys;
776
    exports.VisitorOption = VisitorOption;
777
    exports.Controller = Controller;
778
    exports.cloneEnvironment = function () { return clone({}); };
779

    
780
    return exports;
781
}(exports));
782
/* vim: set sw=4 ts=4 et tw=80 : */
(4-4/6)