|
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 : */
|