UNPKG

typescript-closure-tools

Version:

Command-line tools to convert closure-style JSDoc annotations to typescript, and to convert typescript sources to closure externs files

1,326 lines (1,150 loc) 76.5 kB
/* Copyright (C) 2012-2014 Yusuke Suzuki <utatane.tea@gmail.com> Copyright (C) 2014 Dan Tao <daniel.tao@gmail.com> Copyright (C) 2013 Andrew Eisenberg <andrew@eisenberg.as> Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /*jslint bitwise:true plusplus:true eqeq:true nomen:true*/ /*global doctrine:true, exports:true, parseTypeExpression:true, parseTop:true*/ (function (exports) { 'use strict'; var VERSION, Regex, CanAccessStringByIndex, typed, jsdoc, isArray, hasOwnProperty; // Sync with package.json. VERSION = '0.5.2-dev'; // See also tools/generate-unicode-regex.py. Regex = { NonAsciiIdentifierStart: new RegExp('[\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]'), NonAsciiIdentifierPart: new RegExp('[\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0300-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u0483-\u0487\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u05d0-\u05ea\u05f0-\u05f2\u0610-\u061a\u0620-\u0669\u066e-\u06d3\u06d5-\u06dc\u06df-\u06e8\u06ea-\u06fc\u06ff\u0710-\u074a\u074d-\u07b1\u07c0-\u07f5\u07fa\u0800-\u082d\u0840-\u085b\u08a0\u08a2-\u08ac\u08e4-\u08fe\u0900-\u0963\u0966-\u096f\u0971-\u0977\u0979-\u097f\u0981-\u0983\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bc-\u09c4\u09c7\u09c8\u09cb-\u09ce\u09d7\u09dc\u09dd\u09df-\u09e3\u09e6-\u09f1\u0a01-\u0a03\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a59-\u0a5c\u0a5e\u0a66-\u0a75\u0a81-\u0a83\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abc-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ad0\u0ae0-\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3c-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b5c\u0b5d\u0b5f-\u0b63\u0b66-\u0b6f\u0b71\u0b82\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd0\u0bd7\u0be6-\u0bef\u0c01-\u0c03\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c58\u0c59\u0c60-\u0c63\u0c66-\u0c6f\u0c82\u0c83\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbc-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0cde\u0ce0-\u0ce3\u0ce6-\u0cef\u0cf1\u0cf2\u0d02\u0d03\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d-\u0d44\u0d46-\u0d48\u0d4a-\u0d4e\u0d57\u0d60-\u0d63\u0d66-\u0d6f\u0d7a-\u0d7f\u0d82\u0d83\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e01-\u0e3a\u0e40-\u0e4e\u0e50-\u0e59\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb9\u0ebb-\u0ebd\u0ec0-\u0ec4\u0ec6\u0ec8-\u0ecd\u0ed0-\u0ed9\u0edc-\u0edf\u0f00\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e-\u0f47\u0f49-\u0f6c\u0f71-\u0f84\u0f86-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1049\u1050-\u109d\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u135d-\u135f\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176c\u176e-\u1770\u1772\u1773\u1780-\u17d3\u17d7\u17dc\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1820-\u1877\u1880-\u18aa\u18b0-\u18f5\u1900-\u191c\u1920-\u192b\u1930-\u193b\u1946-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u19d0-\u19d9\u1a00-\u1a1b\u1a20-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1aa7\u1b00-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1bf3\u1c00-\u1c37\u1c40-\u1c49\u1c4d-\u1c7d\u1cd0-\u1cd2\u1cd4-\u1cf6\u1d00-\u1de6\u1dfc-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u200c\u200d\u203f\u2040\u2054\u2071\u207f\u2090-\u209c\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d7f-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2de0-\u2dff\u2e2f\u3005-\u3007\u3021-\u302f\u3031-\u3035\u3038-\u303c\u3041-\u3096\u3099\u309a\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua62b\ua640-\ua66f\ua674-\ua67d\ua67f-\ua697\ua69f-\ua6f1\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua827\ua840-\ua873\ua880-\ua8c4\ua8d0-\ua8d9\ua8e0-\ua8f7\ua8fb\ua900-\ua92d\ua930-\ua953\ua960-\ua97c\ua980-\ua9c0\ua9cf-\ua9d9\uaa00-\uaa36\uaa40-\uaa4d\uaa50-\uaa59\uaa60-\uaa76\uaa7a\uaa7b\uaa80-\uaac2\uaadb-\uaadd\uaae0-\uaaef\uaaf2-\uaaf6\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabea\uabec\uabed\uabf0-\uabf9\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe00-\ufe0f\ufe20-\ufe26\ufe33\ufe34\ufe4d-\ufe4f\ufe70-\ufe74\ufe76-\ufefc\uff10-\uff19\uff21-\uff3a\uff3f\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]') }; CanAccessStringByIndex = typeof 'doctrine'[0] !== undefined; function sliceSource(source, index, last) { return source.slice(index, last); } isArray = Array.isArray; if (!isArray) { isArray = function isArray(ary) { return Object.prototype.toString.call(ary) === '[object Array]'; }; } hasOwnProperty = (function () { var func = Object.prototype.hasOwnProperty; return function hasOwnProperty(obj, name) { return func.call(obj, name); }; }()); if (!CanAccessStringByIndex) { sliceSource = function sliceSource(source, index, last) { return source.slice(index, last).join(''); }; } function shallowCopy(obj) { var ret = {}, key; for (key in obj) { if (obj.hasOwnProperty(key)) { ret[key] = obj[key]; } } return ret; } function isLineTerminator(ch) { return ch === '\n' || ch === '\r' || ch === '\u2028' || ch === '\u2029'; } function isWhiteSpace(ch) { return (ch === ' ') || (ch === '\u0009') || (ch === '\u000B') || (ch === '\u000C') || (ch === '\u00A0') || (ch.charCodeAt(0) >= 0x1680 && '\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\uFEFF'.indexOf(ch) >= 0); } function isDecimalDigit(ch) { return '0123456789'.indexOf(ch) >= 0; } function isHexDigit(ch) { return '0123456789abcdefABCDEF'.indexOf(ch) >= 0; } function isOctalDigit(ch) { return '01234567'.indexOf(ch) >= 0; } function isASCIIAlphanumeric(ch) { return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9'); } function isIdentifierStart(ch) { return (ch === '$') || (ch === '_') || (ch === '\\') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ((ch.charCodeAt(0) >= 0x80) && Regex.NonAsciiIdentifierStart.test(ch)); } function isIdentifierPart(ch) { return (ch === '$') || (ch === '_') || (ch === '\\') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ((ch >= '0') && (ch <= '9')) || ((ch.charCodeAt(0) >= 0x80) && Regex.NonAsciiIdentifierPart.test(ch)); } function isTypeName(ch) { return '><(){}[],:*|?!='.indexOf(ch) === -1 && !isWhiteSpace(ch) && !isLineTerminator(ch); } function isParamTitle(title) { return title === 'param' || title === 'argument' || title === 'arg'; } function isProperty(title) { return title === 'property' || title === 'prop'; } function isNameParameterRequired(title) { return isParamTitle(title) || isProperty(title) || title === 'alias' || title === 'this' || title === 'mixes' || title === 'requires'; } function isAllowedName(title) { return isNameParameterRequired(title) || title === 'const' || title === 'constant'; } function isAllowedNested(title) { return isProperty(title) || isParamTitle(title); } function isTypeParameterRequired(title) { return isParamTitle(title) || title === 'define' || title === 'enum' || title === 'implements' || title === 'return' || title === 'this' || title === 'type' || title === 'typedef' || title === 'returns' || isProperty(title); } // Consider deprecation instead using 'isTypeParameterRequired' and 'Rules' declaration to pick when a type is optional/required // This would require changes to 'parseType' function isAllowedType(title) { return isTypeParameterRequired(title) || title === 'throws' || title === 'const' || title === 'constant' || title === 'namespace' || title === 'member' || title === 'var' || title === 'module' || title === 'constructor' || title === 'class' || title === 'extends' || title === 'augments'; } function stringToArray(str) { return str.split(''); } function DoctrineError(message) { this.name = 'DoctrineError'; this.message = message; } DoctrineError.prototype = new Error(); DoctrineError.prototype.constructor = DoctrineError; function throwError(message) { throw new DoctrineError(message); } function assert(cond, text) { } if (VERSION.slice(-3) === 'dev') { assert = function assert(cond, text) { if (!cond) { throwError(text); } }; } function trim(str) { return str.replace(/^\s+/, '').replace(/\s+$/, ''); } function unwrapComment(doc) { // JSDoc comment is following form // /** // * ....... // */ // remove /**, */ and * var BEFORE_STAR = 0, STAR = 1, AFTER_STAR = 2, index, len, mode, result, ch; doc = doc.replace(/^\/\*\*?/, '').replace(/\*\/$/, ''); index = 0; len = doc.length; mode = BEFORE_STAR; result = ''; while (index < len) { ch = doc[index]; switch (mode) { case BEFORE_STAR: if (isLineTerminator(ch)) { result += ch; } else if (ch === '*') { mode = STAR; } else if (!isWhiteSpace(ch)) { result += ch; mode = AFTER_STAR; } break; case STAR: if (!isWhiteSpace(ch)) { result += ch; } mode = isLineTerminator(ch) ? BEFORE_STAR : AFTER_STAR; break; case AFTER_STAR: result += ch; if (isLineTerminator(ch)) { mode = BEFORE_STAR; } break; } index += 1; } return result; } // Type Expression Parser (function (exports) { var Syntax, Token, source, length, index, previous, token, value; 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' }; Token = { ILLEGAL: 0, // ILLEGAL DOT: 1, // . DOT_LT: 2, // .< REST: 3, // ... LT: 4, // < GT: 5, // > LPAREN: 6, // ( RPAREN: 7, // ) LBRACE: 8, // { RBRACE: 9, // } LBRACK: 10, // [ RBRACK: 11, // ] COMMA: 12, // , COLON: 13, // : STAR: 14, // * PIPE: 15, // | QUESTION: 16, // ? BANG: 17, // ! EQUAL: 18, // = NAME: 19, // name token STRING: 20, // string NUMBER: 21, // number EOF: 22 }; 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 advance() { var ch = source[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 && isHexDigit(source[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, octal = false; quote = source[index]; ++index; while (index < length) { ch = advance(); if (ch === quote) { quote = ''; break; } else if (ch === '\\') { ch = advance(); if (!isLineTerminator(ch)) { 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 (isOctalDigit(ch)) { code = '01234567'.indexOf(ch); // \0 is not octal escape sequence if (code !== 0) { octal = true; } if (index < length && isOctalDigit(source[index])) { 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 && isOctalDigit(source[index])) { code = code * 8 + '01234567'.indexOf(advance()); } } str += String.fromCharCode(code); } else { str += ch; } break; } } else { if (ch === '\r' && source[index] === '\n') { ++index; } } } else if (isLineTerminator(ch)) { break; } else { str += ch; } } if (quote !== '') { throwError('unexpected quote'); } value = str; return Token.STRING; } function scanNumber() { var number, ch; number = ''; if (ch !== '.') { number = advance(); ch = source[index]; if (number === '0') { if (ch === 'x' || ch === 'X') { number += advance(); while (index < length) { ch = source[index]; if (!isHexDigit(ch)) { break; } number += advance(); } if (number.length <= 2) { // only 0x throwError('unexpected token'); } if (index < length) { ch = source[index]; if (isIdentifierStart(ch)) { throwError('unexpected token'); } } value = parseInt(number, 16); return Token.NUMBER; } if (isOctalDigit(ch)) { number += advance(); while (index < length) { ch = source[index]; if (!isOctalDigit(ch)) { break; } number += advance(); } if (index < length) { ch = source[index]; if (isIdentifierStart(ch) || isDecimalDigit(ch)) { throwError('unexpected token'); } } value = parseInt(number, 8); return Token.NUMBER; } if (isDecimalDigit(ch)) { throwError('unexpected token'); } } while (index < length) { ch = source[index]; if (!isDecimalDigit(ch)) { break; } number += advance(); } } if (ch === '.') { number += advance(); while (index < length) { ch = source[index]; if (!isDecimalDigit(ch)) { break; } number += advance(); } } if (ch === 'e' || ch === 'E') { number += advance(); ch = source[index]; if (ch === '+' || ch === '-') { number += advance(); } ch = source[index]; if (isDecimalDigit(ch)) { number += advance(); while (index < length) { ch = source[index]; if (!isDecimalDigit(ch)) { break; } number += advance(); } } else { throwError('unexpected token'); } } if (index < length) { ch = source[index]; if (isIdentifierStart(ch)) { throwError('unexpected token'); } } value = parseFloat(number); return Token.NUMBER; } function scanTypeName() { var ch, ch2; value = advance(); while (index < length && isTypeName(source[index])) { ch = source[index]; if (ch === '.') { if ((index + 1) < length) { ch2 = source[index + 1]; if (ch2 === '<') { break; } } } value += advance(); } return Token.NAME; } function next() { var ch; previous = index; while (index < length && isWhiteSpace(source[index])) { advance(); } if (index >= length) { token = Token.EOF; return token; } ch = source[index]; switch (ch) { case '"': token = scanString(); return token; case ':': advance(); token = Token.COLON; return token; case ',': advance(); token = Token.COMMA; return token; case '(': advance(); token = Token.LPAREN; return token; case ')': advance(); token = Token.RPAREN; return token; case '[': advance(); token = Token.LBRACK; return token; case ']': advance(); token = Token.RBRACK; return token; case '{': advance(); token = Token.LBRACE; return token; case '}': advance(); token = Token.RBRACE; return token; case '.': advance(); if (index < length) { ch = source[index]; if (ch === '<') { advance(); token = Token.DOT_LT; return token; } if (ch === '.' && index + 1 < length && source[index + 1] === '.') { advance(); advance(); token = Token.REST; return token; } if (isDecimalDigit(ch)) { token = scanNumber(); return token; } } token = Token.DOT; return token; case '<': advance(); token = Token.LT; return token; case '>': advance(); token = Token.GT; return token; case '*': advance(); token = Token.STAR; return token; case '|': advance(); token = Token.PIPE; return token; case '?': advance(); token = Token.QUESTION; return token; case '!': advance(); token = Token.BANG; return token; case '=': advance(); token = Token.EQUAL; return token; default: if (isDecimalDigit(ch)) { token = scanNumber(); return token; } // type string permits following case, // // namespace.module.MyClass // // this reduced 1 token TK_NAME if (isTypeName(ch)) { token = scanTypeName(); return token; } token = Token.ILLEGAL; return token; } } function consume(target, text) { assert(token === target, text || 'consumed token not matched'); next(); } function expect(target) { if (token !== target) { throwError('unexpected token'); } next(); } // UnionType := '(' TypeUnionList ')' // // TypeUnionList := // <<empty>> // | NonemptyTypeUnionList // // NonemptyTypeUnionList := // TypeExpression // | TypeExpression '|' NonemptyTypeUnionList function parseUnionType() { var elements; 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 { type: Syntax.UnionType, elements: elements }; } // ArrayType := '[' ElementTypeList ']' // // ElementTypeList := // <<empty>> // | TypeExpression // | '...' TypeExpression // | TypeExpression ',' ElementTypeList function parseArrayType() { var elements; consume(Token.LBRACK, 'ArrayType should start with ['); elements = []; while (token !== Token.RBRACK) { if (token === Token.REST) { consume(Token.REST); elements.push({ type: Syntax.RestType, expression: parseTypeExpression() }); break; } else { elements.push(parseTypeExpression()); } if (token !== Token.RBRACK) { expect(Token.COMMA); } } expect(Token.RBRACK); return { type: Syntax.ArrayType, elements: elements }; } 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); } throwError('unexpected token'); } // FieldType := // FieldName // | FieldName ':' TypeExpression // // FieldName := // NameExpression // | StringLiteral // | NumberLiteral // | ReservedIdentifier function parseFieldType() { var key; key = parseFieldName(); if (token === Token.COLON) { consume(Token.COLON); return { type: Syntax.FieldType, key: key, value: parseTypeExpression() }; } return { type: Syntax.FieldType, key: key, value: null }; } // RecordType := '{' FieldTypeList '}' // // FieldTypeList := // <<empty>> // | FieldType // | FieldType ',' FieldTypeList function parseRecordType() { var fields, field; 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); } } } expect(Token.RBRACE); return { type: Syntax.RecordType, fields: fields }; } function parseNameExpression() { var name = value; expect(Token.NAME); return { type: Syntax.NameExpression, name: name }; } // 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; expr = parseNameExpression(); if (token === Token.DOT_LT || token === Token.LT) { next(); applications = parseTypeExpressionList(); expect(Token.GT); return { type: Syntax.TypeApplication, expression: expr, applications: applications }; } 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 = [], normal = true, expr, rest = false; while (token !== Token.RPAREN) { if (token === Token.REST) { // RestParameterType consume(Token.REST); rest = true; } expr = parseTypeExpression(); if (expr.type === Syntax.NameExpression && token === Token.COLON) { // Identifier ':' TypeExpression consume(Token.COLON); expr = { type: Syntax.ParameterType, name: expr.name, expression: parseTypeExpression() }; } if (token === Token.EQUAL) { consume(Token.EQUAL); expr = { type: Syntax.OptionalType, expression: expr }; normal = false; } else { if (!normal) { throwError('unexpected token'); } } if (rest) { expr = { type: Syntax.RestType, expression: expr }; } 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, name; 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 = { type: Syntax.FunctionType, params: params, result: result }; 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; switch (token) { case Token.STAR: consume(Token.STAR); return { type: Syntax.AllLiteral }; case Token.LPAREN: return parseUnionType(); case Token.LBRACK: return parseArrayType(); case Token.LBRACE: return parseRecordType(); case Token.NAME: if (value === 'null') { consume(Token.NAME); return { type: Syntax.NullLiteral }; } if (value === 'undefined') { consume(Token.NAME); return { type: Syntax.UndefinedLiteral }; } context = Context.save(); if (value === 'function') { try { return parseFunctionType(); } catch (e) { context.restore(); } } return parseTypeName(); default: throwError("unexpected token"); } } // TypeExpression := // BasicTypeExpression // | '?' BasicTypeExpression // | '!' BasicTypeExpression // | BasicTypeExpression '?' // | BasicTypeExpression '!' // | '?' // | BasicTypeExpression '[]' function parseTypeExpression() { var expr; if (token === Token.QUESTION) { 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) { return { type: Syntax.NullableLiteral }; } return { type: Syntax.NullableType, expression: parseBasicTypeExpression(), prefix: true }; } if (token === Token.BANG) { consume(Token.BANG); return { type: Syntax.NonNullableType, expression: parseBasicTypeExpression(), prefix: true }; } expr = parseBasicTypeExpression(); if (token === Token.BANG) { consume(Token.BANG); return { type: Syntax.NonNullableType, expression: expr, prefix: false }; } if (token === Token.QUESTION) { consume(Token.QUESTION); return { type: Syntax.NullableType, expression: expr, prefix: false }; } if (token === Token.LBRACK) { consume(Token.LBRACK); consume(Token.RBRACK, 'expected an array-style type declaration (' + value + '[])'); return { type: Syntax.TypeApplication, expression: { type: Syntax.NameExpression, name: 'Array' }, applications: [expr] }; } 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 { type: Syntax.UnionType, elements: elements }; } function parseTopParamType() { var expr; if (token === Token.REST) { consume(Token.REST); return { type: Syntax.RestType, expression: parseTop() }; } expr = parseTop(); if (token === Token.EQUAL) { consume(Token.EQUAL); return { type: Syntax.OptionalType, expression: expr }; } return expr; } function parseType(src, opt) { var expr; source = src; length = source.length; index = 0; previous = 0; if (!CanAccessStringByIndex) { source = source.split(''); } next(); expr = parseTop(); if (opt && opt.midstream) { return { expression: expr, index: previous }; } if (token !== Token.EOF) { throwError('not reach to EOF'); } return expr; } function parseParamType(src, opt) { var expr; source = src; length = source.length; index = 0; previous = 0; if (!CanAccessStringByIndex) { source = source.split(''); } next(); expr = parseTopParamType(); if (opt && opt.midstream) { return { expression: expr, index: previous }; } if (token !== Token.EOF) { 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 += stringifyI