| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305 | 
							- /*
 
-  * @fileoverview Type expression parser.
 
-  * @author Yusuke Suzuki <utatane.tea@gmail.com>
 
-  * @author Dan Tao <daniel.tao@gmail.com>
 
-  * @author Andrew Eisenberg <andrew@eisenberg.as>
 
-  */
 
- // "typed", the Type Expression Parser for doctrine.
 
- (function () {
 
-     'use strict';
 
-     var Syntax,
 
-         Token,
 
-         source,
 
-         length,
 
-         index,
 
-         previous,
 
-         token,
 
-         value,
 
-         esutils,
 
-         utility,
 
-         rangeOffset,
 
-         addRange;
 
-     esutils = require('esutils');
 
-     utility = require('./utility');
 
-     Syntax = {
 
-         NullableLiteral: 'NullableLiteral',
 
-         AllLiteral: 'AllLiteral',
 
-         NullLiteral: 'NullLiteral',
 
-         UndefinedLiteral: 'UndefinedLiteral',
 
-         VoidLiteral: 'VoidLiteral',
 
-         UnionType: 'UnionType',
 
-         ArrayType: 'ArrayType',
 
-         RecordType: 'RecordType',
 
-         FieldType: 'FieldType',
 
-         FunctionType: 'FunctionType',
 
-         ParameterType: 'ParameterType',
 
-         RestType: 'RestType',
 
-         NonNullableType: 'NonNullableType',
 
-         OptionalType: 'OptionalType',
 
-         NullableType: 'NullableType',
 
-         NameExpression: 'NameExpression',
 
-         TypeApplication: 'TypeApplication',
 
-         StringLiteralType: 'StringLiteralType',
 
-         NumericLiteralType: 'NumericLiteralType',
 
-         BooleanLiteralType: 'BooleanLiteralType'
 
-     };
 
-     Token = {
 
-         ILLEGAL: 0,    // ILLEGAL
 
-         DOT_LT: 1,     // .<
 
-         REST: 2,       // ...
 
-         LT: 3,         // <
 
-         GT: 4,         // >
 
-         LPAREN: 5,     // (
 
-         RPAREN: 6,     // )
 
-         LBRACE: 7,     // {
 
-         RBRACE: 8,     // }
 
-         LBRACK: 9,    // [
 
-         RBRACK: 10,    // ]
 
-         COMMA: 11,     // ,
 
-         COLON: 12,     // :
 
-         STAR: 13,      // *
 
-         PIPE: 14,      // |
 
-         QUESTION: 15,  // ?
 
-         BANG: 16,      // !
 
-         EQUAL: 17,     // =
 
-         NAME: 18,      // name token
 
-         STRING: 19,    // string
 
-         NUMBER: 20,    // number
 
-         EOF: 21
 
-     };
 
-     function isTypeName(ch) {
 
-         return '><(){}[],:*|?!='.indexOf(String.fromCharCode(ch)) === -1 && !esutils.code.isWhiteSpace(ch) && !esutils.code.isLineTerminator(ch);
 
-     }
 
-     function Context(previous, index, token, value) {
 
-         this._previous = previous;
 
-         this._index = index;
 
-         this._token = token;
 
-         this._value = value;
 
-     }
 
-     Context.prototype.restore = function () {
 
-         previous = this._previous;
 
-         index = this._index;
 
-         token = this._token;
 
-         value = this._value;
 
-     };
 
-     Context.save = function () {
 
-         return new Context(previous, index, token, value);
 
-     };
 
-     function maybeAddRange(node, range) {
 
-         if (addRange) {
 
-             node.range = [range[0] + rangeOffset, range[1] + rangeOffset];
 
-         }
 
-         return node;
 
-     }
 
-     function advance() {
 
-         var ch = source.charAt(index);
 
-         index += 1;
 
-         return ch;
 
-     }
 
-     function scanHexEscape(prefix) {
 
-         var i, len, ch, code = 0;
 
-         len = (prefix === 'u') ? 4 : 2;
 
-         for (i = 0; i < len; ++i) {
 
-             if (index < length && esutils.code.isHexDigit(source.charCodeAt(index))) {
 
-                 ch = advance();
 
-                 code = code * 16 + '0123456789abcdef'.indexOf(ch.toLowerCase());
 
-             } else {
 
-                 return '';
 
-             }
 
-         }
 
-         return String.fromCharCode(code);
 
-     }
 
-     function scanString() {
 
-         var str = '', quote, ch, code, unescaped, restore; //TODO review removal octal = false
 
-         quote = source.charAt(index);
 
-         ++index;
 
-         while (index < length) {
 
-             ch = advance();
 
-             if (ch === quote) {
 
-                 quote = '';
 
-                 break;
 
-             } else if (ch === '\\') {
 
-                 ch = advance();
 
-                 if (!esutils.code.isLineTerminator(ch.charCodeAt(0))) {
 
-                     switch (ch) {
 
-                     case 'n':
 
-                         str += '\n';
 
-                         break;
 
-                     case 'r':
 
-                         str += '\r';
 
-                         break;
 
-                     case 't':
 
-                         str += '\t';
 
-                         break;
 
-                     case 'u':
 
-                     case 'x':
 
-                         restore = index;
 
-                         unescaped = scanHexEscape(ch);
 
-                         if (unescaped) {
 
-                             str += unescaped;
 
-                         } else {
 
-                             index = restore;
 
-                             str += ch;
 
-                         }
 
-                         break;
 
-                     case 'b':
 
-                         str += '\b';
 
-                         break;
 
-                     case 'f':
 
-                         str += '\f';
 
-                         break;
 
-                     case 'v':
 
-                         str += '\v';
 
-                         break;
 
-                     default:
 
-                         if (esutils.code.isOctalDigit(ch.charCodeAt(0))) {
 
-                             code = '01234567'.indexOf(ch);
 
-                             // \0 is not octal escape sequence
 
-                             // Deprecating unused code. TODO review removal
 
-                             //if (code !== 0) {
 
-                             //    octal = true;
 
-                             //}
 
-                             if (index < length && esutils.code.isOctalDigit(source.charCodeAt(index))) {
 
-                                 //TODO Review Removal octal = true;
 
-                                 code = code * 8 + '01234567'.indexOf(advance());
 
-                                 // 3 digits are only allowed when string starts
 
-                                 // with 0, 1, 2, 3
 
-                                 if ('0123'.indexOf(ch) >= 0 &&
 
-                                         index < length &&
 
-                                         esutils.code.isOctalDigit(source.charCodeAt(index))) {
 
-                                     code = code * 8 + '01234567'.indexOf(advance());
 
-                                 }
 
-                             }
 
-                             str += String.fromCharCode(code);
 
-                         } else {
 
-                             str += ch;
 
-                         }
 
-                         break;
 
-                     }
 
-                 } else {
 
-                     if (ch ===  '\r' && source.charCodeAt(index) === 0x0A  /* '\n' */) {
 
-                         ++index;
 
-                     }
 
-                 }
 
-             } else if (esutils.code.isLineTerminator(ch.charCodeAt(0))) {
 
-                 break;
 
-             } else {
 
-                 str += ch;
 
-             }
 
-         }
 
-         if (quote !== '') {
 
-             utility.throwError('unexpected quote');
 
-         }
 
-         value = str;
 
-         return Token.STRING;
 
-     }
 
-     function scanNumber() {
 
-         var number, ch;
 
-         number = '';
 
-         ch = source.charCodeAt(index);
 
-         if (ch !== 0x2E  /* '.' */) {
 
-             number = advance();
 
-             ch = source.charCodeAt(index);
 
-             if (number === '0') {
 
-                 if (ch === 0x78  /* 'x' */ || ch === 0x58  /* 'X' */) {
 
-                     number += advance();
 
-                     while (index < length) {
 
-                         ch = source.charCodeAt(index);
 
-                         if (!esutils.code.isHexDigit(ch)) {
 
-                             break;
 
-                         }
 
-                         number += advance();
 
-                     }
 
-                     if (number.length <= 2) {
 
-                         // only 0x
 
-                         utility.throwError('unexpected token');
 
-                     }
 
-                     if (index < length) {
 
-                         ch = source.charCodeAt(index);
 
-                         if (esutils.code.isIdentifierStartES5(ch)) {
 
-                             utility.throwError('unexpected token');
 
-                         }
 
-                     }
 
-                     value = parseInt(number, 16);
 
-                     return Token.NUMBER;
 
-                 }
 
-                 if (esutils.code.isOctalDigit(ch)) {
 
-                     number += advance();
 
-                     while (index < length) {
 
-                         ch = source.charCodeAt(index);
 
-                         if (!esutils.code.isOctalDigit(ch)) {
 
-                             break;
 
-                         }
 
-                         number += advance();
 
-                     }
 
-                     if (index < length) {
 
-                         ch = source.charCodeAt(index);
 
-                         if (esutils.code.isIdentifierStartES5(ch) || esutils.code.isDecimalDigit(ch)) {
 
-                             utility.throwError('unexpected token');
 
-                         }
 
-                     }
 
-                     value = parseInt(number, 8);
 
-                     return Token.NUMBER;
 
-                 }
 
-                 if (esutils.code.isDecimalDigit(ch)) {
 
-                     utility.throwError('unexpected token');
 
-                 }
 
-             }
 
-             while (index < length) {
 
-                 ch = source.charCodeAt(index);
 
-                 if (!esutils.code.isDecimalDigit(ch)) {
 
-                     break;
 
-                 }
 
-                 number += advance();
 
-             }
 
-         }
 
-         if (ch === 0x2E  /* '.' */) {
 
-             number += advance();
 
-             while (index < length) {
 
-                 ch = source.charCodeAt(index);
 
-                 if (!esutils.code.isDecimalDigit(ch)) {
 
-                     break;
 
-                 }
 
-                 number += advance();
 
-             }
 
-         }
 
-         if (ch === 0x65  /* 'e' */ || ch === 0x45  /* 'E' */) {
 
-             number += advance();
 
-             ch = source.charCodeAt(index);
 
-             if (ch === 0x2B  /* '+' */ || ch === 0x2D  /* '-' */) {
 
-                 number += advance();
 
-             }
 
-             ch = source.charCodeAt(index);
 
-             if (esutils.code.isDecimalDigit(ch)) {
 
-                 number += advance();
 
-                 while (index < length) {
 
-                     ch = source.charCodeAt(index);
 
-                     if (!esutils.code.isDecimalDigit(ch)) {
 
-                         break;
 
-                     }
 
-                     number += advance();
 
-                 }
 
-             } else {
 
-                 utility.throwError('unexpected token');
 
-             }
 
-         }
 
-         if (index < length) {
 
-             ch = source.charCodeAt(index);
 
-             if (esutils.code.isIdentifierStartES5(ch)) {
 
-                 utility.throwError('unexpected token');
 
-             }
 
-         }
 
-         value = parseFloat(number);
 
-         return Token.NUMBER;
 
-     }
 
-     function scanTypeName() {
 
-         var ch, ch2;
 
-         value = advance();
 
-         while (index < length && isTypeName(source.charCodeAt(index))) {
 
-             ch = source.charCodeAt(index);
 
-             if (ch === 0x2E  /* '.' */) {
 
-                 if ((index + 1) >= length) {
 
-                     return Token.ILLEGAL;
 
-                 }
 
-                 ch2 = source.charCodeAt(index + 1);
 
-                 if (ch2 === 0x3C  /* '<' */) {
 
-                     break;
 
-                 }
 
-             }
 
-             value += advance();
 
-         }
 
-         return Token.NAME;
 
-     }
 
-     function next() {
 
-         var ch;
 
-         previous = index;
 
-         while (index < length && esutils.code.isWhiteSpace(source.charCodeAt(index))) {
 
-             advance();
 
-         }
 
-         if (index >= length) {
 
-             token = Token.EOF;
 
-             return token;
 
-         }
 
-         ch = source.charCodeAt(index);
 
-         switch (ch) {
 
-         case 0x27:  /* ''' */
 
-         case 0x22:  /* '"' */
 
-             token = scanString();
 
-             return token;
 
-         case 0x3A:  /* ':' */
 
-             advance();
 
-             token = Token.COLON;
 
-             return token;
 
-         case 0x2C:  /* ',' */
 
-             advance();
 
-             token = Token.COMMA;
 
-             return token;
 
-         case 0x28:  /* '(' */
 
-             advance();
 
-             token = Token.LPAREN;
 
-             return token;
 
-         case 0x29:  /* ')' */
 
-             advance();
 
-             token = Token.RPAREN;
 
-             return token;
 
-         case 0x5B:  /* '[' */
 
-             advance();
 
-             token = Token.LBRACK;
 
-             return token;
 
-         case 0x5D:  /* ']' */
 
-             advance();
 
-             token = Token.RBRACK;
 
-             return token;
 
-         case 0x7B:  /* '{' */
 
-             advance();
 
-             token = Token.LBRACE;
 
-             return token;
 
-         case 0x7D:  /* '}' */
 
-             advance();
 
-             token = Token.RBRACE;
 
-             return token;
 
-         case 0x2E:  /* '.' */
 
-             if (index + 1 < length) {
 
-                 ch = source.charCodeAt(index + 1);
 
-                 if (ch === 0x3C  /* '<' */) {
 
-                     advance();  // '.'
 
-                     advance();  // '<'
 
-                     token = Token.DOT_LT;
 
-                     return token;
 
-                 }
 
-                 if (ch === 0x2E  /* '.' */ && index + 2 < length && source.charCodeAt(index + 2) === 0x2E  /* '.' */) {
 
-                     advance();  // '.'
 
-                     advance();  // '.'
 
-                     advance();  // '.'
 
-                     token = Token.REST;
 
-                     return token;
 
-                 }
 
-                 if (esutils.code.isDecimalDigit(ch)) {
 
-                     token = scanNumber();
 
-                     return token;
 
-                 }
 
-             }
 
-             token = Token.ILLEGAL;
 
-             return token;
 
-         case 0x3C:  /* '<' */
 
-             advance();
 
-             token = Token.LT;
 
-             return token;
 
-         case 0x3E:  /* '>' */
 
-             advance();
 
-             token = Token.GT;
 
-             return token;
 
-         case 0x2A:  /* '*' */
 
-             advance();
 
-             token = Token.STAR;
 
-             return token;
 
-         case 0x7C:  /* '|' */
 
-             advance();
 
-             token = Token.PIPE;
 
-             return token;
 
-         case 0x3F:  /* '?' */
 
-             advance();
 
-             token = Token.QUESTION;
 
-             return token;
 
-         case 0x21:  /* '!' */
 
-             advance();
 
-             token = Token.BANG;
 
-             return token;
 
-         case 0x3D:  /* '=' */
 
-             advance();
 
-             token = Token.EQUAL;
 
-             return token;
 
-         case 0x2D: /* '-' */
 
-             token = scanNumber();
 
-             return token;
 
-         default:
 
-             if (esutils.code.isDecimalDigit(ch)) {
 
-                 token = scanNumber();
 
-                 return token;
 
-             }
 
-             // type string permits following case,
 
-             //
 
-             // namespace.module.MyClass
 
-             //
 
-             // this reduced 1 token TK_NAME
 
-             utility.assert(isTypeName(ch));
 
-             token = scanTypeName();
 
-             return token;
 
-         }
 
-     }
 
-     function consume(target, text) {
 
-         utility.assert(token === target, text || 'consumed token not matched');
 
-         next();
 
-     }
 
-     function expect(target, message) {
 
-         if (token !== target) {
 
-             utility.throwError(message || 'unexpected token');
 
-         }
 
-         next();
 
-     }
 
-     // UnionType := '(' TypeUnionList ')'
 
-     //
 
-     // TypeUnionList :=
 
-     //     <<empty>>
 
-     //   | NonemptyTypeUnionList
 
-     //
 
-     // NonemptyTypeUnionList :=
 
-     //     TypeExpression
 
-     //   | TypeExpression '|' NonemptyTypeUnionList
 
-     function parseUnionType() {
 
-         var elements, startIndex = index - 1;
 
-         consume(Token.LPAREN, 'UnionType should start with (');
 
-         elements = [];
 
-         if (token !== Token.RPAREN) {
 
-             while (true) {
 
-                 elements.push(parseTypeExpression());
 
-                 if (token === Token.RPAREN) {
 
-                     break;
 
-                 }
 
-                 expect(Token.PIPE);
 
-             }
 
-         }
 
-         consume(Token.RPAREN, 'UnionType should end with )');
 
-         return maybeAddRange({
 
-             type: Syntax.UnionType,
 
-             elements: elements
 
-         }, [startIndex, previous]);
 
-     }
 
-     // ArrayType := '[' ElementTypeList ']'
 
-     //
 
-     // ElementTypeList :=
 
-     //     <<empty>>
 
-     //  | TypeExpression
 
-     //  | '...' TypeExpression
 
-     //  | TypeExpression ',' ElementTypeList
 
-     function parseArrayType() {
 
-         var elements, startIndex = index - 1, restStartIndex;
 
-         consume(Token.LBRACK, 'ArrayType should start with [');
 
-         elements = [];
 
-         while (token !== Token.RBRACK) {
 
-             if (token === Token.REST) {
 
-                 restStartIndex = index - 3;
 
-                 consume(Token.REST);
 
-                 elements.push(maybeAddRange({
 
-                     type: Syntax.RestType,
 
-                     expression: parseTypeExpression()
 
-                 }, [restStartIndex, previous]));
 
-                 break;
 
-             } else {
 
-                 elements.push(parseTypeExpression());
 
-             }
 
-             if (token !== Token.RBRACK) {
 
-                 expect(Token.COMMA);
 
-             }
 
-         }
 
-         expect(Token.RBRACK);
 
-         return maybeAddRange({
 
-             type: Syntax.ArrayType,
 
-             elements: elements
 
-         }, [startIndex, previous]);
 
-     }
 
-     function parseFieldName() {
 
-         var v = value;
 
-         if (token === Token.NAME || token === Token.STRING) {
 
-             next();
 
-             return v;
 
-         }
 
-         if (token === Token.NUMBER) {
 
-             consume(Token.NUMBER);
 
-             return String(v);
 
-         }
 
-         utility.throwError('unexpected token');
 
-     }
 
-     // FieldType :=
 
-     //     FieldName
 
-     //   | FieldName ':' TypeExpression
 
-     //
 
-     // FieldName :=
 
-     //     NameExpression
 
-     //   | StringLiteral
 
-     //   | NumberLiteral
 
-     //   | ReservedIdentifier
 
-     function parseFieldType() {
 
-         var key, rangeStart = previous;
 
-         key = parseFieldName();
 
-         if (token === Token.COLON) {
 
-             consume(Token.COLON);
 
-             return maybeAddRange({
 
-                 type: Syntax.FieldType,
 
-                 key: key,
 
-                 value: parseTypeExpression()
 
-             }, [rangeStart, previous]);
 
-         }
 
-         return maybeAddRange({
 
-             type: Syntax.FieldType,
 
-             key: key,
 
-             value: null
 
-         }, [rangeStart, previous]);
 
-     }
 
-     // RecordType := '{' FieldTypeList '}'
 
-     //
 
-     // FieldTypeList :=
 
-     //     <<empty>>
 
-     //   | FieldType
 
-     //   | FieldType ',' FieldTypeList
 
-     function parseRecordType() {
 
-         var fields, rangeStart = index - 1, rangeEnd;
 
-         consume(Token.LBRACE, 'RecordType should start with {');
 
-         fields = [];
 
-         if (token === Token.COMMA) {
 
-             consume(Token.COMMA);
 
-         } else {
 
-             while (token !== Token.RBRACE) {
 
-                 fields.push(parseFieldType());
 
-                 if (token !== Token.RBRACE) {
 
-                     expect(Token.COMMA);
 
-                 }
 
-             }
 
-         }
 
-         rangeEnd = index;
 
-         expect(Token.RBRACE);
 
-         return maybeAddRange({
 
-             type: Syntax.RecordType,
 
-             fields: fields
 
-         }, [rangeStart, rangeEnd]);
 
-     }
 
-     // NameExpression :=
 
-     //    Identifier
 
-     //  | TagIdentifier ':' Identifier
 
-     //
 
-     // Tag identifier is one of "module", "external" or "event"
 
-     // Identifier is the same as Token.NAME, including any dots, something like
 
-     // namespace.module.MyClass
 
-     function parseNameExpression() {
 
-         var name = value, rangeStart = index - name.length;
 
-         expect(Token.NAME);
 
-         if (token === Token.COLON && (
 
-                 name === 'module' ||
 
-                 name === 'external' ||
 
-                 name === 'event')) {
 
-             consume(Token.COLON);
 
-             name += ':' + value;
 
-             expect(Token.NAME);
 
-         }
 
-         return maybeAddRange({
 
-             type: Syntax.NameExpression,
 
-             name: name
 
-         }, [rangeStart, previous]);
 
-     }
 
-     // TypeExpressionList :=
 
-     //     TopLevelTypeExpression
 
-     //   | TopLevelTypeExpression ',' TypeExpressionList
 
-     function parseTypeExpressionList() {
 
-         var elements = [];
 
-         elements.push(parseTop());
 
-         while (token === Token.COMMA) {
 
-             consume(Token.COMMA);
 
-             elements.push(parseTop());
 
-         }
 
-         return elements;
 
-     }
 
-     // TypeName :=
 
-     //     NameExpression
 
-     //   | NameExpression TypeApplication
 
-     //
 
-     // TypeApplication :=
 
-     //     '.<' TypeExpressionList '>'
 
-     //   | '<' TypeExpressionList '>'   // this is extension of doctrine
 
-     function parseTypeName() {
 
-         var expr, applications, startIndex = index - value.length;
 
-         expr = parseNameExpression();
 
-         if (token === Token.DOT_LT || token === Token.LT) {
 
-             next();
 
-             applications = parseTypeExpressionList();
 
-             expect(Token.GT);
 
-             return maybeAddRange({
 
-                 type: Syntax.TypeApplication,
 
-                 expression: expr,
 
-                 applications: applications
 
-             }, [startIndex, previous]);
 
-         }
 
-         return expr;
 
-     }
 
-     // ResultType :=
 
-     //     <<empty>>
 
-     //   | ':' void
 
-     //   | ':' TypeExpression
 
-     //
 
-     // BNF is above
 
-     // but, we remove <<empty>> pattern, so token is always TypeToken::COLON
 
-     function parseResultType() {
 
-         consume(Token.COLON, 'ResultType should start with :');
 
-         if (token === Token.NAME && value === 'void') {
 
-             consume(Token.NAME);
 
-             return {
 
-                 type: Syntax.VoidLiteral
 
-             };
 
-         }
 
-         return parseTypeExpression();
 
-     }
 
-     // ParametersType :=
 
-     //     RestParameterType
 
-     //   | NonRestParametersType
 
-     //   | NonRestParametersType ',' RestParameterType
 
-     //
 
-     // RestParameterType :=
 
-     //     '...'
 
-     //     '...' Identifier
 
-     //
 
-     // NonRestParametersType :=
 
-     //     ParameterType ',' NonRestParametersType
 
-     //   | ParameterType
 
-     //   | OptionalParametersType
 
-     //
 
-     // OptionalParametersType :=
 
-     //     OptionalParameterType
 
-     //   | OptionalParameterType, OptionalParametersType
 
-     //
 
-     // OptionalParameterType := ParameterType=
 
-     //
 
-     // ParameterType := TypeExpression | Identifier ':' TypeExpression
 
-     //
 
-     // Identifier is "new" or "this"
 
-     function parseParametersType() {
 
-         var params = [], optionalSequence = false, expr, rest = false, startIndex, restStartIndex = index - 3, nameStartIndex;
 
-         while (token !== Token.RPAREN) {
 
-             if (token === Token.REST) {
 
-                 // RestParameterType
 
-                 consume(Token.REST);
 
-                 rest = true;
 
-             }
 
-             startIndex = previous;
 
-             expr = parseTypeExpression();
 
-             if (expr.type === Syntax.NameExpression && token === Token.COLON) {
 
-                 nameStartIndex = previous - expr.name.length;
 
-                 // Identifier ':' TypeExpression
 
-                 consume(Token.COLON);
 
-                 expr = maybeAddRange({
 
-                     type: Syntax.ParameterType,
 
-                     name: expr.name,
 
-                     expression: parseTypeExpression()
 
-                 }, [nameStartIndex, previous]);
 
-             }
 
-             if (token === Token.EQUAL) {
 
-                 consume(Token.EQUAL);
 
-                 expr = maybeAddRange({
 
-                     type: Syntax.OptionalType,
 
-                     expression: expr
 
-                 }, [startIndex, previous]);
 
-                 optionalSequence = true;
 
-             } else {
 
-                 if (optionalSequence) {
 
-                     utility.throwError('unexpected token');
 
-                 }
 
-             }
 
-             if (rest) {
 
-                 expr = maybeAddRange({
 
-                     type: Syntax.RestType,
 
-                     expression: expr
 
-                 }, [restStartIndex, previous]);
 
-             }
 
-             params.push(expr);
 
-             if (token !== Token.RPAREN) {
 
-                 expect(Token.COMMA);
 
-             }
 
-         }
 
-         return params;
 
-     }
 
-     // FunctionType := 'function' FunctionSignatureType
 
-     //
 
-     // FunctionSignatureType :=
 
-     //   | TypeParameters '(' ')' ResultType
 
-     //   | TypeParameters '(' ParametersType ')' ResultType
 
-     //   | TypeParameters '(' 'this' ':' TypeName ')' ResultType
 
-     //   | TypeParameters '(' 'this' ':' TypeName ',' ParametersType ')' ResultType
 
-     function parseFunctionType() {
 
-         var isNew, thisBinding, params, result, fnType, startIndex = index - value.length;
 
-         utility.assert(token === Token.NAME && value === 'function', 'FunctionType should start with \'function\'');
 
-         consume(Token.NAME);
 
-         // Google Closure Compiler is not implementing TypeParameters.
 
-         // So we do not. if we don't get '(', we see it as error.
 
-         expect(Token.LPAREN);
 
-         isNew = false;
 
-         params = [];
 
-         thisBinding = null;
 
-         if (token !== Token.RPAREN) {
 
-             // ParametersType or 'this'
 
-             if (token === Token.NAME &&
 
-                     (value === 'this' || value === 'new')) {
 
-                 // 'this' or 'new'
 
-                 // 'new' is Closure Compiler extension
 
-                 isNew = value === 'new';
 
-                 consume(Token.NAME);
 
-                 expect(Token.COLON);
 
-                 thisBinding = parseTypeName();
 
-                 if (token === Token.COMMA) {
 
-                     consume(Token.COMMA);
 
-                     params = parseParametersType();
 
-                 }
 
-             } else {
 
-                 params = parseParametersType();
 
-             }
 
-         }
 
-         expect(Token.RPAREN);
 
-         result = null;
 
-         if (token === Token.COLON) {
 
-             result = parseResultType();
 
-         }
 
-         fnType = maybeAddRange({
 
-             type: Syntax.FunctionType,
 
-             params: params,
 
-             result: result
 
-         }, [startIndex, previous]);
 
-         if (thisBinding) {
 
-             // avoid adding null 'new' and 'this' properties
 
-             fnType['this'] = thisBinding;
 
-             if (isNew) {
 
-                 fnType['new'] = true;
 
-             }
 
-         }
 
-         return fnType;
 
-     }
 
-     // BasicTypeExpression :=
 
-     //     '*'
 
-     //   | 'null'
 
-     //   | 'undefined'
 
-     //   | TypeName
 
-     //   | FunctionType
 
-     //   | UnionType
 
-     //   | RecordType
 
-     //   | ArrayType
 
-     function parseBasicTypeExpression() {
 
-         var context, startIndex;
 
-         switch (token) {
 
-         case Token.STAR:
 
-             consume(Token.STAR);
 
-             return maybeAddRange({
 
-                 type: Syntax.AllLiteral
 
-             }, [previous - 1, previous]);
 
-         case Token.LPAREN:
 
-             return parseUnionType();
 
-         case Token.LBRACK:
 
-             return parseArrayType();
 
-         case Token.LBRACE:
 
-             return parseRecordType();
 
-         case Token.NAME:
 
-             startIndex = index - value.length;
 
-             if (value === 'null') {
 
-                 consume(Token.NAME);
 
-                 return maybeAddRange({
 
-                     type: Syntax.NullLiteral
 
-                 }, [startIndex, previous]);
 
-             }
 
-             if (value === 'undefined') {
 
-                 consume(Token.NAME);
 
-                 return maybeAddRange({
 
-                     type: Syntax.UndefinedLiteral
 
-                 }, [startIndex, previous]);
 
-             }
 
-             if (value === 'true' || value === 'false') {
 
-                 consume(Token.NAME);
 
-                 return maybeAddRange({
 
-                     type: Syntax.BooleanLiteralType,
 
-                     value: value === 'true'
 
-                 }, [startIndex, previous]);
 
-             }
 
-             context = Context.save();
 
-             if (value === 'function') {
 
-                 try {
 
-                     return parseFunctionType();
 
-                 } catch (e) {
 
-                     context.restore();
 
-                 }
 
-             }
 
-             return parseTypeName();
 
-         case Token.STRING:
 
-             next();
 
-             return maybeAddRange({
 
-                 type: Syntax.StringLiteralType,
 
-                 value: value
 
-             }, [previous - value.length - 2, previous]);
 
-         case Token.NUMBER:
 
-             next();
 
-             return maybeAddRange({
 
-                 type: Syntax.NumericLiteralType,
 
-                 value: value
 
-             }, [previous - String(value).length, previous]);
 
-         default:
 
-             utility.throwError('unexpected token');
 
-         }
 
-     }
 
-     // TypeExpression :=
 
-     //     BasicTypeExpression
 
-     //   | '?' BasicTypeExpression
 
-     //   | '!' BasicTypeExpression
 
-     //   | BasicTypeExpression '?'
 
-     //   | BasicTypeExpression '!'
 
-     //   | '?'
 
-     //   | BasicTypeExpression '[]'
 
-     function parseTypeExpression() {
 
-         var expr, rangeStart;
 
-         if (token === Token.QUESTION) {
 
-             rangeStart = index - 1;
 
-             consume(Token.QUESTION);
 
-             if (token === Token.COMMA || token === Token.EQUAL || token === Token.RBRACE ||
 
-                     token === Token.RPAREN || token === Token.PIPE || token === Token.EOF ||
 
-                     token === Token.RBRACK || token === Token.GT) {
 
-                 return maybeAddRange({
 
-                     type: Syntax.NullableLiteral
 
-                 }, [rangeStart, previous]);
 
-             }
 
-             return maybeAddRange({
 
-                 type: Syntax.NullableType,
 
-                 expression: parseBasicTypeExpression(),
 
-                 prefix: true
 
-             }, [rangeStart, previous]);
 
-         } else if (token === Token.BANG) {
 
-             rangeStart = index - 1;
 
-             consume(Token.BANG);
 
-             return maybeAddRange({
 
-                 type: Syntax.NonNullableType,
 
-                 expression: parseBasicTypeExpression(),
 
-                 prefix: true
 
-             }, [rangeStart, previous]);
 
-         } else {
 
-             rangeStart = previous;
 
-         }
 
-         expr = parseBasicTypeExpression();
 
-         if (token === Token.BANG) {
 
-             consume(Token.BANG);
 
-             return maybeAddRange({
 
-                 type: Syntax.NonNullableType,
 
-                 expression: expr,
 
-                 prefix: false
 
-             }, [rangeStart, previous]);
 
-         }
 
-         if (token === Token.QUESTION) {
 
-             consume(Token.QUESTION);
 
-             return maybeAddRange({
 
-                 type: Syntax.NullableType,
 
-                 expression: expr,
 
-                 prefix: false
 
-             }, [rangeStart, previous]);
 
-         }
 
-         if (token === Token.LBRACK) {
 
-             consume(Token.LBRACK);
 
-             expect(Token.RBRACK, 'expected an array-style type declaration (' + value + '[])');
 
-             return maybeAddRange({
 
-                 type: Syntax.TypeApplication,
 
-                 expression: maybeAddRange({
 
-                     type: Syntax.NameExpression,
 
-                     name: 'Array'
 
-                 }, [rangeStart, previous]),
 
-                 applications: [expr]
 
-             }, [rangeStart, previous]);
 
-         }
 
-         return expr;
 
-     }
 
-     // TopLevelTypeExpression :=
 
-     //      TypeExpression
 
-     //    | TypeUnionList
 
-     //
 
-     // This rule is Google Closure Compiler extension, not ES4
 
-     // like,
 
-     //   { number | string }
 
-     // If strict to ES4, we should write it as
 
-     //   { (number|string) }
 
-     function parseTop() {
 
-         var expr, elements;
 
-         expr = parseTypeExpression();
 
-         if (token !== Token.PIPE) {
 
-             return expr;
 
-         }
 
-         elements = [expr];
 
-         consume(Token.PIPE);
 
-         while (true) {
 
-             elements.push(parseTypeExpression());
 
-             if (token !== Token.PIPE) {
 
-                 break;
 
-             }
 
-             consume(Token.PIPE);
 
-         }
 
-         return maybeAddRange({
 
-             type: Syntax.UnionType,
 
-             elements: elements
 
-         }, [0, index]);
 
-     }
 
-     function parseTopParamType() {
 
-         var expr;
 
-         if (token === Token.REST) {
 
-             consume(Token.REST);
 
-             return maybeAddRange({
 
-                 type: Syntax.RestType,
 
-                 expression: parseTop()
 
-             }, [0, index]);
 
-         }
 
-         expr = parseTop();
 
-         if (token === Token.EQUAL) {
 
-             consume(Token.EQUAL);
 
-             return maybeAddRange({
 
-                 type: Syntax.OptionalType,
 
-                 expression: expr
 
-             }, [0, index]);
 
-         }
 
-         return expr;
 
-     }
 
-     function parseType(src, opt) {
 
-         var expr;
 
-         source = src;
 
-         length = source.length;
 
-         index = 0;
 
-         previous = 0;
 
-         addRange = opt && opt.range;
 
-         rangeOffset = opt && opt.startIndex || 0;
 
-         next();
 
-         expr = parseTop();
 
-         if (opt && opt.midstream) {
 
-             return {
 
-                 expression: expr,
 
-                 index: previous
 
-             };
 
-         }
 
-         if (token !== Token.EOF) {
 
-             utility.throwError('not reach to EOF');
 
-         }
 
-         return expr;
 
-     }
 
-     function parseParamType(src, opt) {
 
-         var expr;
 
-         source = src;
 
-         length = source.length;
 
-         index = 0;
 
-         previous = 0;
 
-         addRange = opt && opt.range;
 
-         rangeOffset = opt && opt.startIndex || 0;
 
-         next();
 
-         expr = parseTopParamType();
 
-         if (opt && opt.midstream) {
 
-             return {
 
-                 expression: expr,
 
-                 index: previous
 
-             };
 
-         }
 
-         if (token !== Token.EOF) {
 
-             utility.throwError('not reach to EOF');
 
-         }
 
-         return expr;
 
-     }
 
-     function stringifyImpl(node, compact, topLevel) {
 
-         var result, i, iz;
 
-         switch (node.type) {
 
-         case Syntax.NullableLiteral:
 
-             result = '?';
 
-             break;
 
-         case Syntax.AllLiteral:
 
-             result = '*';
 
-             break;
 
-         case Syntax.NullLiteral:
 
-             result = 'null';
 
-             break;
 
-         case Syntax.UndefinedLiteral:
 
-             result = 'undefined';
 
-             break;
 
-         case Syntax.VoidLiteral:
 
-             result = 'void';
 
-             break;
 
-         case Syntax.UnionType:
 
-             if (!topLevel) {
 
-                 result = '(';
 
-             } else {
 
-                 result = '';
 
-             }
 
-             for (i = 0, iz = node.elements.length; i < iz; ++i) {
 
-                 result += stringifyImpl(node.elements[i], compact);
 
-                 if ((i + 1) !== iz) {
 
-                     result += compact ? '|' : ' | ';
 
-                 }
 
-             }
 
-             if (!topLevel) {
 
-                 result += ')';
 
-             }
 
-             break;
 
-         case Syntax.ArrayType:
 
-             result = '[';
 
-             for (i = 0, iz = node.elements.length; i < iz; ++i) {
 
-                 result += stringifyImpl(node.elements[i], compact);
 
-                 if ((i + 1) !== iz) {
 
-                     result += compact ? ',' : ', ';
 
-                 }
 
-             }
 
-             result += ']';
 
-             break;
 
-         case Syntax.RecordType:
 
-             result = '{';
 
-             for (i = 0, iz = node.fields.length; i < iz; ++i) {
 
-                 result += stringifyImpl(node.fields[i], compact);
 
-                 if ((i + 1) !== iz) {
 
-                     result += compact ? ',' : ', ';
 
-                 }
 
-             }
 
-             result += '}';
 
-             break;
 
-         case Syntax.FieldType:
 
-             if (node.value) {
 
-                 result = node.key + (compact ? ':' : ': ') + stringifyImpl(node.value, compact);
 
-             } else {
 
-                 result = node.key;
 
-             }
 
-             break;
 
-         case Syntax.FunctionType:
 
-             result = compact ? 'function(' : 'function (';
 
-             if (node['this']) {
 
-                 if (node['new']) {
 
-                     result += (compact ? 'new:' : 'new: ');
 
-                 } else {
 
-                     result += (compact ? 'this:' : 'this: ');
 
-                 }
 
-                 result += stringifyImpl(node['this'], compact);
 
-                 if (node.params.length !== 0) {
 
-                     result += compact ? ',' : ', ';
 
-                 }
 
-             }
 
-             for (i = 0, iz = node.params.length; i < iz; ++i) {
 
-                 result += stringifyImpl(node.params[i], compact);
 
-                 if ((i + 1) !== iz) {
 
-                     result += compact ? ',' : ', ';
 
-                 }
 
-             }
 
-             result += ')';
 
-             if (node.result) {
 
-                 result += (compact ? ':' : ': ') + stringifyImpl(node.result, compact);
 
-             }
 
-             break;
 
-         case Syntax.ParameterType:
 
-             result = node.name + (compact ? ':' : ': ') + stringifyImpl(node.expression, compact);
 
-             break;
 
-         case Syntax.RestType:
 
-             result = '...';
 
-             if (node.expression) {
 
-                 result += stringifyImpl(node.expression, compact);
 
-             }
 
-             break;
 
-         case Syntax.NonNullableType:
 
-             if (node.prefix) {
 
-                 result = '!' + stringifyImpl(node.expression, compact);
 
-             } else {
 
-                 result = stringifyImpl(node.expression, compact) + '!';
 
-             }
 
-             break;
 
-         case Syntax.OptionalType:
 
-             result = stringifyImpl(node.expression, compact) + '=';
 
-             break;
 
-         case Syntax.NullableType:
 
-             if (node.prefix) {
 
-                 result = '?' + stringifyImpl(node.expression, compact);
 
-             } else {
 
-                 result = stringifyImpl(node.expression, compact) + '?';
 
-             }
 
-             break;
 
-         case Syntax.NameExpression:
 
-             result = node.name;
 
-             break;
 
-         case Syntax.TypeApplication:
 
-             result = stringifyImpl(node.expression, compact) + '.<';
 
-             for (i = 0, iz = node.applications.length; i < iz; ++i) {
 
-                 result += stringifyImpl(node.applications[i], compact);
 
-                 if ((i + 1) !== iz) {
 
-                     result += compact ? ',' : ', ';
 
-                 }
 
-             }
 
-             result += '>';
 
-             break;
 
-         case Syntax.StringLiteralType:
 
-             result = '"' + node.value + '"';
 
-             break;
 
-         case Syntax.NumericLiteralType:
 
-             result = String(node.value);
 
-             break;
 
-         case Syntax.BooleanLiteralType:
 
-             result = String(node.value);
 
-             break;
 
-         default:
 
-             utility.throwError('Unknown type ' + node.type);
 
-         }
 
-         return result;
 
-     }
 
-     function stringify(node, options) {
 
-         if (options == null) {
 
-             options = {};
 
-         }
 
-         return stringifyImpl(node, options.compact, options.topLevel);
 
-     }
 
-     exports.parseType = parseType;
 
-     exports.parseParamType = parseParamType;
 
-     exports.stringify = stringify;
 
-     exports.Syntax = Syntax;
 
- }());
 
- /* vim: set sw=4 ts=4 et tw=80 : */
 
 
  |