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 | 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,
|
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,
|
72 | STRING: 19,
|
73 | NUMBER: 20,
|
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;
|
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 |
|
177 |
|
178 |
|
179 |
|
180 |
|
181 |
|
182 | if (index < length && esutils.code.isOctalDigit(source.charCodeAt(index))) {
|
183 |
|
184 | code = code * 8 + '01234567'.indexOf(advance());
|
185 |
|
186 |
|
187 |
|
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 ) {
|
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 || ch === 0x58 ) {
|
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 |
|
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 || ch === 0x45 ) {
|
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 |
|
488 |
|
489 |
|
490 |
|
491 |
|
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 |
|
511 |
|
512 |
|
513 |
|
514 |
|
515 |
|
516 |
|
517 |
|
518 |
|
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 |
|
540 |
|
541 |
|
542 |
|
543 |
|
544 |
|
545 |
|
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 |
|
589 |
|
590 |
|
591 |
|
592 |
|
593 |
|
594 |
|
595 |
|
596 |
|
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 |
|
617 |
|
618 |
|
619 |
|
620 |
|
621 |
|
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 |
|
646 |
|
647 |
|
648 |
|
649 |
|
650 |
|
651 |
|
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 |
|
672 |
|
673 |
|
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 |
|
686 |
|
687 |
|
688 |
|
689 |
|
690 |
|
691 |
|
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 |
|
710 |
|
711 |
|
712 |
|
713 |
|
714 |
|
715 |
|
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 |
|
728 |
|
729 |
|
730 |
|
731 |
|
732 |
|
733 |
|
734 |
|
735 |
|
736 |
|
737 |
|
738 |
|
739 |
|
740 |
|
741 |
|
742 |
|
743 |
|
744 |
|
745 |
|
746 |
|
747 |
|
748 |
|
749 |
|
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 |
|
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 |
|
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 |
|
800 |
|
801 |
|
802 |
|
803 |
|
804 |
|
805 |
|
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 |
|
812 |
|
813 | expect(Token.LPAREN);
|
814 |
|
815 | isNew = false;
|
816 | params = [];
|
817 | thisBinding = null;
|
818 | if (token !== Token.RPAREN) {
|
819 |
|
820 | if (token === Token.NAME &&
|
821 | (value === 'this' || value === 'new')) {
|
822 |
|
823 |
|
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 |
|
851 | fnType['this'] = thisBinding;
|
852 | if (isNew) {
|
853 | fnType['new'] = true;
|
854 | }
|
855 | }
|
856 | return fnType;
|
857 | }
|
858 |
|
859 |
|
860 |
|
861 |
|
862 |
|
863 |
|
864 |
|
865 |
|
866 |
|
867 |
|
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 |
|
942 |
|
943 |
|
944 |
|
945 |
|
946 |
|
947 |
|
948 |
|
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 |
|
1015 |
|
1016 |
|
1017 |
|
1018 |
|
1019 |
|
1020 |
|
1021 |
|
1022 |
|
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 |
|