.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / doctrine / lib / typed.js
1 /*
2  * @fileoverview Type expression parser.
3  * @author Yusuke Suzuki <utatane.tea@gmail.com>
4  * @author Dan Tao <daniel.tao@gmail.com>
5  * @author Andrew Eisenberg <andrew@eisenberg.as>
6  */
7
8 // "typed", the Type Expression Parser for doctrine.
9
10 (function () {
11     'use strict';
12
13     var Syntax,
14         Token,
15         source,
16         length,
17         index,
18         previous,
19         token,
20         value,
21         esutils,
22         utility,
23         rangeOffset,
24         addRange;
25
26     esutils = require('esutils');
27     utility = require('./utility');
28
29     Syntax = {
30         NullableLiteral: 'NullableLiteral',
31         AllLiteral: 'AllLiteral',
32         NullLiteral: 'NullLiteral',
33         UndefinedLiteral: 'UndefinedLiteral',
34         VoidLiteral: 'VoidLiteral',
35         UnionType: 'UnionType',
36         ArrayType: 'ArrayType',
37         RecordType: 'RecordType',
38         FieldType: 'FieldType',
39         FunctionType: 'FunctionType',
40         ParameterType: 'ParameterType',
41         RestType: 'RestType',
42         NonNullableType: 'NonNullableType',
43         OptionalType: 'OptionalType',
44         NullableType: 'NullableType',
45         NameExpression: 'NameExpression',
46         TypeApplication: 'TypeApplication',
47         StringLiteralType: 'StringLiteralType',
48         NumericLiteralType: 'NumericLiteralType',
49         BooleanLiteralType: 'BooleanLiteralType'
50     };
51
52     Token = {
53         ILLEGAL: 0,    // ILLEGAL
54         DOT_LT: 1,     // .<
55         REST: 2,       // ...
56         LT: 3,         // <
57         GT: 4,         // >
58         LPAREN: 5,     // (
59         RPAREN: 6,     // )
60         LBRACE: 7,     // {
61         RBRACE: 8,     // }
62         LBRACK: 9,    // [
63         RBRACK: 10,    // ]
64         COMMA: 11,     // ,
65         COLON: 12,     // :
66         STAR: 13,      // *
67         PIPE: 14,      // |
68         QUESTION: 15,  // ?
69         BANG: 16,      // !
70         EQUAL: 17,     // =
71         NAME: 18,      // name token
72         STRING: 19,    // string
73         NUMBER: 20,    // number
74         EOF: 21
75     };
76
77     function isTypeName(ch) {
78         return '><(){}[],:*|?!='.indexOf(String.fromCharCode(ch)) === -1 && !esutils.code.isWhiteSpace(ch) && !esutils.code.isLineTerminator(ch);
79     }
80
81     function Context(previous, index, token, value) {
82         this._previous = previous;
83         this._index = index;
84         this._token = token;
85         this._value = value;
86     }
87
88     Context.prototype.restore = function () {
89         previous = this._previous;
90         index = this._index;
91         token = this._token;
92         value = this._value;
93     };
94
95     Context.save = function () {
96         return new Context(previous, index, token, value);
97     };
98
99     function maybeAddRange(node, range) {
100         if (addRange) {
101             node.range = [range[0] + rangeOffset, range[1] + rangeOffset];
102         }
103         return node;
104     }
105
106     function advance() {
107         var ch = source.charAt(index);
108         index += 1;
109         return ch;
110     }
111
112     function scanHexEscape(prefix) {
113         var i, len, ch, code = 0;
114
115         len = (prefix === 'u') ? 4 : 2;
116         for (i = 0; i < len; ++i) {
117             if (index < length && esutils.code.isHexDigit(source.charCodeAt(index))) {
118                 ch = advance();
119                 code = code * 16 + '0123456789abcdef'.indexOf(ch.toLowerCase());
120             } else {
121                 return '';
122             }
123         }
124         return String.fromCharCode(code);
125     }
126
127     function scanString() {
128         var str = '', quote, ch, code, unescaped, restore; //TODO review removal octal = false
129         quote = source.charAt(index);
130         ++index;
131
132         while (index < length) {
133             ch = advance();
134
135             if (ch === quote) {
136                 quote = '';
137                 break;
138             } else if (ch === '\\') {
139                 ch = advance();
140                 if (!esutils.code.isLineTerminator(ch.charCodeAt(0))) {
141                     switch (ch) {
142                     case 'n':
143                         str += '\n';
144                         break;
145                     case 'r':
146                         str += '\r';
147                         break;
148                     case 't':
149                         str += '\t';
150                         break;
151                     case 'u':
152                     case 'x':
153                         restore = index;
154                         unescaped = scanHexEscape(ch);
155                         if (unescaped) {
156                             str += unescaped;
157                         } else {
158                             index = restore;
159                             str += ch;
160                         }
161                         break;
162                     case 'b':
163                         str += '\b';
164                         break;
165                     case 'f':
166                         str += '\f';
167                         break;
168                     case 'v':
169                         str += '\v';
170                         break;
171
172                     default:
173                         if (esutils.code.isOctalDigit(ch.charCodeAt(0))) {
174                             code = '01234567'.indexOf(ch);
175
176                             // \0 is not octal escape sequence
177                             // Deprecating unused code. TODO review removal
178                             //if (code !== 0) {
179                             //    octal = true;
180                             //}
181
182                             if (index < length && esutils.code.isOctalDigit(source.charCodeAt(index))) {
183                                 //TODO Review Removal octal = true;
184                                 code = code * 8 + '01234567'.indexOf(advance());
185
186                                 // 3 digits are only allowed when string starts
187                                 // with 0, 1, 2, 3
188                                 if ('0123'.indexOf(ch) >= 0 &&
189                                         index < length &&
190                                         esutils.code.isOctalDigit(source.charCodeAt(index))) {
191                                     code = code * 8 + '01234567'.indexOf(advance());
192                                 }
193                             }
194                             str += String.fromCharCode(code);
195                         } else {
196                             str += ch;
197                         }
198                         break;
199                     }
200                 } else {
201                     if (ch ===  '\r' && source.charCodeAt(index) === 0x0A  /* '\n' */) {
202                         ++index;
203                     }
204                 }
205             } else if (esutils.code.isLineTerminator(ch.charCodeAt(0))) {
206                 break;
207             } else {
208                 str += ch;
209             }
210         }
211
212         if (quote !== '') {
213             utility.throwError('unexpected quote');
214         }
215
216         value = str;
217         return Token.STRING;
218     }
219
220     function scanNumber() {
221         var number, ch;
222
223         number = '';
224         ch = source.charCodeAt(index);
225
226         if (ch !== 0x2E  /* '.' */) {
227             number = advance();
228             ch = source.charCodeAt(index);
229
230             if (number === '0') {
231                 if (ch === 0x78  /* 'x' */ || ch === 0x58  /* 'X' */) {
232                     number += advance();
233                     while (index < length) {
234                         ch = source.charCodeAt(index);
235                         if (!esutils.code.isHexDigit(ch)) {
236                             break;
237                         }
238                         number += advance();
239                     }
240
241                     if (number.length <= 2) {
242                         // only 0x
243                         utility.throwError('unexpected token');
244                     }
245
246                     if (index < length) {
247                         ch = source.charCodeAt(index);
248                         if (esutils.code.isIdentifierStartES5(ch)) {
249                             utility.throwError('unexpected token');
250                         }
251                     }
252                     value = parseInt(number, 16);
253                     return Token.NUMBER;
254                 }
255
256                 if (esutils.code.isOctalDigit(ch)) {
257                     number += advance();
258                     while (index < length) {
259                         ch = source.charCodeAt(index);
260                         if (!esutils.code.isOctalDigit(ch)) {
261                             break;
262                         }
263                         number += advance();
264                     }
265
266                     if (index < length) {
267                         ch = source.charCodeAt(index);
268                         if (esutils.code.isIdentifierStartES5(ch) || esutils.code.isDecimalDigit(ch)) {
269                             utility.throwError('unexpected token');
270                         }
271                     }
272                     value = parseInt(number, 8);
273                     return Token.NUMBER;
274                 }
275
276                 if (esutils.code.isDecimalDigit(ch)) {
277                     utility.throwError('unexpected token');
278                 }
279             }
280
281             while (index < length) {
282                 ch = source.charCodeAt(index);
283                 if (!esutils.code.isDecimalDigit(ch)) {
284                     break;
285                 }
286                 number += advance();
287             }
288         }
289
290         if (ch === 0x2E  /* '.' */) {
291             number += advance();
292             while (index < length) {
293                 ch = source.charCodeAt(index);
294                 if (!esutils.code.isDecimalDigit(ch)) {
295                     break;
296                 }
297                 number += advance();
298             }
299         }
300
301         if (ch === 0x65  /* 'e' */ || ch === 0x45  /* 'E' */) {
302             number += advance();
303
304             ch = source.charCodeAt(index);
305             if (ch === 0x2B  /* '+' */ || ch === 0x2D  /* '-' */) {
306                 number += advance();
307             }
308
309             ch = source.charCodeAt(index);
310             if (esutils.code.isDecimalDigit(ch)) {
311                 number += advance();
312                 while (index < length) {
313                     ch = source.charCodeAt(index);
314                     if (!esutils.code.isDecimalDigit(ch)) {
315                         break;
316                     }
317                     number += advance();
318                 }
319             } else {
320                 utility.throwError('unexpected token');
321             }
322         }
323
324         if (index < length) {
325             ch = source.charCodeAt(index);
326             if (esutils.code.isIdentifierStartES5(ch)) {
327                 utility.throwError('unexpected token');
328             }
329         }
330
331         value = parseFloat(number);
332         return Token.NUMBER;
333     }
334
335
336     function scanTypeName() {
337         var ch, ch2;
338
339         value = advance();
340         while (index < length && isTypeName(source.charCodeAt(index))) {
341             ch = source.charCodeAt(index);
342             if (ch === 0x2E  /* '.' */) {
343                 if ((index + 1) >= length) {
344                     return Token.ILLEGAL;
345                 }
346                 ch2 = source.charCodeAt(index + 1);
347                 if (ch2 === 0x3C  /* '<' */) {
348                     break;
349                 }
350             }
351             value += advance();
352         }
353         return Token.NAME;
354     }
355
356     function next() {
357         var ch;
358
359         previous = index;
360
361         while (index < length && esutils.code.isWhiteSpace(source.charCodeAt(index))) {
362             advance();
363         }
364         if (index >= length) {
365             token = Token.EOF;
366             return token;
367         }
368
369         ch = source.charCodeAt(index);
370         switch (ch) {
371         case 0x27:  /* ''' */
372         case 0x22:  /* '"' */
373             token = scanString();
374             return token;
375
376         case 0x3A:  /* ':' */
377             advance();
378             token = Token.COLON;
379             return token;
380
381         case 0x2C:  /* ',' */
382             advance();
383             token = Token.COMMA;
384             return token;
385
386         case 0x28:  /* '(' */
387             advance();
388             token = Token.LPAREN;
389             return token;
390
391         case 0x29:  /* ')' */
392             advance();
393             token = Token.RPAREN;
394             return token;
395
396         case 0x5B:  /* '[' */
397             advance();
398             token = Token.LBRACK;
399             return token;
400
401         case 0x5D:  /* ']' */
402             advance();
403             token = Token.RBRACK;
404             return token;
405
406         case 0x7B:  /* '{' */
407             advance();
408             token = Token.LBRACE;
409             return token;
410
411         case 0x7D:  /* '}' */
412             advance();
413             token = Token.RBRACE;
414             return token;
415
416         case 0x2E:  /* '.' */
417             if (index + 1 < length) {
418                 ch = source.charCodeAt(index + 1);
419                 if (ch === 0x3C  /* '<' */) {
420                     advance();  // '.'
421                     advance();  // '<'
422                     token = Token.DOT_LT;
423                     return token;
424                 }
425
426                 if (ch === 0x2E  /* '.' */ && index + 2 < length && source.charCodeAt(index + 2) === 0x2E  /* '.' */) {
427                     advance();  // '.'
428                     advance();  // '.'
429                     advance();  // '.'
430                     token = Token.REST;
431                     return token;
432                 }
433
434                 if (esutils.code.isDecimalDigit(ch)) {
435                     token = scanNumber();
436                     return token;
437                 }
438             }
439             token = Token.ILLEGAL;
440             return token;
441
442         case 0x3C:  /* '<' */
443             advance();
444             token = Token.LT;
445             return token;
446
447         case 0x3E:  /* '>' */
448             advance();
449             token = Token.GT;
450             return token;
451
452         case 0x2A:  /* '*' */
453             advance();
454             token = Token.STAR;
455             return token;
456
457         case 0x7C:  /* '|' */
458             advance();
459             token = Token.PIPE;
460             return token;
461
462         case 0x3F:  /* '?' */
463             advance();
464             token = Token.QUESTION;
465             return token;
466
467         case 0x21:  /* '!' */
468             advance();
469             token = Token.BANG;
470             return token;
471
472         case 0x3D:  /* '=' */
473             advance();
474             token = Token.EQUAL;
475             return token;
476
477         case 0x2D: /* '-' */
478             token = scanNumber();
479             return token;
480
481         default:
482             if (esutils.code.isDecimalDigit(ch)) {
483                 token = scanNumber();
484                 return token;
485             }
486
487             // type string permits following case,
488             //
489             // namespace.module.MyClass
490             //
491             // this reduced 1 token TK_NAME
492             utility.assert(isTypeName(ch));
493             token = scanTypeName();
494             return token;
495         }
496     }
497
498     function consume(target, text) {
499         utility.assert(token === target, text || 'consumed token not matched');
500         next();
501     }
502
503     function expect(target, message) {
504         if (token !== target) {
505             utility.throwError(message || 'unexpected token');
506         }
507         next();
508     }
509
510     // UnionType := '(' TypeUnionList ')'
511     //
512     // TypeUnionList :=
513     //     <<empty>>
514     //   | NonemptyTypeUnionList
515     //
516     // NonemptyTypeUnionList :=
517     //     TypeExpression
518     //   | TypeExpression '|' NonemptyTypeUnionList
519     function parseUnionType() {
520         var elements, startIndex = index - 1;
521         consume(Token.LPAREN, 'UnionType should start with (');
522         elements = [];
523         if (token !== Token.RPAREN) {
524             while (true) {
525                 elements.push(parseTypeExpression());
526                 if (token === Token.RPAREN) {
527                     break;
528                 }
529                 expect(Token.PIPE);
530             }
531         }
532         consume(Token.RPAREN, 'UnionType should end with )');
533         return maybeAddRange({
534             type: Syntax.UnionType,
535             elements: elements
536         }, [startIndex, previous]);
537     }
538
539     // ArrayType := '[' ElementTypeList ']'
540     //
541     // ElementTypeList :=
542     //     <<empty>>
543     //  | TypeExpression
544     //  | '...' TypeExpression
545     //  | TypeExpression ',' ElementTypeList
546     function parseArrayType() {
547         var elements, startIndex = index - 1, restStartIndex;
548         consume(Token.LBRACK, 'ArrayType should start with [');
549         elements = [];
550         while (token !== Token.RBRACK) {
551             if (token === Token.REST) {
552                 restStartIndex = index - 3;
553                 consume(Token.REST);
554                 elements.push(maybeAddRange({
555                     type: Syntax.RestType,
556                     expression: parseTypeExpression()
557                 }, [restStartIndex, previous]));
558                 break;
559             } else {
560                 elements.push(parseTypeExpression());
561             }
562             if (token !== Token.RBRACK) {
563                 expect(Token.COMMA);
564             }
565         }
566         expect(Token.RBRACK);
567         return maybeAddRange({
568             type: Syntax.ArrayType,
569             elements: elements
570         }, [startIndex, previous]);
571     }
572
573     function parseFieldName() {
574         var v = value;
575         if (token === Token.NAME || token === Token.STRING) {
576             next();
577             return v;
578         }
579
580         if (token === Token.NUMBER) {
581             consume(Token.NUMBER);
582             return String(v);
583         }
584
585         utility.throwError('unexpected token');
586     }
587
588     // FieldType :=
589     //     FieldName
590     //   | FieldName ':' TypeExpression
591     //
592     // FieldName :=
593     //     NameExpression
594     //   | StringLiteral
595     //   | NumberLiteral
596     //   | ReservedIdentifier
597     function parseFieldType() {
598         var key, rangeStart = previous;
599
600         key = parseFieldName();
601         if (token === Token.COLON) {
602             consume(Token.COLON);
603             return maybeAddRange({
604                 type: Syntax.FieldType,
605                 key: key,
606                 value: parseTypeExpression()
607             }, [rangeStart, previous]);
608         }
609         return maybeAddRange({
610             type: Syntax.FieldType,
611             key: key,
612             value: null
613         }, [rangeStart, previous]);
614     }
615
616     // RecordType := '{' FieldTypeList '}'
617     //
618     // FieldTypeList :=
619     //     <<empty>>
620     //   | FieldType
621     //   | FieldType ',' FieldTypeList
622     function parseRecordType() {
623         var fields, rangeStart = index - 1, rangeEnd;
624
625         consume(Token.LBRACE, 'RecordType should start with {');
626         fields = [];
627         if (token === Token.COMMA) {
628             consume(Token.COMMA);
629         } else {
630             while (token !== Token.RBRACE) {
631                 fields.push(parseFieldType());
632                 if (token !== Token.RBRACE) {
633                     expect(Token.COMMA);
634                 }
635             }
636         }
637         rangeEnd = index;
638         expect(Token.RBRACE);
639         return maybeAddRange({
640             type: Syntax.RecordType,
641             fields: fields
642         }, [rangeStart, rangeEnd]);
643     }
644
645     // NameExpression :=
646     //    Identifier
647     //  | TagIdentifier ':' Identifier
648     //
649     // Tag identifier is one of "module", "external" or "event"
650     // Identifier is the same as Token.NAME, including any dots, something like
651     // namespace.module.MyClass
652     function parseNameExpression() {
653         var name = value, rangeStart = index - name.length;
654         expect(Token.NAME);
655
656         if (token === Token.COLON && (
657                 name === 'module' ||
658                 name === 'external' ||
659                 name === 'event')) {
660             consume(Token.COLON);
661             name += ':' + value;
662             expect(Token.NAME);
663         }
664
665         return maybeAddRange({
666             type: Syntax.NameExpression,
667             name: name
668         }, [rangeStart, previous]);
669     }
670
671     // TypeExpressionList :=
672     //     TopLevelTypeExpression
673     //   | TopLevelTypeExpression ',' TypeExpressionList
674     function parseTypeExpressionList() {
675         var elements = [];
676
677         elements.push(parseTop());
678         while (token === Token.COMMA) {
679             consume(Token.COMMA);
680             elements.push(parseTop());
681         }
682         return elements;
683     }
684
685     // TypeName :=
686     //     NameExpression
687     //   | NameExpression TypeApplication
688     //
689     // TypeApplication :=
690     //     '.<' TypeExpressionList '>'
691     //   | '<' TypeExpressionList '>'   // this is extension of doctrine
692     function parseTypeName() {
693         var expr, applications, startIndex = index - value.length;
694
695         expr = parseNameExpression();
696         if (token === Token.DOT_LT || token === Token.LT) {
697             next();
698             applications = parseTypeExpressionList();
699             expect(Token.GT);
700             return maybeAddRange({
701                 type: Syntax.TypeApplication,
702                 expression: expr,
703                 applications: applications
704             }, [startIndex, previous]);
705         }
706         return expr;
707     }
708
709     // ResultType :=
710     //     <<empty>>
711     //   | ':' void
712     //   | ':' TypeExpression
713     //
714     // BNF is above
715     // but, we remove <<empty>> pattern, so token is always TypeToken::COLON
716     function parseResultType() {
717         consume(Token.COLON, 'ResultType should start with :');
718         if (token === Token.NAME && value === 'void') {
719             consume(Token.NAME);
720             return {
721                 type: Syntax.VoidLiteral
722             };
723         }
724         return parseTypeExpression();
725     }
726
727     // ParametersType :=
728     //     RestParameterType
729     //   | NonRestParametersType
730     //   | NonRestParametersType ',' RestParameterType
731     //
732     // RestParameterType :=
733     //     '...'
734     //     '...' Identifier
735     //
736     // NonRestParametersType :=
737     //     ParameterType ',' NonRestParametersType
738     //   | ParameterType
739     //   | OptionalParametersType
740     //
741     // OptionalParametersType :=
742     //     OptionalParameterType
743     //   | OptionalParameterType, OptionalParametersType
744     //
745     // OptionalParameterType := ParameterType=
746     //
747     // ParameterType := TypeExpression | Identifier ':' TypeExpression
748     //
749     // Identifier is "new" or "this"
750     function parseParametersType() {
751         var params = [], optionalSequence = false, expr, rest = false, startIndex, restStartIndex = index - 3, nameStartIndex;
752
753         while (token !== Token.RPAREN) {
754             if (token === Token.REST) {
755                 // RestParameterType
756                 consume(Token.REST);
757                 rest = true;
758             }
759
760             startIndex = previous;
761
762             expr = parseTypeExpression();
763             if (expr.type === Syntax.NameExpression && token === Token.COLON) {
764                 nameStartIndex = previous - expr.name.length;
765                 // Identifier ':' TypeExpression
766                 consume(Token.COLON);
767                 expr = maybeAddRange({
768                     type: Syntax.ParameterType,
769                     name: expr.name,
770                     expression: parseTypeExpression()
771                 }, [nameStartIndex, previous]);
772             }
773             if (token === Token.EQUAL) {
774                 consume(Token.EQUAL);
775                 expr = maybeAddRange({
776                     type: Syntax.OptionalType,
777                     expression: expr
778                 }, [startIndex, previous]);
779                 optionalSequence = true;
780             } else {
781                 if (optionalSequence) {
782                     utility.throwError('unexpected token');
783                 }
784             }
785             if (rest) {
786                 expr = maybeAddRange({
787                     type: Syntax.RestType,
788                     expression: expr
789                 }, [restStartIndex, previous]);
790             }
791             params.push(expr);
792             if (token !== Token.RPAREN) {
793                 expect(Token.COMMA);
794             }
795         }
796         return params;
797     }
798
799     // FunctionType := 'function' FunctionSignatureType
800     //
801     // FunctionSignatureType :=
802     //   | TypeParameters '(' ')' ResultType
803     //   | TypeParameters '(' ParametersType ')' ResultType
804     //   | TypeParameters '(' 'this' ':' TypeName ')' ResultType
805     //   | TypeParameters '(' 'this' ':' TypeName ',' ParametersType ')' ResultType
806     function parseFunctionType() {
807         var isNew, thisBinding, params, result, fnType, startIndex = index - value.length;
808         utility.assert(token === Token.NAME && value === 'function', 'FunctionType should start with \'function\'');
809         consume(Token.NAME);
810
811         // Google Closure Compiler is not implementing TypeParameters.
812         // So we do not. if we don't get '(', we see it as error.
813         expect(Token.LPAREN);
814
815         isNew = false;
816         params = [];
817         thisBinding = null;
818         if (token !== Token.RPAREN) {
819             // ParametersType or 'this'
820             if (token === Token.NAME &&
821                     (value === 'this' || value === 'new')) {
822                 // 'this' or 'new'
823                 // 'new' is Closure Compiler extension
824                 isNew = value === 'new';
825                 consume(Token.NAME);
826                 expect(Token.COLON);
827                 thisBinding = parseTypeName();
828                 if (token === Token.COMMA) {
829                     consume(Token.COMMA);
830                     params = parseParametersType();
831                 }
832             } else {
833                 params = parseParametersType();
834             }
835         }
836
837         expect(Token.RPAREN);
838
839         result = null;
840         if (token === Token.COLON) {
841             result = parseResultType();
842         }
843
844         fnType = maybeAddRange({
845             type: Syntax.FunctionType,
846             params: params,
847             result: result
848         }, [startIndex, previous]);
849         if (thisBinding) {
850             // avoid adding null 'new' and 'this' properties
851             fnType['this'] = thisBinding;
852             if (isNew) {
853                 fnType['new'] = true;
854             }
855         }
856         return fnType;
857     }
858
859     // BasicTypeExpression :=
860     //     '*'
861     //   | 'null'
862     //   | 'undefined'
863     //   | TypeName
864     //   | FunctionType
865     //   | UnionType
866     //   | RecordType
867     //   | ArrayType
868     function parseBasicTypeExpression() {
869         var context, startIndex;
870         switch (token) {
871         case Token.STAR:
872             consume(Token.STAR);
873             return maybeAddRange({
874                 type: Syntax.AllLiteral
875             }, [previous - 1, previous]);
876
877         case Token.LPAREN:
878             return parseUnionType();
879
880         case Token.LBRACK:
881             return parseArrayType();
882
883         case Token.LBRACE:
884             return parseRecordType();
885
886         case Token.NAME:
887             startIndex = index - value.length;
888
889             if (value === 'null') {
890                 consume(Token.NAME);
891                 return maybeAddRange({
892                     type: Syntax.NullLiteral
893                 }, [startIndex, previous]);
894             }
895
896             if (value === 'undefined') {
897                 consume(Token.NAME);
898                 return maybeAddRange({
899                     type: Syntax.UndefinedLiteral
900                 }, [startIndex, previous]);
901             }
902
903             if (value === 'true' || value === 'false') {
904                 consume(Token.NAME);
905                 return maybeAddRange({
906                     type: Syntax.BooleanLiteralType,
907                     value: value === 'true'
908                 }, [startIndex, previous]);
909             }
910
911             context = Context.save();
912             if (value === 'function') {
913                 try {
914                     return parseFunctionType();
915                 } catch (e) {
916                     context.restore();
917                 }
918             }
919
920             return parseTypeName();
921
922         case Token.STRING:
923             next();
924             return maybeAddRange({
925                 type: Syntax.StringLiteralType,
926                 value: value
927             }, [previous - value.length - 2, previous]);
928
929         case Token.NUMBER:
930             next();
931             return maybeAddRange({
932                 type: Syntax.NumericLiteralType,
933                 value: value
934             }, [previous - String(value).length, previous]);
935
936         default:
937             utility.throwError('unexpected token');
938         }
939     }
940
941     // TypeExpression :=
942     //     BasicTypeExpression
943     //   | '?' BasicTypeExpression
944     //   | '!' BasicTypeExpression
945     //   | BasicTypeExpression '?'
946     //   | BasicTypeExpression '!'
947     //   | '?'
948     //   | BasicTypeExpression '[]'
949     function parseTypeExpression() {
950         var expr, rangeStart;
951
952         if (token === Token.QUESTION) {
953             rangeStart = index - 1;
954             consume(Token.QUESTION);
955             if (token === Token.COMMA || token === Token.EQUAL || token === Token.RBRACE ||
956                     token === Token.RPAREN || token === Token.PIPE || token === Token.EOF ||
957                     token === Token.RBRACK || token === Token.GT) {
958                 return maybeAddRange({
959                     type: Syntax.NullableLiteral
960                 }, [rangeStart, previous]);
961             }
962             return maybeAddRange({
963                 type: Syntax.NullableType,
964                 expression: parseBasicTypeExpression(),
965                 prefix: true
966             }, [rangeStart, previous]);
967         } else if (token === Token.BANG) {
968             rangeStart = index - 1;
969             consume(Token.BANG);
970             return maybeAddRange({
971                 type: Syntax.NonNullableType,
972                 expression: parseBasicTypeExpression(),
973                 prefix: true
974             }, [rangeStart, previous]);
975         } else {
976             rangeStart = previous;
977         }
978
979         expr = parseBasicTypeExpression();
980         if (token === Token.BANG) {
981             consume(Token.BANG);
982             return maybeAddRange({
983                 type: Syntax.NonNullableType,
984                 expression: expr,
985                 prefix: false
986             }, [rangeStart, previous]);
987         }
988
989         if (token === Token.QUESTION) {
990             consume(Token.QUESTION);
991             return maybeAddRange({
992                 type: Syntax.NullableType,
993                 expression: expr,
994                 prefix: false
995             }, [rangeStart, previous]);
996         }
997
998         if (token === Token.LBRACK) {
999             consume(Token.LBRACK);
1000             expect(Token.RBRACK, 'expected an array-style type declaration (' + value + '[])');
1001             return maybeAddRange({
1002                 type: Syntax.TypeApplication,
1003                 expression: maybeAddRange({
1004                     type: Syntax.NameExpression,
1005                     name: 'Array'
1006                 }, [rangeStart, previous]),
1007                 applications: [expr]
1008             }, [rangeStart, previous]);
1009         }
1010
1011         return expr;
1012     }
1013
1014     // TopLevelTypeExpression :=
1015     //      TypeExpression
1016     //    | TypeUnionList
1017     //
1018     // This rule is Google Closure Compiler extension, not ES4
1019     // like,
1020     //   { number | string }
1021     // If strict to ES4, we should write it as
1022     //   { (number|string) }
1023     function parseTop() {
1024         var expr, elements;
1025
1026         expr = parseTypeExpression();
1027         if (token !== Token.PIPE) {
1028             return expr;
1029         }
1030
1031         elements = [expr];
1032         consume(Token.PIPE);
1033         while (true) {
1034             elements.push(parseTypeExpression());
1035             if (token !== Token.PIPE) {
1036                 break;
1037             }
1038             consume(Token.PIPE);
1039         }
1040
1041         return maybeAddRange({
1042             type: Syntax.UnionType,
1043             elements: elements
1044         }, [0, index]);
1045     }
1046
1047     function parseTopParamType() {
1048         var expr;
1049
1050         if (token === Token.REST) {
1051             consume(Token.REST);
1052             return maybeAddRange({
1053                 type: Syntax.RestType,
1054                 expression: parseTop()
1055             }, [0, index]);
1056         }
1057
1058         expr = parseTop();
1059         if (token === Token.EQUAL) {
1060             consume(Token.EQUAL);
1061             return maybeAddRange({
1062                 type: Syntax.OptionalType,
1063                 expression: expr
1064             }, [0, index]);
1065         }
1066
1067         return expr;
1068     }
1069
1070     function parseType(src, opt) {
1071         var expr;
1072
1073         source = src;
1074         length = source.length;
1075         index = 0;
1076         previous = 0;
1077         addRange = opt && opt.range;
1078         rangeOffset = opt && opt.startIndex || 0;
1079
1080         next();
1081         expr = parseTop();
1082
1083         if (opt && opt.midstream) {
1084             return {
1085                 expression: expr,
1086                 index: previous
1087             };
1088         }
1089
1090         if (token !== Token.EOF) {
1091             utility.throwError('not reach to EOF');
1092         }
1093
1094         return expr;
1095     }
1096
1097     function parseParamType(src, opt) {
1098         var expr;
1099
1100         source = src;
1101         length = source.length;
1102         index = 0;
1103         previous = 0;
1104         addRange = opt && opt.range;
1105         rangeOffset = opt && opt.startIndex || 0;
1106
1107         next();
1108         expr = parseTopParamType();
1109
1110         if (opt && opt.midstream) {
1111             return {
1112                 expression: expr,
1113                 index: previous
1114             };
1115         }
1116
1117         if (token !== Token.EOF) {
1118             utility.throwError('not reach to EOF');
1119         }
1120
1121         return expr;
1122     }
1123
1124     function stringifyImpl(node, compact, topLevel) {
1125         var result, i, iz;
1126
1127         switch (node.type) {
1128         case Syntax.NullableLiteral:
1129             result = '?';
1130             break;
1131
1132         case Syntax.AllLiteral:
1133             result = '*';
1134             break;
1135
1136         case Syntax.NullLiteral:
1137             result = 'null';
1138             break;
1139
1140         case Syntax.UndefinedLiteral:
1141             result = 'undefined';
1142             break;
1143
1144         case Syntax.VoidLiteral:
1145             result = 'void';
1146             break;
1147
1148         case Syntax.UnionType:
1149             if (!topLevel) {
1150                 result = '(';
1151             } else {
1152                 result = '';
1153             }
1154
1155             for (i = 0, iz = node.elements.length; i < iz; ++i) {
1156                 result += stringifyImpl(node.elements[i], compact);
1157                 if ((i + 1) !== iz) {
1158                     result += compact ? '|' : ' | ';
1159                 }
1160             }
1161
1162             if (!topLevel) {
1163                 result += ')';
1164             }
1165             break;
1166
1167         case Syntax.ArrayType:
1168             result = '[';
1169             for (i = 0, iz = node.elements.length; i < iz; ++i) {
1170                 result += stringifyImpl(node.elements[i], compact);
1171                 if ((i + 1) !== iz) {
1172                     result += compact ? ',' : ', ';
1173                 }
1174             }
1175             result += ']';
1176             break;
1177
1178         case Syntax.RecordType:
1179             result = '{';
1180             for (i = 0, iz = node.fields.length; i < iz; ++i) {
1181                 result += stringifyImpl(node.fields[i], compact);
1182                 if ((i + 1) !== iz) {
1183                     result += compact ? ',' : ', ';
1184                 }
1185             }
1186             result += '}';
1187             break;
1188
1189         case Syntax.FieldType:
1190             if (node.value) {
1191                 result = node.key + (compact ? ':' : ': ') + stringifyImpl(node.value, compact);
1192             } else {
1193                 result = node.key;
1194             }
1195             break;
1196
1197         case Syntax.FunctionType:
1198             result = compact ? 'function(' : 'function (';
1199
1200             if (node['this']) {
1201                 if (node['new']) {
1202                     result += (compact ? 'new:' : 'new: ');
1203                 } else {
1204                     result += (compact ? 'this:' : 'this: ');
1205                 }
1206
1207                 result += stringifyImpl(node['this'], compact);
1208
1209                 if (node.params.length !== 0) {
1210                     result += compact ? ',' : ', ';
1211                 }
1212             }
1213
1214             for (i = 0, iz = node.params.length; i < iz; ++i) {
1215                 result += stringifyImpl(node.params[i], compact);
1216                 if ((i + 1) !== iz) {
1217                     result += compact ? ',' : ', ';
1218                 }
1219             }
1220
1221             result += ')';
1222
1223             if (node.result) {
1224                 result += (compact ? ':' : ': ') + stringifyImpl(node.result, compact);
1225             }
1226             break;
1227
1228         case Syntax.ParameterType:
1229             result = node.name + (compact ? ':' : ': ') + stringifyImpl(node.expression, compact);
1230             break;
1231
1232         case Syntax.RestType:
1233             result = '...';
1234             if (node.expression) {
1235                 result += stringifyImpl(node.expression, compact);
1236             }
1237             break;
1238
1239         case Syntax.NonNullableType:
1240             if (node.prefix) {
1241                 result = '!' + stringifyImpl(node.expression, compact);
1242             } else {
1243                 result = stringifyImpl(node.expression, compact) + '!';
1244             }
1245             break;
1246
1247         case Syntax.OptionalType:
1248             result = stringifyImpl(node.expression, compact) + '=';
1249             break;
1250
1251         case Syntax.NullableType:
1252             if (node.prefix) {
1253                 result = '?' + stringifyImpl(node.expression, compact);
1254             } else {
1255                 result = stringifyImpl(node.expression, compact) + '?';
1256             }
1257             break;
1258
1259         case Syntax.NameExpression:
1260             result = node.name;
1261             break;
1262
1263         case Syntax.TypeApplication:
1264             result = stringifyImpl(node.expression, compact) + '.<';
1265             for (i = 0, iz = node.applications.length; i < iz; ++i) {
1266                 result += stringifyImpl(node.applications[i], compact);
1267                 if ((i + 1) !== iz) {
1268                     result += compact ? ',' : ', ';
1269                 }
1270             }
1271             result += '>';
1272             break;
1273
1274         case Syntax.StringLiteralType:
1275             result = '"' + node.value + '"';
1276             break;
1277
1278         case Syntax.NumericLiteralType:
1279             result = String(node.value);
1280             break;
1281
1282         case Syntax.BooleanLiteralType:
1283             result = String(node.value);
1284             break;
1285
1286         default:
1287             utility.throwError('Unknown type ' + node.type);
1288         }
1289
1290         return result;
1291     }
1292
1293     function stringify(node, options) {
1294         if (options == null) {
1295             options = {};
1296         }
1297         return stringifyImpl(node, options.compact, options.topLevel);
1298     }
1299
1300     exports.parseType = parseType;
1301     exports.parseParamType = parseParamType;
1302     exports.stringify = stringify;
1303     exports.Syntax = Syntax;
1304 }());
1305 /* vim: set sw=4 ts=4 et tw=80 : */