/* Copyright (C) 2012-2014 Yusuke Suzuki Copyright (C) 2014 Dan Tao Copyright (C) 2013 Andrew Eisenberg 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 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 := // <> // | 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 := // <> // | 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 := // <> // | 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 := // <> // | ':' void // | ':' TypeExpression // // BNF is above // but, we remove <> 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 : */