mirror of
https://github.com/github/codeql.git
synced 2025-12-16 08:43:11 +01:00
2147 lines
77 KiB
JavaScript
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 : */
|