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