UNPKG

67.3 kBJavaScriptView Raw
1/*
2 Copyright (C) 2012-2014 Yusuke Suzuki <utatane.tea@gmail.com>
3 Copyright (C) 2014 Dan Tao <daniel.tao@gmail.com>
4 Copyright (C) 2013 Andrew Eisenberg <andrew@eisenberg.as>
5
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are met:
8
9 * Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
11 * Redistributions in binary form must reproduce the above copyright
12 notice, this list of conditions and the following disclaimer in the
13 documentation and/or other materials provided with the distribution.
14
15 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
19 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25*/
26
27(function () {
28 'use strict';
29
30 var VERSION,
31 typed,
32 jsdoc,
33 esutils,
34 isArray,
35 hasOwnProperty;
36
37 // Sync with package.json.
38 VERSION = require('./package.json').version;
39
40 esutils = require('esutils');
41
42 function sliceSource(source, index, last) {
43 return source.slice(index, last);
44 }
45
46 isArray = Array.isArray;
47 if (!isArray) {
48 isArray = function isArray(ary) {
49 return Object.prototype.toString.call(ary) === '[object Array]';
50 };
51 }
52
53 hasOwnProperty = (function () {
54 var func = Object.prototype.hasOwnProperty;
55 return function hasOwnProperty(obj, name) {
56 return func.call(obj, name);
57 };
58 }());
59
60 function shallowCopy(obj) {
61 var ret = {}, key;
62 for (key in obj) {
63 if (obj.hasOwnProperty(key)) {
64 ret[key] = obj[key];
65 }
66 }
67 return ret;
68 }
69
70 function isASCIIAlphanumeric(ch) {
71 return (ch >= 0x61 /* 'a' */ && ch <= 0x7A /* 'z' */) ||
72 (ch >= 0x41 /* 'A' */ && ch <= 0x5A /* 'Z' */) ||
73 (ch >= 0x30 /* '0' */ && ch <= 0x39 /* '9' */);
74 }
75
76 function isTypeName(ch) {
77 return '><(){}[],:*|?!='.indexOf(String.fromCharCode(ch)) === -1 && !esutils.code.isWhiteSpace(ch) && !esutils.code.isLineTerminator(ch);
78 }
79
80 function isParamTitle(title) {
81 return title === 'param' || title === 'argument' || title === 'arg';
82 }
83
84 function isProperty(title) {
85 return title === 'property' || title === 'prop';
86 }
87
88 function isNameParameterRequired(title) {
89 return isParamTitle(title) || isProperty(title) ||
90 title === 'alias' || title === 'this' || title === 'mixes' || title === 'requires';
91 }
92
93 function isAllowedName(title) {
94 return isNameParameterRequired(title) || title === 'const' || title === 'constant';
95 }
96
97 function isAllowedNested(title) {
98 return isProperty(title) || isParamTitle(title);
99 }
100
101 function isTypeParameterRequired(title) {
102 return isParamTitle(title) || title === 'define' || title === 'enum' ||
103 title === 'implements' || title === 'return' ||
104 title === 'this' || title === 'type' || title === 'typedef' ||
105 title === 'returns' || isProperty(title);
106 }
107
108 // Consider deprecation instead using 'isTypeParameterRequired' and 'Rules' declaration to pick when a type is optional/required
109 // This would require changes to 'parseType'
110 function isAllowedType(title) {
111 return isTypeParameterRequired(title) || title === 'throws' || title === 'const' || title === 'constant' ||
112 title === 'namespace' || title === 'member' || title === 'var' || title === 'module' ||
113 title === 'constructor' || title === 'class' || title === 'extends' || title === 'augments' ||
114 title === 'public' || title === 'private' || title === 'protected';
115 }
116
117 function DoctrineError(message) {
118 this.name = 'DoctrineError';
119 this.message = message;
120 }
121 DoctrineError.prototype = (function () {
122 var Middle = function () { };
123 Middle.prototype = Error.prototype;
124 return new Middle();
125 }());
126 DoctrineError.prototype.constructor = DoctrineError;
127
128 function throwError(message) {
129 throw new DoctrineError(message);
130 }
131
132 function assert(cond, text) {
133 if (VERSION.slice(-3) === 'dev') {
134 if (!cond) {
135 throwError(text);
136 }
137 }
138 }
139
140 function trim(str) {
141 return str.replace(/^\s+/, '').replace(/\s+$/, '');
142 }
143
144 function unwrapComment(doc) {
145 // JSDoc comment is following form
146 // /**
147 // * .......
148 // */
149 // remove /**, */ and *
150 var BEFORE_STAR = 0,
151 STAR = 1,
152 AFTER_STAR = 2,
153 index,
154 len,
155 mode,
156 result,
157 ch;
158
159 doc = doc.replace(/^\/\*\*?/, '').replace(/\*\/$/, '');
160 index = 0;
161 len = doc.length;
162 mode = BEFORE_STAR;
163 result = '';
164
165 while (index < len) {
166 ch = doc.charCodeAt(index);
167 switch (mode) {
168 case BEFORE_STAR:
169 if (esutils.code.isLineTerminator(ch)) {
170 result += String.fromCharCode(ch);
171 } else if (ch === 0x2A /* '*' */) {
172 mode = STAR;
173 } else if (!esutils.code.isWhiteSpace(ch)) {
174 result += String.fromCharCode(ch);
175 mode = AFTER_STAR;
176 }
177 break;
178
179 case STAR:
180 if (!esutils.code.isWhiteSpace(ch)) {
181 result += String.fromCharCode(ch);
182 }
183 mode = esutils.code.isLineTerminator(ch) ? BEFORE_STAR : AFTER_STAR;
184 break;
185
186 case AFTER_STAR:
187 result += String.fromCharCode(ch);
188 if (esutils.code.isLineTerminator(ch)) {
189 mode = BEFORE_STAR;
190 }
191 break;
192 }
193 index += 1;
194 }
195
196 return result;
197 }
198
199 // Type Expression Parser
200
201 (function (exports) {
202 var Syntax,
203 Token,
204 source,
205 length,
206 index,
207 previous,
208 token,
209 value;
210
211 Syntax = {
212 NullableLiteral: 'NullableLiteral',
213 AllLiteral: 'AllLiteral',
214 NullLiteral: 'NullLiteral',
215 UndefinedLiteral: 'UndefinedLiteral',
216 VoidLiteral: 'VoidLiteral',
217 UnionType: 'UnionType',
218 ArrayType: 'ArrayType',
219 RecordType: 'RecordType',
220 FieldType: 'FieldType',
221 FunctionType: 'FunctionType',
222 ParameterType: 'ParameterType',
223 RestType: 'RestType',
224 NonNullableType: 'NonNullableType',
225 OptionalType: 'OptionalType',
226 NullableType: 'NullableType',
227 NameExpression: 'NameExpression',
228 TypeApplication: 'TypeApplication'
229 };
230
231 Token = {
232 ILLEGAL: 0, // ILLEGAL
233 DOT_LT: 1, // .<
234 REST: 2, // ...
235 LT: 3, // <
236 GT: 4, // >
237 LPAREN: 5, // (
238 RPAREN: 6, // )
239 LBRACE: 7, // {
240 RBRACE: 8, // }
241 LBRACK: 9, // [
242 RBRACK: 10, // ]
243 COMMA: 11, // ,
244 COLON: 12, // :
245 STAR: 13, // *
246 PIPE: 14, // |
247 QUESTION: 15, // ?
248 BANG: 16, // !
249 EQUAL: 17, // =
250 NAME: 18, // name token
251 STRING: 19, // string
252 NUMBER: 20, // number
253 EOF: 21
254 };
255
256 function Context(previous, index, token, value) {
257 this._previous = previous;
258 this._index = index;
259 this._token = token;
260 this._value = value;
261 }
262
263 Context.prototype.restore = function () {
264 previous = this._previous;
265 index = this._index;
266 token = this._token;
267 value = this._value;
268 };
269
270 Context.save = function () {
271 return new Context(previous, index, token, value);
272 };
273
274 function advance() {
275 var ch = source.charAt(index);
276 index += 1;
277 return ch;
278 }
279
280 function scanHexEscape(prefix) {
281 var i, len, ch, code = 0;
282
283 len = (prefix === 'u') ? 4 : 2;
284 for (i = 0; i < len; ++i) {
285 if (index < length && esutils.code.isHexDigit(source.charCodeAt(index))) {
286 ch = advance();
287 code = code * 16 + '0123456789abcdef'.indexOf(ch.toLowerCase());
288 } else {
289 return '';
290 }
291 }
292 return String.fromCharCode(code);
293 }
294
295 function scanString() {
296 var str = '', quote, ch, code, unescaped, restore; //TODO review removal octal = false
297 quote = source.charAt(index);
298 ++index;
299
300 while (index < length) {
301 ch = advance();
302
303 if (ch === quote) {
304 quote = '';
305 break;
306 } else if (ch === '\\') {
307 ch = advance();
308 if (!esutils.code.isLineTerminator(ch.charCodeAt(0))) {
309 switch (ch) {
310 case 'n':
311 str += '\n';
312 break;
313 case 'r':
314 str += '\r';
315 break;
316 case 't':
317 str += '\t';
318 break;
319 case 'u':
320 case 'x':
321 restore = index;
322 unescaped = scanHexEscape(ch);
323 if (unescaped) {
324 str += unescaped;
325 } else {
326 index = restore;
327 str += ch;
328 }
329 break;
330 case 'b':
331 str += '\b';
332 break;
333 case 'f':
334 str += '\f';
335 break;
336 case 'v':
337 str += '\v';
338 break;
339
340 default:
341 if (esutils.code.isOctalDigit(ch.charCodeAt(0))) {
342 code = '01234567'.indexOf(ch);
343
344 // \0 is not octal escape sequence
345 // Deprecating unused code. TODO review removal
346 //if (code !== 0) {
347 // octal = true;
348 //}
349
350 if (index < length && esutils.code.isOctalDigit(source.charCodeAt(index))) {
351 //TODO Review Removal octal = true;
352 code = code * 8 + '01234567'.indexOf(advance());
353
354 // 3 digits are only allowed when string starts
355 // with 0, 1, 2, 3
356 if ('0123'.indexOf(ch) >= 0 &&
357 index < length &&
358 esutils.code.isOctalDigit(source.charCodeAt(index))) {
359 code = code * 8 + '01234567'.indexOf(advance());
360 }
361 }
362 str += String.fromCharCode(code);
363 } else {
364 str += ch;
365 }
366 break;
367 }
368 } else {
369 if (ch === '\r' && source.charCodeAt(index) === 0x0A /* '\n' */) {
370 ++index;
371 }
372 }
373 } else if (esutils.code.isLineTerminator(ch.charCodeAt(0))) {
374 break;
375 } else {
376 str += ch;
377 }
378 }
379
380 if (quote !== '') {
381 throwError('unexpected quote');
382 }
383
384 value = str;
385 return Token.STRING;
386 }
387
388 function scanNumber() {
389 var number, ch;
390
391 number = '';
392 ch = source.charCodeAt(index);
393
394 if (ch !== 0x2E /* '.' */) {
395 number = advance();
396 ch = source.charCodeAt(index);
397
398 if (number === '0') {
399 if (ch === 0x78 /* 'x' */ || ch === 0x58 /* 'X' */) {
400 number += advance();
401 while (index < length) {
402 ch = source.charCodeAt(index);
403 if (!esutils.code.isHexDigit(ch)) {
404 break;
405 }
406 number += advance();
407 }
408
409 if (number.length <= 2) {
410 // only 0x
411 throwError('unexpected token');
412 }
413
414 if (index < length) {
415 ch = source.charCodeAt(index);
416 if (esutils.code.isIdentifierStart(ch)) {
417 throwError('unexpected token');
418 }
419 }
420 value = parseInt(number, 16);
421 return Token.NUMBER;
422 }
423
424 if (esutils.code.isOctalDigit(ch)) {
425 number += advance();
426 while (index < length) {
427 ch = source.charCodeAt(index);
428 if (!esutils.code.isOctalDigit(ch)) {
429 break;
430 }
431 number += advance();
432 }
433
434 if (index < length) {
435 ch = source.charCodeAt(index);
436 if (esutils.code.isIdentifierStart(ch) || esutils.code.isDecimalDigit(ch)) {
437 throwError('unexpected token');
438 }
439 }
440 value = parseInt(number, 8);
441 return Token.NUMBER;
442 }
443
444 if (esutils.code.isDecimalDigit(ch)) {
445 throwError('unexpected token');
446 }
447 }
448
449 while (index < length) {
450 ch = source.charCodeAt(index);
451 if (!esutils.code.isDecimalDigit(ch)) {
452 break;
453 }
454 number += advance();
455 }
456 }
457
458 if (ch === 0x2E /* '.' */) {
459 number += advance();
460 while (index < length) {
461 ch = source.charCodeAt(index);
462 if (!esutils.code.isDecimalDigit(ch)) {
463 break;
464 }
465 number += advance();
466 }
467 }
468
469 if (ch === 0x65 /* 'e' */ || ch === 0x45 /* 'E' */) {
470 number += advance();
471
472 ch = source.charCodeAt(index);
473 if (ch === 0x2B /* '+' */ || ch === 0x2D /* '-' */) {
474 number += advance();
475 }
476
477 ch = source.charCodeAt(index);
478 if (esutils.code.isDecimalDigit(ch)) {
479 number += advance();
480 while (index < length) {
481 ch = source.charCodeAt(index);
482 if (!esutils.code.isDecimalDigit(ch)) {
483 break;
484 }
485 number += advance();
486 }
487 } else {
488 throwError('unexpected token');
489 }
490 }
491
492 if (index < length) {
493 ch = source.charCodeAt(index);
494 if (esutils.code.isIdentifierStart(ch)) {
495 throwError('unexpected token');
496 }
497 }
498
499 value = parseFloat(number);
500 return Token.NUMBER;
501 }
502
503
504 function scanTypeName() {
505 var ch, ch2;
506
507 value = advance();
508 while (index < length && isTypeName(source.charCodeAt(index))) {
509 ch = source.charCodeAt(index);
510 if (ch === 0x2E /* '.' */) {
511 if ((index + 1) >= length) {
512 return Token.ILLEGAL;
513 }
514 ch2 = source.charCodeAt(index + 1);
515 if (ch2 === 0x3C /* '<' */) {
516 break;
517 }
518 }
519 value += advance();
520 }
521 return Token.NAME;
522 }
523
524 function next() {
525 var ch;
526
527 previous = index;
528
529 while (index < length && esutils.code.isWhiteSpace(source.charCodeAt(index))) {
530 advance();
531 }
532 if (index >= length) {
533 token = Token.EOF;
534 return token;
535 }
536
537 ch = source.charCodeAt(index);
538 switch (ch) {
539 case 0x27: /* ''' */
540 case 0x22: /* '"' */
541 token = scanString();
542 return token;
543
544 case 0x3A: /* ':' */
545 advance();
546 token = Token.COLON;
547 return token;
548
549 case 0x2C: /* ',' */
550 advance();
551 token = Token.COMMA;
552 return token;
553
554 case 0x28: /* '(' */
555 advance();
556 token = Token.LPAREN;
557 return token;
558
559 case 0x29: /* ')' */
560 advance();
561 token = Token.RPAREN;
562 return token;
563
564 case 0x5B: /* '[' */
565 advance();
566 token = Token.LBRACK;
567 return token;
568
569 case 0x5D: /* ']' */
570 advance();
571 token = Token.RBRACK;
572 return token;
573
574 case 0x7B: /* '{' */
575 advance();
576 token = Token.LBRACE;
577 return token;
578
579 case 0x7D: /* '}' */
580 advance();
581 token = Token.RBRACE;
582 return token;
583
584 case 0x2E: /* '.' */
585 if (index + 1 < length) {
586 ch = source.charCodeAt(index + 1);
587 if (ch === 0x3C /* '<' */) {
588 advance(); // '.'
589 advance(); // '<'
590 token = Token.DOT_LT;
591 return token;
592 }
593
594 if (ch === 0x2E /* '.' */ && index + 2 < length && source.charCodeAt(index + 2) === 0x2E /* '.' */) {
595 advance(); // '.'
596 advance(); // '.'
597 advance(); // '.'
598 token = Token.REST;
599 return token;
600 }
601
602 if (esutils.code.isDecimalDigit(ch)) {
603 token = scanNumber();
604 return token;
605 }
606 }
607 token = Token.ILLEGAL;
608 return token;
609
610 case 0x3C: /* '<' */
611 advance();
612 token = Token.LT;
613 return token;
614
615 case 0x3E: /* '>' */
616 advance();
617 token = Token.GT;
618 return token;
619
620 case 0x2A: /* '*' */
621 advance();
622 token = Token.STAR;
623 return token;
624
625 case 0x7C: /* '|' */
626 advance();
627 token = Token.PIPE;
628 return token;
629
630 case 0x3F: /* '?' */
631 advance();
632 token = Token.QUESTION;
633 return token;
634
635 case 0x21: /* '!' */
636 advance();
637 token = Token.BANG;
638 return token;
639
640 case 0x3D: /* '=' */
641 advance();
642 token = Token.EQUAL;
643 return token;
644
645 default:
646 if (esutils.code.isDecimalDigit(ch)) {
647 token = scanNumber();
648 return token;
649 }
650
651 // type string permits following case,
652 //
653 // namespace.module.MyClass
654 //
655 // this reduced 1 token TK_NAME
656 assert(isTypeName(ch));
657 token = scanTypeName();
658 return token;
659 }
660 }
661
662 function consume(target, text) {
663 assert(token === target, text || 'consumed token not matched');
664 next();
665 }
666
667 function expect(target) {
668 if (token !== target) {
669 throwError('unexpected token');
670 }
671 next();
672 }
673
674 // UnionType := '(' TypeUnionList ')'
675 //
676 // TypeUnionList :=
677 // <<empty>>
678 // | NonemptyTypeUnionList
679 //
680 // NonemptyTypeUnionList :=
681 // TypeExpression
682 // | TypeExpression '|' NonemptyTypeUnionList
683 function parseUnionType() {
684 var elements;
685 consume(Token.LPAREN, 'UnionType should start with (');
686 elements = [];
687 if (token !== Token.RPAREN) {
688 while (true) {
689 elements.push(parseTypeExpression());
690 if (token === Token.RPAREN) {
691 break;
692 }
693 expect(Token.PIPE);
694 }
695 }
696 consume(Token.RPAREN, 'UnionType should end with )');
697 return {
698 type: Syntax.UnionType,
699 elements: elements
700 };
701 }
702
703 // ArrayType := '[' ElementTypeList ']'
704 //
705 // ElementTypeList :=
706 // <<empty>>
707 // | TypeExpression
708 // | '...' TypeExpression
709 // | TypeExpression ',' ElementTypeList
710 function parseArrayType() {
711 var elements;
712 consume(Token.LBRACK, 'ArrayType should start with [');
713 elements = [];
714 while (token !== Token.RBRACK) {
715 if (token === Token.REST) {
716 consume(Token.REST);
717 elements.push({
718 type: Syntax.RestType,
719 expression: parseTypeExpression()
720 });
721 break;
722 } else {
723 elements.push(parseTypeExpression());
724 }
725 if (token !== Token.RBRACK) {
726 expect(Token.COMMA);
727 }
728 }
729 expect(Token.RBRACK);
730 return {
731 type: Syntax.ArrayType,
732 elements: elements
733 };
734 }
735
736 function parseFieldName() {
737 var v = value;
738 if (token === Token.NAME || token === Token.STRING) {
739 next();
740 return v;
741 }
742
743 if (token === Token.NUMBER) {
744 consume(Token.NUMBER);
745 return String(v);
746 }
747
748 throwError('unexpected token');
749 }
750
751 // FieldType :=
752 // FieldName
753 // | FieldName ':' TypeExpression
754 //
755 // FieldName :=
756 // NameExpression
757 // | StringLiteral
758 // | NumberLiteral
759 // | ReservedIdentifier
760 function parseFieldType() {
761 var key;
762
763 key = parseFieldName();
764 if (token === Token.COLON) {
765 consume(Token.COLON);
766 return {
767 type: Syntax.FieldType,
768 key: key,
769 value: parseTypeExpression()
770 };
771 }
772 return {
773 type: Syntax.FieldType,
774 key: key,
775 value: null
776 };
777 }
778
779 // RecordType := '{' FieldTypeList '}'
780 //
781 // FieldTypeList :=
782 // <<empty>>
783 // | FieldType
784 // | FieldType ',' FieldTypeList
785 function parseRecordType() {
786 var fields;
787
788 consume(Token.LBRACE, 'RecordType should start with {');
789 fields = [];
790 if (token === Token.COMMA) {
791 consume(Token.COMMA);
792 } else {
793 while (token !== Token.RBRACE) {
794 fields.push(parseFieldType());
795 if (token !== Token.RBRACE) {
796 expect(Token.COMMA);
797 }
798 }
799 }
800 expect(Token.RBRACE);
801 return {
802 type: Syntax.RecordType,
803 fields: fields
804 };
805 }
806
807 function parseNameExpression() {
808 var name = value;
809 expect(Token.NAME);
810 return {
811 type: Syntax.NameExpression,
812 name: name
813 };
814 }
815
816 // TypeExpressionList :=
817 // TopLevelTypeExpression
818 // | TopLevelTypeExpression ',' TypeExpressionList
819 function parseTypeExpressionList() {
820 var elements = [];
821
822 elements.push(parseTop());
823 while (token === Token.COMMA) {
824 consume(Token.COMMA);
825 elements.push(parseTop());
826 }
827 return elements;
828 }
829
830 // TypeName :=
831 // NameExpression
832 // | NameExpression TypeApplication
833 //
834 // TypeApplication :=
835 // '.<' TypeExpressionList '>'
836 // | '<' TypeExpressionList '>' // this is extension of doctrine
837 function parseTypeName() {
838 var expr, applications;
839
840 expr = parseNameExpression();
841 if (token === Token.DOT_LT || token === Token.LT) {
842 next();
843 applications = parseTypeExpressionList();
844 expect(Token.GT);
845 return {
846 type: Syntax.TypeApplication,
847 expression: expr,
848 applications: applications
849 };
850 }
851 return expr;
852 }
853
854 // ResultType :=
855 // <<empty>>
856 // | ':' void
857 // | ':' TypeExpression
858 //
859 // BNF is above
860 // but, we remove <<empty>> pattern, so token is always TypeToken::COLON
861 function parseResultType() {
862 consume(Token.COLON, 'ResultType should start with :');
863 if (token === Token.NAME && value === 'void') {
864 consume(Token.NAME);
865 return {
866 type: Syntax.VoidLiteral
867 };
868 }
869 return parseTypeExpression();
870 }
871
872 // ParametersType :=
873 // RestParameterType
874 // | NonRestParametersType
875 // | NonRestParametersType ',' RestParameterType
876 //
877 // RestParameterType :=
878 // '...'
879 // '...' Identifier
880 //
881 // NonRestParametersType :=
882 // ParameterType ',' NonRestParametersType
883 // | ParameterType
884 // | OptionalParametersType
885 //
886 // OptionalParametersType :=
887 // OptionalParameterType
888 // | OptionalParameterType, OptionalParametersType
889 //
890 // OptionalParameterType := ParameterType=
891 //
892 // ParameterType := TypeExpression | Identifier ':' TypeExpression
893 //
894 // Identifier is "new" or "this"
895 function parseParametersType() {
896 var params = [], optionalSequence = false, expr, rest = false;
897
898 while (token !== Token.RPAREN) {
899 if (token === Token.REST) {
900 // RestParameterType
901 consume(Token.REST);
902 rest = true;
903 }
904
905 expr = parseTypeExpression();
906 if (expr.type === Syntax.NameExpression && token === Token.COLON) {
907 // Identifier ':' TypeExpression
908 consume(Token.COLON);
909 expr = {
910 type: Syntax.ParameterType,
911 name: expr.name,
912 expression: parseTypeExpression()
913 };
914 }
915 if (token === Token.EQUAL) {
916 consume(Token.EQUAL);
917 expr = {
918 type: Syntax.OptionalType,
919 expression: expr
920 };
921 optionalSequence = true;
922 } else {
923 if (optionalSequence) {
924 throwError('unexpected token');
925 }
926 }
927 if (rest) {
928 expr = {
929 type: Syntax.RestType,
930 expression: expr
931 };
932 }
933 params.push(expr);
934 if (token !== Token.RPAREN) {
935 expect(Token.COMMA);
936 }
937 }
938 return params;
939 }
940
941 // FunctionType := 'function' FunctionSignatureType
942 //
943 // FunctionSignatureType :=
944 // | TypeParameters '(' ')' ResultType
945 // | TypeParameters '(' ParametersType ')' ResultType
946 // | TypeParameters '(' 'this' ':' TypeName ')' ResultType
947 // | TypeParameters '(' 'this' ':' TypeName ',' ParametersType ')' ResultType
948 function parseFunctionType() {
949 var isNew, thisBinding, params, result, fnType;
950 assert(token === Token.NAME && value === 'function', 'FunctionType should start with \'function\'');
951 consume(Token.NAME);
952
953 // Google Closure Compiler is not implementing TypeParameters.
954 // So we do not. if we don't get '(', we see it as error.
955 expect(Token.LPAREN);
956
957 isNew = false;
958 params = [];
959 thisBinding = null;
960 if (token !== Token.RPAREN) {
961 // ParametersType or 'this'
962 if (token === Token.NAME &&
963 (value === 'this' || value === 'new')) {
964 // 'this' or 'new'
965 // 'new' is Closure Compiler extension
966 isNew = value === 'new';
967 consume(Token.NAME);
968 expect(Token.COLON);
969 thisBinding = parseTypeName();
970 if (token === Token.COMMA) {
971 consume(Token.COMMA);
972 params = parseParametersType();
973 }
974 } else {
975 params = parseParametersType();
976 }
977 }
978
979 expect(Token.RPAREN);
980
981 result = null;
982 if (token === Token.COLON) {
983 result = parseResultType();
984 }
985
986 fnType = {
987 type: Syntax.FunctionType,
988 params: params,
989 result: result
990 };
991 if (thisBinding) {
992 // avoid adding null 'new' and 'this' properties
993 fnType['this'] = thisBinding;
994 if (isNew) {
995 fnType['new'] = true;
996 }
997 }
998 return fnType;
999 }
1000
1001 // BasicTypeExpression :=
1002 // '*'
1003 // | 'null'
1004 // | 'undefined'
1005 // | TypeName
1006 // | FunctionType
1007 // | UnionType
1008 // | RecordType
1009 // | ArrayType
1010 function parseBasicTypeExpression() {
1011 var context;
1012 switch (token) {
1013 case Token.STAR:
1014 consume(Token.STAR);
1015 return {
1016 type: Syntax.AllLiteral
1017 };
1018
1019 case Token.LPAREN:
1020 return parseUnionType();
1021
1022 case Token.LBRACK:
1023 return parseArrayType();
1024
1025 case Token.LBRACE:
1026 return parseRecordType();
1027
1028 case Token.NAME:
1029 if (value === 'null') {
1030 consume(Token.NAME);
1031 return {
1032 type: Syntax.NullLiteral
1033 };
1034 }
1035
1036 if (value === 'undefined') {
1037 consume(Token.NAME);
1038 return {
1039 type: Syntax.UndefinedLiteral
1040 };
1041 }
1042
1043 context = Context.save();
1044 if (value === 'function') {
1045 try {
1046 return parseFunctionType();
1047 } catch (e) {
1048 context.restore();
1049 }
1050 }
1051
1052 return parseTypeName();
1053
1054 default:
1055 throwError('unexpected token');
1056 }
1057 }
1058
1059 // TypeExpression :=
1060 // BasicTypeExpression
1061 // | '?' BasicTypeExpression
1062 // | '!' BasicTypeExpression
1063 // | BasicTypeExpression '?'
1064 // | BasicTypeExpression '!'
1065 // | '?'
1066 // | BasicTypeExpression '[]'
1067 function parseTypeExpression() {
1068 var expr;
1069
1070 if (token === Token.QUESTION) {
1071 consume(Token.QUESTION);
1072 if (token === Token.COMMA || token === Token.EQUAL || token === Token.RBRACE ||
1073 token === Token.RPAREN || token === Token.PIPE || token === Token.EOF ||
1074 token === Token.RBRACK) {
1075 return {
1076 type: Syntax.NullableLiteral
1077 };
1078 }
1079 return {
1080 type: Syntax.NullableType,
1081 expression: parseBasicTypeExpression(),
1082 prefix: true
1083 };
1084 }
1085
1086 if (token === Token.BANG) {
1087 consume(Token.BANG);
1088 return {
1089 type: Syntax.NonNullableType,
1090 expression: parseBasicTypeExpression(),
1091 prefix: true
1092 };
1093 }
1094
1095 expr = parseBasicTypeExpression();
1096 if (token === Token.BANG) {
1097 consume(Token.BANG);
1098 return {
1099 type: Syntax.NonNullableType,
1100 expression: expr,
1101 prefix: false
1102 };
1103 }
1104
1105 if (token === Token.QUESTION) {
1106 consume(Token.QUESTION);
1107 return {
1108 type: Syntax.NullableType,
1109 expression: expr,
1110 prefix: false
1111 };
1112 }
1113
1114 if (token === Token.LBRACK) {
1115 consume(Token.LBRACK);
1116 consume(Token.RBRACK, 'expected an array-style type declaration (' + value + '[])');
1117 return {
1118 type: Syntax.TypeApplication,
1119 expression: {
1120 type: Syntax.NameExpression,
1121 name: 'Array'
1122 },
1123 applications: [expr]
1124 };
1125 }
1126
1127 return expr;
1128 }
1129
1130 // TopLevelTypeExpression :=
1131 // TypeExpression
1132 // | TypeUnionList
1133 //
1134 // This rule is Google Closure Compiler extension, not ES4
1135 // like,
1136 // { number | string }
1137 // If strict to ES4, we should write it as
1138 // { (number|string) }
1139 function parseTop() {
1140 var expr, elements;
1141
1142 expr = parseTypeExpression();
1143 if (token !== Token.PIPE) {
1144 return expr;
1145 }
1146
1147 elements = [ expr ];
1148 consume(Token.PIPE);
1149 while (true) {
1150 elements.push(parseTypeExpression());
1151 if (token !== Token.PIPE) {
1152 break;
1153 }
1154 consume(Token.PIPE);
1155 }
1156
1157 return {
1158 type: Syntax.UnionType,
1159 elements: elements
1160 };
1161 }
1162
1163 function parseTopParamType() {
1164 var expr;
1165
1166 if (token === Token.REST) {
1167 consume(Token.REST);
1168 return {
1169 type: Syntax.RestType,
1170 expression: parseTop()
1171 };
1172 }
1173
1174 expr = parseTop();
1175 if (token === Token.EQUAL) {
1176 consume(Token.EQUAL);
1177 return {
1178 type: Syntax.OptionalType,
1179 expression: expr
1180 };
1181 }
1182
1183 return expr;
1184 }
1185
1186 function parseType(src, opt) {
1187 var expr;
1188
1189 source = src;
1190 length = source.length;
1191 index = 0;
1192 previous = 0;
1193
1194 next();
1195 expr = parseTop();
1196
1197 if (opt && opt.midstream) {
1198 return {
1199 expression: expr,
1200 index: previous
1201 };
1202 }
1203
1204 if (token !== Token.EOF) {
1205 throwError('not reach to EOF');
1206 }
1207
1208 return expr;
1209 }
1210
1211 function parseParamType(src, opt) {
1212 var expr;
1213
1214 source = src;
1215 length = source.length;
1216 index = 0;
1217 previous = 0;
1218
1219 next();
1220 expr = parseTopParamType();
1221
1222 if (opt && opt.midstream) {
1223 return {
1224 expression: expr,
1225 index: previous
1226 };
1227 }
1228
1229 if (token !== Token.EOF) {
1230 throwError('not reach to EOF');
1231 }
1232
1233 return expr;
1234 }
1235
1236 function stringifyImpl(node, compact, topLevel) {
1237 var result, i, iz;
1238
1239 switch (node.type) {
1240 case Syntax.NullableLiteral:
1241 result = '?';
1242 break;
1243
1244 case Syntax.AllLiteral:
1245 result = '*';
1246 break;
1247
1248 case Syntax.NullLiteral:
1249 result = 'null';
1250 break;
1251
1252 case Syntax.UndefinedLiteral:
1253 result = 'undefined';
1254 break;
1255
1256 case Syntax.VoidLiteral:
1257 result = 'void';
1258 break;
1259
1260 case Syntax.UnionType:
1261 if (!topLevel) {
1262 result = '(';
1263 } else {
1264 result = '';
1265 }
1266
1267 for (i = 0, iz = node.elements.length; i < iz; ++i) {
1268 result += stringifyImpl(node.elements[i], compact);
1269 if ((i + 1) !== iz) {
1270 result += '|';
1271 }
1272 }
1273
1274 if (!topLevel) {
1275 result += ')';
1276 }
1277 break;
1278
1279 case Syntax.ArrayType:
1280 result = '[';
1281 for (i = 0, iz = node.elements.length; i < iz; ++i) {
1282 result += stringifyImpl(node.elements[i], compact);
1283 if ((i + 1) !== iz) {
1284 result += compact ? ',' : ', ';
1285 }
1286 }
1287 result += ']';
1288 break;
1289
1290 case Syntax.RecordType:
1291 result = '{';
1292 for (i = 0, iz = node.fields.length; i < iz; ++i) {
1293 result += stringifyImpl(node.fields[i], compact);
1294 if ((i + 1) !== iz) {
1295 result += compact ? ',' : ', ';
1296 }
1297 }
1298 result += '}';
1299 break;
1300
1301 case Syntax.FieldType:
1302 if (node.value) {
1303 result = node.key + (compact ? ':' : ': ') + stringifyImpl(node.value, compact);
1304 } else {
1305 result = node.key;
1306 }
1307 break;
1308
1309 case Syntax.FunctionType:
1310 result = compact ? 'function(' : 'function (';
1311
1312 if (node['this']) {
1313 if (node['new']) {
1314 result += (compact ? 'new:' : 'new: ');
1315 } else {
1316 result += (compact ? 'this:' : 'this: ');
1317 }
1318
1319 result += stringifyImpl(node['this'], compact);
1320
1321 if (node.params.length !== 0) {
1322 result += compact ? ',' : ', ';
1323 }
1324 }
1325
1326 for (i = 0, iz = node.params.length; i < iz; ++i) {
1327 result += stringifyImpl(node.params[i], compact);
1328 if ((i + 1) !== iz) {
1329 result += compact ? ',' : ', ';
1330 }
1331 }
1332
1333 result += ')';
1334
1335 if (node.result) {
1336 result += (compact ? ':' : ': ') + stringifyImpl(node.result, compact);
1337 }
1338 break;
1339
1340 case Syntax.ParameterType:
1341 result = node.name + (compact ? ':' : ': ') + stringifyImpl(node.expression, compact);
1342 break;
1343
1344 case Syntax.RestType:
1345 result = '...';
1346 if (node.expression) {
1347 result += stringifyImpl(node.expression, compact);
1348 }
1349 break;
1350
1351 case Syntax.NonNullableType:
1352 if (node.prefix) {
1353 result = '!' + stringifyImpl(node.expression, compact);
1354 } else {
1355 result = stringifyImpl(node.expression, compact) + '!';
1356 }
1357 break;
1358
1359 case Syntax.OptionalType:
1360 result = stringifyImpl(node.expression, compact) + '=';
1361 break;
1362
1363 case Syntax.NullableType:
1364 if (node.prefix) {
1365 result = '?' + stringifyImpl(node.expression, compact);
1366 } else {
1367 result = stringifyImpl(node.expression, compact) + '?';
1368 }
1369 break;
1370
1371 case Syntax.NameExpression:
1372 result = node.name;
1373 break;
1374
1375 case Syntax.TypeApplication:
1376 result = stringifyImpl(node.expression, compact) + '.<';
1377 for (i = 0, iz = node.applications.length; i < iz; ++i) {
1378 result += stringifyImpl(node.applications[i], compact);
1379 if ((i + 1) !== iz) {
1380 result += compact ? ',' : ', ';
1381 }
1382 }
1383 result += '>';
1384 break;
1385
1386 default:
1387 throwError('Unknown type ' + node.type);
1388 }
1389
1390 return result;
1391 }
1392
1393 function stringify(node, options) {
1394 if (options == null) {
1395 options = {};
1396 }
1397 return stringifyImpl(node, options.compact, options.topLevel);
1398 }
1399
1400 exports.parseType = parseType;
1401 exports.parseParamType = parseParamType;
1402 exports.stringify = stringify;
1403 exports.Syntax = Syntax;
1404 }(typed = {}));
1405
1406 // JSDoc Tag Parser
1407
1408 (function (exports) {
1409 var Rules,
1410 index,
1411 lineNumber,
1412 length,
1413 source,
1414 recoverable,
1415 sloppy,
1416 strict;
1417
1418 function advance() {
1419 var ch = source.charCodeAt(index);
1420 index += 1;
1421 if (esutils.code.isLineTerminator(ch) && !(ch === 0x0D /* '\r' */ && source.charCodeAt(index) === 0x0A /* '\n' */)) {
1422 lineNumber += 1;
1423 }
1424 return String.fromCharCode(ch);
1425 }
1426
1427 function scanTitle() {
1428 var title = '';
1429 // waste '@'
1430 advance();
1431
1432 while (index < length && isASCIIAlphanumeric(source.charCodeAt(index))) {
1433 title += advance();
1434 }
1435
1436 return title;
1437 }
1438
1439 function seekContent() {
1440 var ch, waiting, last = index;
1441
1442 waiting = false;
1443 while (last < length) {
1444 ch = source.charCodeAt(last);
1445 if (esutils.code.isLineTerminator(ch) && !(ch === 0x0D /* '\r' */ && source.charCodeAt(last + 1) === 0x0A /* '\n' */)) {
1446 lineNumber += 1;
1447 waiting = true;
1448 } else if (waiting) {
1449 if (ch === 0x40 /* '@' */) {
1450 break;
1451 }
1452 if (!esutils.code.isWhiteSpace(ch)) {
1453 waiting = false;
1454 }
1455 }
1456 last += 1;
1457 }
1458 return last;
1459 }
1460
1461 // type expression may have nest brace, such as,
1462 // { { ok: string } }
1463 //
1464 // therefore, scanning type expression with balancing braces.
1465 function parseType(title, last) {
1466 var ch, brace, type, direct = false;
1467
1468
1469 // search '{'
1470 while (index < last) {
1471 ch = source.charCodeAt(index);
1472 if (esutils.code.isWhiteSpace(ch)) {
1473 advance();
1474 } else if (ch === 0x7B /* '{' */) {
1475 advance();
1476 break;
1477 } else {
1478 // this is direct pattern
1479 direct = true;
1480 break;
1481 }
1482 }
1483
1484
1485 if (direct) {
1486 return null;
1487 }
1488
1489 // type expression { is found
1490 brace = 1;
1491 type = '';
1492 while (index < last) {
1493 ch = source.charCodeAt(index);
1494 if (esutils.code.isLineTerminator(ch)) {
1495 advance();
1496 } else {
1497 if (ch === 0x7D /* '}' */) {
1498 brace -= 1;
1499 if (brace === 0) {
1500 advance();
1501 break;
1502 }
1503 } else if (ch === 0x7B /* '{' */) {
1504 brace += 1;
1505 }
1506 type += advance();
1507 }
1508 }
1509
1510 if (brace !== 0) {
1511 // braces is not balanced
1512 return throwError('Braces are not balanced');
1513 }
1514
1515 if (isParamTitle(title)) {
1516 return typed.parseParamType(type);
1517 }
1518 return typed.parseType(type);
1519 }
1520
1521 function scanIdentifier(last) {
1522 var identifier;
1523 if (!esutils.code.isIdentifierStart(source.charCodeAt(index))) {
1524 return null;
1525 }
1526 identifier = advance();
1527 while (index < last && esutils.code.isIdentifierPart(source.charCodeAt(index))) {
1528 identifier += advance();
1529 }
1530 return identifier;
1531 }
1532
1533 function skipWhiteSpace(last) {
1534 while (index < last && (esutils.code.isWhiteSpace(source.charCodeAt(index)) || esutils.code.isLineTerminator(source.charCodeAt(index)))) {
1535 advance();
1536 }
1537 }
1538
1539 function parseName(last, allowBrackets, allowNestedParams) {
1540 var name = '', useBrackets;
1541
1542 skipWhiteSpace(last);
1543
1544 if (index >= last) {
1545 return null;
1546 }
1547
1548 if (allowBrackets && source.charCodeAt(index) === 0x5B /* '[' */) {
1549 useBrackets = true;
1550 name = advance();
1551 }
1552
1553 if (!esutils.code.isIdentifierStart(source.charCodeAt(index))) {
1554 return null;
1555 }
1556
1557 name += scanIdentifier(last);
1558
1559 if (allowNestedParams) {
1560 while (source.charCodeAt(index) === 0x2E /* '.' */ ||
1561 source.charCodeAt(index) === 0x23 /* '#' */ ||
1562 source.charCodeAt(index) === 0x7E /* '~' */) {
1563 name += advance();
1564 name += scanIdentifier(last);
1565 }
1566 }
1567
1568 if (useBrackets) {
1569 // do we have a default value for this?
1570 if (source.charCodeAt(index) === 0x3D /* '=' */) {
1571
1572 // consume the '='' symbol
1573 name += advance();
1574 // scan in the default value
1575 while (index < last && source.charCodeAt(index) !== 0x5D /* ']' */) {
1576 name += advance();
1577 }
1578 }
1579
1580 if (index >= last || source.charCodeAt(index) !== 0x5D /* ']' */) {
1581 // we never found a closing ']'
1582 return null;
1583 }
1584
1585 // collect the last ']'
1586 name += advance();
1587 }
1588
1589 return name;
1590 }
1591
1592 function skipToTag() {
1593 while (index < length && source.charCodeAt(index) !== 0x40 /* '@' */) {
1594 advance();
1595 }
1596 if (index >= length) {
1597 return false;
1598 }
1599 assert(source.charCodeAt(index) === 0x40 /* '@' */);
1600 return true;
1601 }
1602
1603 function TagParser(options, title) {
1604 this._options = options;
1605 this._title = title;
1606 this._tag = {
1607 title: title,
1608 description: null
1609 };
1610 if (this._options.lineNumbers) {
1611 this._tag.lineNumber = lineNumber;
1612 }
1613 this._last = 0;
1614 // space to save special information for title parsers.
1615 this._extra = { };
1616 }
1617
1618 // addError(err, ...)
1619 TagParser.prototype.addError = function addError(errorText) {
1620 var args = Array.prototype.slice.call(arguments, 1),
1621 msg = errorText.replace(
1622 /%(\d)/g,
1623 function (whole, index) {
1624 assert(index < args.length, 'Message reference must be in range');
1625 return args[index];
1626 }
1627 );
1628
1629 if (!this._tag.errors) {
1630 this._tag.errors = [];
1631 }
1632 if (strict) {
1633 throwError(msg);
1634 }
1635 this._tag.errors.push(msg);
1636 return recoverable;
1637 };
1638
1639 TagParser.prototype.parseType = function () {
1640 // type required titles
1641 if (isTypeParameterRequired(this._title)) {
1642 try {
1643 this._tag.type = parseType(this._title, this._last);
1644 if (!this._tag.type) {
1645 if (!isParamTitle(this._title)) {
1646 if (!this.addError('Missing or invalid tag type')) {
1647 return false;
1648 }
1649 }
1650 }
1651 } catch (error) {
1652 this._tag.type = null;
1653 if (!this.addError(error.message)) {
1654 return false;
1655 }
1656 }
1657 } else if (isAllowedType(this._title)) {
1658 // optional types
1659 try {
1660 this._tag.type = parseType(this._title, this._last);
1661 } catch (e) {
1662 //For optional types, lets drop the thrown error when we hit the end of the file
1663 }
1664 }
1665 return true;
1666 };
1667
1668 TagParser.prototype._parseNamePath = function (optional) {
1669 var name;
1670 name = parseName(this._last, sloppy && isParamTitle(this._title), true);
1671 if (!name) {
1672 if (!optional) {
1673 if (!this.addError('Missing or invalid tag name')) {
1674 return false;
1675 }
1676 }
1677 }
1678 this._tag.name = name;
1679 return true;
1680 };
1681
1682 TagParser.prototype.parseNamePath = function () {
1683 return this._parseNamePath(false);
1684 };
1685
1686 TagParser.prototype.parseNamePathOptional = function () {
1687 return this._parseNamePath(true);
1688 };
1689
1690
1691 TagParser.prototype.parseName = function () {
1692 var assign, name;
1693
1694 // param, property requires name
1695 if (isAllowedName(this._title)) {
1696 this._tag.name = parseName(this._last, sloppy && isParamTitle(this._title), isAllowedNested(this._title));
1697 if (!this._tag.name) {
1698 if (!isNameParameterRequired(this._title)) {
1699 return true;
1700 }
1701
1702 // it's possible the name has already been parsed but interpreted as a type
1703 // it's also possible this is a sloppy declaration, in which case it will be
1704 // fixed at the end
1705 if (isParamTitle(this._title) && this._tag.type && this._tag.type.name) {
1706 this._extra.name = this._tag.type;
1707 this._tag.name = this._tag.type.name;
1708 this._tag.type = null;
1709 } else {
1710 if (!this.addError('Missing or invalid tag name')) {
1711 return false;
1712 }
1713 }
1714 } else {
1715 name = this._tag.name;
1716 if (name.charAt(0) === '[' && name.charAt(name.length - 1) === ']') {
1717 // extract the default value if there is one
1718 // example: @param {string} [somebody=John Doe] description
1719 assign = name.substring(1, name.length - 1).split('=');
1720 if (assign[1]) {
1721 this._tag['default'] = assign[1];
1722 }
1723 this._tag.name = assign[0];
1724
1725 // convert to an optional type
1726 if (this._tag.type && this._tag.type.type !== 'OptionalType') {
1727 this._tag.type = {
1728 type: 'OptionalType',
1729 expression: this._tag.type
1730 };
1731 }
1732 }
1733 }
1734 }
1735
1736 return true;
1737 };
1738
1739 TagParser.prototype.parseDescription = function parseDescription() {
1740 var description = trim(sliceSource(source, index, this._last));
1741 if (description) {
1742 if ((/^-\s+/).test(description)) {
1743 description = description.substring(2);
1744 }
1745 this._tag.description = description;
1746 }
1747 return true;
1748 };
1749
1750 TagParser.prototype.parseKind = function parseKind() {
1751 var kind, kinds;
1752 kinds = {
1753 'class': true,
1754 'constant': true,
1755 'event': true,
1756 'external': true,
1757 'file': true,
1758 'function': true,
1759 'member': true,
1760 'mixin': true,
1761 'module': true,
1762 'namespace': true,
1763 'typedef': true
1764 };
1765 kind = trim(sliceSource(source, index, this._last));
1766 this._tag.kind = kind;
1767 if (!hasOwnProperty(kinds, kind)) {
1768 if (!this.addError('Invalid kind name \'%0\'', kind)) {
1769 return false;
1770 }
1771 }
1772 return true;
1773 };
1774
1775 TagParser.prototype.parseAccess = function parseAccess() {
1776 var access;
1777 access = trim(sliceSource(source, index, this._last));
1778 this._tag.access = access;
1779 if (access !== 'private' && access !== 'protected' && access !== 'public') {
1780 if (!this.addError('Invalid access name \'%0\'', access)) {
1781 return false;
1782 }
1783 }
1784 return true;
1785 };
1786
1787 TagParser.prototype.parseVariation = function parseVariation() {
1788 var variation, text;
1789 text = trim(sliceSource(source, index, this._last));
1790 variation = parseFloat(text, 10);
1791 this._tag.variation = variation;
1792 if (isNaN(variation)) {
1793 if (!this.addError('Invalid variation \'%0\'', text)) {
1794 return false;
1795 }
1796 }
1797 return true;
1798 };
1799
1800 TagParser.prototype.ensureEnd = function () {
1801 var shouldBeEmpty = trim(sliceSource(source, index, this._last));
1802 if (shouldBeEmpty) {
1803 if (!this.addError('Unknown content \'%0\'', shouldBeEmpty)) {
1804 return false;
1805 }
1806 }
1807 return true;
1808 };
1809
1810 TagParser.prototype.epilogue = function epilogue() {
1811 var description;
1812
1813 description = this._tag.description;
1814 // un-fix potentially sloppy declaration
1815 if (isParamTitle(this._title) && !this._tag.type && description && description.charAt(0) === '[') {
1816 this._tag.type = this._extra.name;
1817 this._tag.name = undefined;
1818
1819 if (!sloppy) {
1820 if (!this.addError('Missing or invalid tag name')) {
1821 return false;
1822 }
1823 }
1824 }
1825
1826 return true;
1827 };
1828
1829 Rules = {
1830 // http://usejsdoc.org/tags-access.html
1831 'access': ['parseAccess'],
1832 // http://usejsdoc.org/tags-alias.html
1833 'alias': ['parseNamePath', 'ensureEnd'],
1834 // http://usejsdoc.org/tags-augments.html
1835 'augments': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
1836 // http://usejsdoc.org/tags-constructor.html
1837 'constructor': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
1838 // Synonym: http://usejsdoc.org/tags-constructor.html
1839 'class': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
1840 // Synonym: http://usejsdoc.org/tags-extends.html
1841 'extends': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
1842 // http://usejsdoc.org/tags-deprecated.html
1843 'deprecated': ['parseDescription'],
1844 // http://usejsdoc.org/tags-global.html
1845 'global': ['ensureEnd'],
1846 // http://usejsdoc.org/tags-inner.html
1847 'inner': ['ensureEnd'],
1848 // http://usejsdoc.org/tags-instance.html
1849 'instance': ['ensureEnd'],
1850 // http://usejsdoc.org/tags-kind.html
1851 'kind': ['parseKind'],
1852 // http://usejsdoc.org/tags-mixes.html
1853 'mixes': ['parseNamePath', 'ensureEnd'],
1854 // http://usejsdoc.org/tags-mixin.html
1855 'mixin': ['parseNamePathOptional', 'ensureEnd'],
1856 // http://usejsdoc.org/tags-member.html
1857 'member': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
1858 // http://usejsdoc.org/tags-method.html
1859 'method': ['parseNamePathOptional', 'ensureEnd'],
1860 // http://usejsdoc.org/tags-module.html
1861 'module': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
1862 // Synonym: http://usejsdoc.org/tags-method.html
1863 'func': ['parseNamePathOptional', 'ensureEnd'],
1864 // Synonym: http://usejsdoc.org/tags-method.html
1865 'function': ['parseNamePathOptional', 'ensureEnd'],
1866 // Synonym: http://usejsdoc.org/tags-member.html
1867 'var': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
1868 // http://usejsdoc.org/tags-name.html
1869 'name': ['parseNamePath', 'ensureEnd'],
1870 // http://usejsdoc.org/tags-namespace.html
1871 'namespace': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
1872 // http://usejsdoc.org/tags-private.html
1873 'private': ['parseType', 'parseDescription'],
1874 // http://usejsdoc.org/tags-protected.html
1875 'protected': ['parseType', 'parseDescription'],
1876 // http://usejsdoc.org/tags-public.html
1877 'public': ['parseType', 'parseDescription'],
1878 // http://usejsdoc.org/tags-readonly.html
1879 'readonly': ['ensureEnd'],
1880 // http://usejsdoc.org/tags-requires.html
1881 'requires': ['parseNamePath', 'ensureEnd'],
1882 // http://usejsdoc.org/tags-since.html
1883 'since': ['parseDescription'],
1884 // http://usejsdoc.org/tags-static.html
1885 'static': ['ensureEnd'],
1886 // http://usejsdoc.org/tags-summary.html
1887 'summary': ['parseDescription'],
1888 // http://usejsdoc.org/tags-this.html
1889 'this': ['parseNamePath', 'ensureEnd'],
1890 // http://usejsdoc.org/tags-todo.html
1891 'todo': ['parseDescription'],
1892 // http://usejsdoc.org/tags-variation.html
1893 'variation': ['parseVariation'],
1894 // http://usejsdoc.org/tags-version.html
1895 'version': ['parseDescription']
1896 };
1897
1898 TagParser.prototype.parse = function parse() {
1899 var i, iz, sequences, method;
1900
1901 // empty title
1902 if (!this._title) {
1903 if (!this.addError('Missing or invalid title')) {
1904 return null;
1905 }
1906 }
1907
1908 // Seek to content last index.
1909 this._last = seekContent(this._title);
1910
1911 if (hasOwnProperty(Rules, this._title)) {
1912 sequences = Rules[this._title];
1913 } else {
1914 // default sequences
1915 sequences = ['parseType', 'parseName', 'parseDescription', 'epilogue'];
1916 }
1917
1918 for (i = 0, iz = sequences.length; i < iz; ++i) {
1919 method = sequences[i];
1920 if (!this[method]()) {
1921 return null;
1922 }
1923 }
1924
1925 // Seek global index to end of this tag.
1926 index = this._last;
1927 return this._tag;
1928 };
1929
1930 function parseTag(options) {
1931 var title, parser;
1932
1933 // skip to tag
1934 if (!skipToTag()) {
1935 return null;
1936 }
1937
1938 // scan title
1939 title = scanTitle();
1940
1941 // construct tag parser
1942 parser = new TagParser(options, title);
1943 return parser.parse();
1944 }
1945
1946 //
1947 // Parse JSDoc
1948 //
1949
1950 function scanJSDocDescription() {
1951 var description = '', ch, atAllowed;
1952
1953 atAllowed = true;
1954 while (index < length) {
1955 ch = source.charCodeAt(index);
1956
1957 if (atAllowed && ch === 0x40 /* '@' */) {
1958 break;
1959 }
1960
1961 if (esutils.code.isLineTerminator(ch)) {
1962 atAllowed = true;
1963 } else if (atAllowed && !esutils.code.isWhiteSpace(ch)) {
1964 atAllowed = false;
1965 }
1966
1967 description += advance();
1968 }
1969 return trim(description);
1970 }
1971
1972 function parse(comment, options) {
1973 var tags = [], tag, description, interestingTags, i, iz;
1974
1975 if (options === undefined) {
1976 options = {};
1977 }
1978
1979 if (typeof options.unwrap === 'boolean' && options.unwrap) {
1980 source = unwrapComment(comment);
1981 } else {
1982 source = comment;
1983 }
1984
1985 // array of relevant tags
1986 if (options.tags) {
1987 if (isArray(options.tags)) {
1988 interestingTags = { };
1989 for (i = 0, iz = options.tags.length; i < iz; i++) {
1990 if (typeof options.tags[i] === 'string') {
1991 interestingTags[options.tags[i]] = true;
1992 } else {
1993 throwError('Invalid "tags" parameter: ' + options.tags);
1994 }
1995 }
1996 } else {
1997 throwError('Invalid "tags" parameter: ' + options.tags);
1998 }
1999 }
2000
2001 length = source.length;
2002 index = 0;
2003 lineNumber = 0;
2004 recoverable = options.recoverable;
2005 sloppy = options.sloppy;
2006 strict = options.strict;
2007
2008 description = scanJSDocDescription();
2009
2010 while (true) {
2011 tag = parseTag(options);
2012 if (!tag) {
2013 break;
2014 }
2015 if (!interestingTags || interestingTags.hasOwnProperty(tag.title)) {
2016 tags.push(tag);
2017 }
2018 }
2019
2020 return {
2021 description: description,
2022 tags: tags
2023 };
2024 }
2025 exports.parse = parse;
2026 }(jsdoc = {}));
2027
2028 exports.version = VERSION;
2029 exports.parse = jsdoc.parse;
2030 exports.parseType = typed.parseType;
2031 exports.parseParamType = typed.parseParamType;
2032 exports.unwrapComment = unwrapComment;
2033 exports.Syntax = shallowCopy(typed.Syntax);
2034 exports.Error = DoctrineError;
2035 exports.type = {
2036 Syntax: exports.Syntax,
2037 parseType: typed.parseType,
2038 parseParamType: typed.parseParamType,
2039 stringify: typed.stringify
2040 };
2041}());
2042/* vim: set sw=4 ts=4 et tw=80 : */