Files
codeql/javascript/extractor/lib/external/doctrine/doctrine.js
2018-11-07 07:48:25 +00:00

2147 lines
77 KiB
JavaScript

/*
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) {
var output;
if (!CanAccessStringByIndex) {
output = source.slice(index, last).join('');
} else {
output = source.slice(index, last);
}
return output;
}
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);
};
}());
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' ||
title === 'public' || title === 'private' || title === 'protected';
}
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') {
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; //TODO review removal 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
// Deprecating unused code. TODO review removal
//if (code !== 0) {
// octal = true;
//}
if (index < length && isOctalDigit(source[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 &&
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;
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;
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 += stringifyImpl(node.elements[i], compact);
if ((i + 1) !== iz) {
result += '|';
}
}
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;
default:
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;
}(typed = {}));
// JSDoc Tag Parser
(function (exports) {
var Rules,
index,
lineNumber,
lineStart,
length,
source,
recoverable,
sloppy,
strict;
function skipStars(index) {
while (index < length && isWhiteSpace(source[index]) && !isLineTerminator(source[index])) {
index += 1;
}
while (source[index] === '*') {
index += 1;
}
while (index < length && isWhiteSpace(source[index]) && !isLineTerminator(source[index])) {
index += 1;
}
return index;
}
function advance() {
var ch = source[index];
index += 1;
if (isLineTerminator(ch) && !(ch === '\r' && source[index] === '\n')) {
lineNumber += 1;
lineStart = index;
index = skipStars(index);
}
return ch;
}
function scanTitle() {
var title = '';
// waste '@'
advance();
while (index < length && isASCIIAlphanumeric(source[index])) {
title += advance();
}
return title;
}
function seekContent() {
var ch, waiting, last = index;
waiting = false;
while (last < length) {
ch = source[last];
if (isLineTerminator(ch) && !(ch === '\r' && source[last + 1] === '\n')) {
lineNumber += 1;
lineStart = last + 1;
last = skipStars(last + 1) - 1;
waiting = true;
} else if (waiting) {
if (ch === '@') {
break;
}
if (!isWhiteSpace(ch)) {
waiting = false;
}
}
last += 1;
}
return last;
}
// type expression may have nest brace, such as,
// { { ok: string } }
//
// therefore, scanning type expression with balancing braces.
function parseType(title, last) {
var ch, brace, type, direct = false;
// search '{'
while (index < last) {
ch = source[index];
if (isWhiteSpace(ch)) {
advance();
} else if (ch === '{') {
advance();
break;
} else {
// this is direct pattern
direct = true;
break;
}
}
if (!direct) {
// type expression { is found
brace = 1;
type = '';
while (index < last) {
ch = source[index];
if (isLineTerminator(ch)) {
advance();
} else {
if (ch === '}') {
brace -= 1;
if (brace === 0) {
advance();
break;
}
} else if (ch === '{') {
brace += 1;
}
type += advance();
}
}
if (brace !== 0) {
// braces is not balanced
return throwError('Braces are not balanced');
}
try {
if (isParamTitle(title)) {
return typed.parseParamType(type);
}
return typed.parseType(type);
} catch (e1) {
// parse failed
return null;
}
} else {
return null;
}
}
function scanIdentifier(last) {
var identifier;
if (!isIdentifierStart(source[index])) {
return null;
}
identifier = advance();
while (index < last && isIdentifierPart(source[index])) {
identifier += advance();
}
return identifier;
}
function skipWhiteSpace(last) {
while (index < last && (isWhiteSpace(source[index]) || isLineTerminator(source[index]))) {
advance();
}
}
function parseName(last, allowBrackets, allowNestedParams) {
var name = '', useBrackets;
skipWhiteSpace(last);
if (index >= last) {
return null;
}
if (allowBrackets && source[index] === '[') {
useBrackets = true;
name = advance();
}
if (!isIdentifierStart(source[index])) {
return null;
}
name += scanIdentifier(last);
if (allowNestedParams) {
while (source[index] === '.' || source[index] === '#' || source[index] === '~') {
name += source[index];
index += 1;
name += scanIdentifier(last);
}
}
if (useBrackets) {
// do we have a default value for this?
if (source[index] === '=') {
// consume the '='' symbol
name += advance();
// scan in the default value
while (index < last && source[index] !== ']') {
name += advance();
}
}
if (index >= last || source[index] !== ']') {
// we never found a closing ']'
return null;
}
// collect the last ']'
name += advance();
}
return name;
}
function skipToTag() {
while (index < length && source[index] !== '@') {
advance();
}
if (index >= length) {
return false;
}
assert(source[index] === '@');
return true;
}
function TagParser(options, title) {
this._options = options;
this._title = title;
this._tag = {
title: title,
description: null
};
if (this._options.lineNumbers) {
this._tag.lineNumber = lineNumber;
}
this._last = 0;
// space to save special information for title parsers.
this._extra = { };
}
// addError(err, ...)
TagParser.prototype.addError = function addError(errorText) {
var args = Array.prototype.slice.call(arguments, 1),
msg = errorText.replace(
/%(\d)/g,
function (whole, index) {
assert(index < args.length, 'Message reference must be in range');
return args[index];
}
);
if (!this._tag.errors) {
this._tag.errors = [];
}
if (strict) {
throwError(msg);
}
this._tag.errors.push(msg);
return recoverable;
};
TagParser.prototype.parseType = function () {
// type required titles
if (isTypeParameterRequired(this._title)) {
try {
this._tag.type = parseType(this._title, this._last);
if (!this._tag.type) {
if (!isParamTitle(this._title)) {
if (!this.addError('Missing or invalid tag type')) {
return false;
}
}
}
} catch (error) {
this._tag.type = null;
if (!this.addError(error.message)) {
return false;
}
}
} else if (isAllowedType(this._title)) {
// optional types
try {
this._tag.type = parseType(this._title, this._last);
} catch (e) {
//For optional types, lets drop the thrown error when we hit the end of the file
}
}
return true;
};
TagParser.prototype._parseNamePath = function (optional) {
var name;
name = parseName(this._last, sloppy && isParamTitle(this._title), true);
if (!name) {
if (!optional) {
if (!this.addError('Missing or invalid tag name')) {
return false;
}
}
}
this._tag.name = name;
return true;
};
TagParser.prototype.parseNamePath = function () {
return this._parseNamePath(false);
};
TagParser.prototype.parseNamePathOptional = function () {
return this._parseNamePath(true);
};
TagParser.prototype.parseName = function () {
var assign, name;
// param, property requires name
if (isAllowedName(this._title)) {
this._tag.name = parseName(this._last, sloppy && isParamTitle(this._title), isAllowedNested(this._title));
if (!this._tag.name) {
if (!isNameParameterRequired(this._title)) {
return true;
}
// it's possible the name has already been parsed but interpreted as a type
// it's also possible this is a sloppy declaration, in which case it will be
// fixed at the end
if (isParamTitle(this._title) && this._tag.type && this._tag.type.name) {
this._extra.name = this._tag.type;
this._tag.name = this._tag.type.name;
this._tag.type = null;
} else {
if (!this.addError('Missing or invalid tag name')) {
return false;
}
}
} else {
name = this._tag.name;
if (name.charAt(0) === '[' && name.charAt(name.length - 1) === ']') {
// extract the default value if there is one
// example: @param {string} [somebody=John Doe] description
assign = name.substring(1, name.length - 1).split('=');
if (assign[1]) {
this._tag['default'] = assign[1];
}
this._tag.name = assign[0];
// convert to an optional type
if (this._tag.type && this._tag.type.type !== 'OptionalType') {
this._tag.type = {
type: 'OptionalType',
expression: this._tag.type
};
}
}
}
}
return true;
};
TagParser.prototype.parseDescription = function parseDescription() {
var description = trim(sliceSource(source, index, this._last));
if (description) {
if ((/^-\s+/).test(description)) {
description = description.substring(2);
}
if (this._options.unwrap) {
description = description.replace(/^\s*\*+\s*/gm, '');
}
this._tag.description = description;
}
return true;
};
TagParser.prototype.parseKind = function parseKind() {
var kind, kinds;
kinds = {
'class': true,
'constant': true,
'event': true,
'external': true,
'file': true,
'function': true,
'member': true,
'mixin': true,
'module': true,
'namespace': true,
'typedef': true
};
kind = trim(sliceSource(source, index, this._last));
this._tag.kind = kind;
if (!hasOwnProperty(kinds, kind)) {
if (!this.addError('Invalid kind name \'%0\'', kind)) {
return false;
}
}
return true;
};
TagParser.prototype.parseAccess = function parseAccess() {
var access;
access = trim(sliceSource(source, index, this._last));
this._tag.access = access;
if (access !== 'private' && access !== 'protected' && access !== 'public') {
if (!this.addError('Invalid access name \'%0\'', access)) {
return false;
}
}
return true;
};
TagParser.prototype.parseVariation = function parseVariation() {
var variation, text;
text = trim(sliceSource(source, index, this._last));
variation = parseFloat(text, 10);
this._tag.variation = variation;
if (isNaN(variation)) {
if (!this.addError('Invalid variation \'%0\'', text)) {
return false;
}
}
return true;
};
TagParser.prototype.ensureEnd = function () {
var shouldBeEmpty = trim(sliceSource(source, index, this._last));
if (!shouldBeEmpty.match(/^[\s*]*$/)) {
if (!this.addError('Unknown content \'%0\'', shouldBeEmpty)) {
return false;
}
}
return true;
};
TagParser.prototype.epilogue = function epilogue() {
var description;
description = this._tag.description;
// un-fix potentially sloppy declaration
if (isParamTitle(this._title) && !this._tag.type && description && description.charAt(0) === '[') {
this._tag.type = this._extra.name;
this._tag.name = undefined;
if (!sloppy) {
if (!this.addError('Missing or invalid tag name')) {
return false;
}
}
}
return true;
};
Rules = {
// http://usejsdoc.org/tags-access.html
'access': ['parseAccess'],
// http://usejsdoc.org/tags-alias.html
'alias': ['parseNamePath', 'ensureEnd'],
// http://usejsdoc.org/tags-augments.html
'augments': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
// http://usejsdoc.org/tags-constructor.html
'constructor': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
// Synonym: http://usejsdoc.org/tags-constructor.html
'class': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
// Synonym: http://usejsdoc.org/tags-extends.html
'extends': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
// http://usejsdoc.org/tags-deprecated.html
'deprecated': ['parseDescription'],
// http://usejsdoc.org/tags-global.html
'global': ['ensureEnd'],
// http://usejsdoc.org/tags-inner.html
'inner': ['ensureEnd'],
// http://usejsdoc.org/tags-instance.html
'instance': ['ensureEnd'],
// http://usejsdoc.org/tags-kind.html
'kind': ['parseKind'],
// http://usejsdoc.org/tags-mixes.html
'mixes': ['parseNamePath', 'ensureEnd'],
// http://usejsdoc.org/tags-mixin.html
'mixin': ['parseNamePathOptional', 'ensureEnd'],
// http://usejsdoc.org/tags-member.html
'member': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
// http://usejsdoc.org/tags-method.html
'method': ['parseNamePathOptional', 'ensureEnd'],
// http://usejsdoc.org/tags-module.html
'module': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
// Synonym: http://usejsdoc.org/tags-method.html
'func': ['parseNamePathOptional', 'ensureEnd'],
// Synonym: http://usejsdoc.org/tags-method.html
'function': ['parseNamePathOptional', 'ensureEnd'],
// Synonym: http://usejsdoc.org/tags-member.html
'var': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
// http://usejsdoc.org/tags-name.html
'name': ['parseNamePath', 'ensureEnd'],
// http://usejsdoc.org/tags-namespace.html
'namespace': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
// http://usejsdoc.org/tags-private.html
'private': ['parseType', 'parseDescription'],
// http://usejsdoc.org/tags-protected.html
'protected': ['parseType', 'parseDescription'],
// http://usejsdoc.org/tags-public.html
'public': ['parseType', 'parseDescription'],
// http://usejsdoc.org/tags-readonly.html
'readonly': ['ensureEnd'],
// http://usejsdoc.org/tags-requires.html
'requires': ['parseNamePath', 'ensureEnd'],
// http://usejsdoc.org/tags-since.html
'since': ['parseDescription'],
// http://usejsdoc.org/tags-static.html
'static': ['ensureEnd'],
// http://usejsdoc.org/tags-summary.html
'summary': ['parseDescription'],
// http://usejsdoc.org/tags-this.html
'this': ['parseNamePath', 'ensureEnd'],
// http://usejsdoc.org/tags-todo.html
'todo': ['parseDescription'],
// http://usejsdoc.org/tags-variation.html
'variation': ['parseVariation'],
// http://usejsdoc.org/tags-version.html
'version': ['parseDescription']
};
TagParser.prototype.parse = function parse() {
var i, iz, sequences, method,
oldLineNumber, oldLineStart,
newLineNumber, newLineStart;
// empty title
if (!this._title) {
if (!this.addError('Missing or invalid title')) {
return null;
}
}
// Seek to content last index.
oldLineNumber = lineNumber;
oldLineStart = lineStart;
this._last = seekContent(this._title);
newLineNumber = lineNumber;
newLineStart = lineStart;
lineNumber = oldLineNumber;
lineStart = oldLineStart;
if (hasOwnProperty(Rules, this._title)) {
sequences = Rules[this._title];
} else {
// default sequences
sequences = ['parseType', 'parseName', 'parseDescription', 'epilogue'];
}
for (i = 0, iz = sequences.length; i < iz; ++i) {
method = sequences[i];
if (!this[method]()) {
return null;
}
}
// Seek global index to end of this tag.
index = this._last;
lineNumber = newLineNumber;
lineStart = newLineStart;
return this._tag;
};
function parseTag(options) {
var title, parser, startLine, startColumn, res;
// skip to tag
if (!skipToTag()) {
return null;
}
if (options.lineNumbers) {
startLine = lineNumber;
startColumn = index - lineStart;
}
// scan title
title = scanTitle();
// construct tag parser
parser = new TagParser(options, title);
res = parser.parse();
if (options.lineNumbers) {
res.startLine = startLine;
res.startColumn = startColumn;
}
return res;
}
//
// Parse JSDoc
//
function scanJSDocDescription() {
var description = '', ch, atAllowed;
atAllowed = true;
while (index < length) {
ch = source[index];
if (atAllowed && ch === '@') {
break;
}
if (isLineTerminator(ch)) {
atAllowed = true;
} else if (atAllowed && !isWhiteSpace(ch)) {
atAllowed = false;
}
description += advance();
}
return trim(description);
}
function parse(comment, options) {
var tags = [], tag, description, interestingTags, i, iz;
if (options === undefined) {
options = {};
}
if (typeof options.unwrap === 'boolean' && options.unwrap) {
source = comment.replace(/^\/?\*+/, '').replace(/\*+\/?$/, '');
} else {
source = comment;
}
// array of relevant tags
if (options.tags) {
if (isArray(options.tags)) {
interestingTags = { };
for (i = 0, iz = options.tags.length; i < iz; i++) {
if (typeof options.tags[i] === 'string') {
interestingTags[options.tags[i]] = true;
} else {
throwError('Invalid "tags" parameter: ' + options.tags);
}
}
} else {
throwError('Invalid "tags" parameter: ' + options.tags);
}
}
if (!CanAccessStringByIndex) {
source = source.split('');
}
length = source.length;
index = 0;
lineNumber = 0;
lineStart = 0;
recoverable = options.recoverable;
sloppy = options.sloppy;
strict = options.strict;
description = scanJSDocDescription();
while (true) {
tag = parseTag(options);
if (!tag) {
break;
}
if (!interestingTags || interestingTags.hasOwnProperty(tag.title)) {
tags.push(tag);
}
}
return {
description: description,
tags: tags
};
}
exports.parse = parse;
}(jsdoc = {}));
exports.version = VERSION;
exports.parse = jsdoc.parse;
exports.parseType = typed.parseType;
exports.parseParamType = typed.parseParamType;
exports.unwrapComment = unwrapComment;
exports.Syntax = shallowCopy(typed.Syntax);
exports.Error = DoctrineError;
exports.type = {
Syntax: exports.Syntax,
parseType: typed.parseType,
parseParamType: typed.parseParamType,
stringify: typed.stringify
};
}(typeof exports === 'undefined' ? (doctrine = {}) : exports));
/* vim: set sw=4 ts=4 et tw=80 : */