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