UNPKG

76.7 kBJavaScriptView Raw
1/*
2 Copyright (C) 2012-2014 Yusuke Suzuki <utatane.tea@gmail.com>
3 Copyright (C) 2014 Dan Tao <daniel.tao@gmail.com>
4 Copyright (C) 2013 Andrew Eisenberg <andrew@eisenberg.as>
5
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are met:
8
9 * Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
11 * Redistributions in binary form must reproduce the above copyright
12 notice, this list of conditions and the following disclaimer in the
13 documentation and/or other materials provided with the distribution.
14
15 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
19 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25*/
26
27/*jslint bitwise:true plusplus:true eqeq:true nomen:true*/
28/*global doctrine:true, exports:true, parseTypeExpression:true, parseTop:true*/
29
30(function (exports) {
31 'use strict';
32
33 var VERSION,
34 Regex,
35 CanAccessStringByIndex,
36 typed,
37 jsdoc,
38 isArray,
39 hasOwnProperty;
40
41 // Sync with package.json.
42 VERSION = '0.5.2-dev';
43
44 // See also tools/generate-unicode-regex.py.
45 Regex = {
46 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]'),
47 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]')
48 };
49
50 CanAccessStringByIndex = typeof 'doctrine'[0] !== undefined;
51
52 function sliceSource(source, index, last) {
53 var output;
54 if (!CanAccessStringByIndex) {
55 output = source.slice(index, last).join('');
56 } else {
57 output = source.slice(index, last);
58 }
59 return output;
60 }
61
62 isArray = Array.isArray;
63 if (!isArray) {
64 isArray = function isArray(ary) {
65 return Object.prototype.toString.call(ary) === '[object Array]';
66 };
67 }
68
69 hasOwnProperty = (function () {
70 var func = Object.prototype.hasOwnProperty;
71 return function hasOwnProperty(obj, name) {
72 return func.call(obj, name);
73 };
74 }());
75
76 function shallowCopy(obj) {
77 var ret = {}, key;
78 for (key in obj) {
79 if (obj.hasOwnProperty(key)) {
80 ret[key] = obj[key];
81 }
82 }
83 return ret;
84 }
85
86 function isLineTerminator(ch) {
87 return ch === '\n' || ch === '\r' || ch === '\u2028' || ch === '\u2029';
88 }
89
90 function isWhiteSpace(ch) {
91 return (ch === ' ') || (ch === '\u0009') || (ch === '\u000B') ||
92 (ch === '\u000C') || (ch === '\u00A0') ||
93 (ch.charCodeAt(0) >= 0x1680 &&
94 '\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\uFEFF'.indexOf(ch) >= 0);
95 }
96
97 function isDecimalDigit(ch) {
98 return '0123456789'.indexOf(ch) >= 0;
99 }
100
101 function isHexDigit(ch) {
102 return '0123456789abcdefABCDEF'.indexOf(ch) >= 0;
103 }
104
105 function isOctalDigit(ch) {
106 return '01234567'.indexOf(ch) >= 0;
107 }
108
109 function isASCIIAlphanumeric(ch) {
110 return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9');
111 }
112
113 function isIdentifierStart(ch) {
114 return (ch === '$') || (ch === '_') || (ch === '\\') ||
115 (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
116 ((ch.charCodeAt(0) >= 0x80) && Regex.NonAsciiIdentifierStart.test(ch));
117 }
118
119 function isIdentifierPart(ch) {
120 return (ch === '$') || (ch === '_') || (ch === '\\') ||
121 (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
122 ((ch >= '0') && (ch <= '9')) ||
123 ((ch.charCodeAt(0) >= 0x80) && Regex.NonAsciiIdentifierPart.test(ch));
124 }
125
126 function isTypeName(ch) {
127 return '><(){}[],:*|?!='.indexOf(ch) === -1 && !isWhiteSpace(ch) && !isLineTerminator(ch);
128 }
129
130 function isParamTitle(title) {
131 return title === 'param' || title === 'argument' || title === 'arg';
132 }
133
134 function isProperty(title) {
135 return title === 'property' || title === 'prop';
136 }
137
138 function isNameParameterRequired(title) {
139 return isParamTitle(title) || isProperty(title) ||
140 title === 'alias' || title === 'this' || title === 'mixes' || title === 'requires';
141 }
142
143 function isAllowedName(title) {
144 return isNameParameterRequired(title) || title === 'const' || title === 'constant';
145 }
146
147 function isAllowedNested(title) {
148 return isProperty(title) || isParamTitle(title);
149 }
150
151 function isTypeParameterRequired(title) {
152 return isParamTitle(title) || title === 'define' || title === 'enum' ||
153 title === 'implements' || title === 'return' ||
154 title === 'this' || title === 'type' || title === 'typedef' ||
155 title === 'returns' || isProperty(title);
156 }
157
158 // Consider deprecation instead using 'isTypeParameterRequired' and 'Rules' declaration to pick when a type is optional/required
159 // This would require changes to 'parseType'
160 function isAllowedType(title) {
161 return isTypeParameterRequired(title) || title === 'throws' || title === 'const' || title === 'constant' ||
162 title === 'namespace' || title === 'member' || title === 'var' || title === 'module' ||
163 title === 'constructor' || title === 'class' || title === 'extends' || title === 'augments' ||
164 title === 'public' || title === 'private' || title === 'protected';
165 }
166
167 function DoctrineError(message) {
168 this.name = 'DoctrineError';
169 this.message = message;
170 }
171 DoctrineError.prototype = new Error();
172 DoctrineError.prototype.constructor = DoctrineError;
173
174 function throwError(message) {
175 throw new DoctrineError(message);
176 }
177
178 function assert(cond, text) {
179 if (VERSION.slice(-3) === 'dev') {
180 if (!cond) {
181 throwError(text);
182 }
183 }
184 }
185
186 function trim(str) {
187 return str.replace(/^\s+/, '').replace(/\s+$/, '');
188 }
189
190 function unwrapComment(doc) {
191 // JSDoc comment is following form
192 // /**
193 // * .......
194 // */
195 // remove /**, */ and *
196 var BEFORE_STAR = 0,
197 STAR = 1,
198 AFTER_STAR = 2,
199 index,
200 len,
201 mode,
202 result,
203 ch;
204
205 doc = doc.replace(/^\/\*\*?/, '').replace(/\*\/$/, '');
206 index = 0;
207 len = doc.length;
208 mode = BEFORE_STAR;
209 result = '';
210
211 while (index < len) {
212 ch = doc[index];
213 switch (mode) {
214 case BEFORE_STAR:
215 if (isLineTerminator(ch)) {
216 result += ch;
217 } else if (ch === '*') {
218 mode = STAR;
219 } else if (!isWhiteSpace(ch)) {
220 result += ch;
221 mode = AFTER_STAR;
222 }
223 break;
224
225 case STAR:
226 if (!isWhiteSpace(ch)) {
227 result += ch;
228 }
229 mode = isLineTerminator(ch) ? BEFORE_STAR : AFTER_STAR;
230 break;
231
232 case AFTER_STAR:
233 result += ch;
234 if (isLineTerminator(ch)) {
235 mode = BEFORE_STAR;
236 }
237 break;
238 }
239 index += 1;
240 }
241
242 return result;
243 }
244
245 // Type Expression Parser
246
247 (function (exports) {
248 var Syntax,
249 Token,
250 source,
251 length,
252 index,
253 previous,
254 token,
255 value;
256
257 Syntax = {
258 NullableLiteral: 'NullableLiteral',
259 AllLiteral: 'AllLiteral',
260 NullLiteral: 'NullLiteral',
261 UndefinedLiteral: 'UndefinedLiteral',
262 VoidLiteral: 'VoidLiteral',
263 UnionType: 'UnionType',
264 ArrayType: 'ArrayType',
265 RecordType: 'RecordType',
266 FieldType: 'FieldType',
267 FunctionType: 'FunctionType',
268 ParameterType: 'ParameterType',
269 RestType: 'RestType',
270 NonNullableType: 'NonNullableType',
271 OptionalType: 'OptionalType',
272 NullableType: 'NullableType',
273 NameExpression: 'NameExpression',
274 TypeApplication: 'TypeApplication'
275 };
276
277 Token = {
278 ILLEGAL: 0, // ILLEGAL
279 DOT: 1, // .
280 DOT_LT: 2, // .<
281 REST: 3, // ...
282 LT: 4, // <
283 GT: 5, // >
284 LPAREN: 6, // (
285 RPAREN: 7, // )
286 LBRACE: 8, // {
287 RBRACE: 9, // }
288 LBRACK: 10, // [
289 RBRACK: 11, // ]
290 COMMA: 12, // ,
291 COLON: 13, // :
292 STAR: 14, // *
293 PIPE: 15, // |
294 QUESTION: 16, // ?
295 BANG: 17, // !
296 EQUAL: 18, // =
297 NAME: 19, // name token
298 STRING: 20, // string
299 NUMBER: 21, // number
300 EOF: 22
301 };
302
303 function Context(previous, index, token, value) {
304 this._previous = previous;
305 this._index = index;
306 this._token = token;
307 this._value = value;
308 }
309
310 Context.prototype.restore = function () {
311 previous = this._previous;
312 index = this._index;
313 token = this._token;
314 value = this._value;
315 };
316
317 Context.save = function () {
318 return new Context(previous, index, token, value);
319 };
320
321 function advance() {
322 var ch = source[index];
323 index += 1;
324 return ch;
325 }
326
327 function scanHexEscape(prefix) {
328 var i, len, ch, code = 0;
329
330 len = (prefix === 'u') ? 4 : 2;
331 for (i = 0; i < len; ++i) {
332 if (index < length && isHexDigit(source[index])) {
333 ch = advance();
334 code = code * 16 + '0123456789abcdef'.indexOf(ch.toLowerCase());
335 } else {
336 return '';
337 }
338 }
339 return String.fromCharCode(code);
340 }
341
342 function scanString() {
343 var str = '', quote, ch, code, unescaped, restore; //TODO review removal octal = false
344 quote = source[index];
345 ++index;
346
347 while (index < length) {
348 ch = advance();
349
350 if (ch === quote) {
351 quote = '';
352 break;
353 } else if (ch === '\\') {
354 ch = advance();
355 if (!isLineTerminator(ch)) {
356 switch (ch) {
357 case 'n':
358 str += '\n';
359 break;
360 case 'r':
361 str += '\r';
362 break;
363 case 't':
364 str += '\t';
365 break;
366 case 'u':
367 case 'x':
368 restore = index;
369 unescaped = scanHexEscape(ch);
370 if (unescaped) {
371 str += unescaped;
372 } else {
373 index = restore;
374 str += ch;
375 }
376 break;
377 case 'b':
378 str += '\b';
379 break;
380 case 'f':
381 str += '\f';
382 break;
383 case 'v':
384 str += '\v';
385 break;
386
387 default:
388 if (isOctalDigit(ch)) {
389 code = '01234567'.indexOf(ch);
390
391 // \0 is not octal escape sequence
392 // Deprecating unused code. TODO review removal
393 //if (code !== 0) {
394 // octal = true;
395 //}
396
397 if (index < length && isOctalDigit(source[index])) {
398 //TODO Review Removal octal = true;
399 code = code * 8 + '01234567'.indexOf(advance());
400
401 // 3 digits are only allowed when string starts
402 // with 0, 1, 2, 3
403 if ('0123'.indexOf(ch) >= 0 &&
404 index < length &&
405 isOctalDigit(source[index])) {
406 code = code * 8 + '01234567'.indexOf(advance());
407 }
408 }
409 str += String.fromCharCode(code);
410 } else {
411 str += ch;
412 }
413 break;
414 }
415 } else {
416 if (ch === '\r' && source[index] === '\n') {
417 ++index;
418 }
419 }
420 } else if (isLineTerminator(ch)) {
421 break;
422 } else {
423 str += ch;
424 }
425 }
426
427 if (quote !== '') {
428 throwError('unexpected quote');
429 }
430
431 value = str;
432 return Token.STRING;
433 }
434
435 function scanNumber() {
436 var number, ch;
437
438 number = '';
439 if (ch !== '.') {
440 number = advance();
441 ch = source[index];
442
443 if (number === '0') {
444 if (ch === 'x' || ch === 'X') {
445 number += advance();
446 while (index < length) {
447 ch = source[index];
448 if (!isHexDigit(ch)) {
449 break;
450 }
451 number += advance();
452 }
453
454 if (number.length <= 2) {
455 // only 0x
456 throwError('unexpected token');
457 }
458
459 if (index < length) {
460 ch = source[index];
461 if (isIdentifierStart(ch)) {
462 throwError('unexpected token');
463 }
464 }
465 value = parseInt(number, 16);
466 return Token.NUMBER;
467 }
468
469 if (isOctalDigit(ch)) {
470 number += advance();
471 while (index < length) {
472 ch = source[index];
473 if (!isOctalDigit(ch)) {
474 break;
475 }
476 number += advance();
477 }
478
479 if (index < length) {
480 ch = source[index];
481 if (isIdentifierStart(ch) || isDecimalDigit(ch)) {
482 throwError('unexpected token');
483 }
484 }
485 value = parseInt(number, 8);
486 return Token.NUMBER;
487 }
488
489 if (isDecimalDigit(ch)) {
490 throwError('unexpected token');
491 }
492 }
493
494 while (index < length) {
495 ch = source[index];
496 if (!isDecimalDigit(ch)) {
497 break;
498 }
499 number += advance();
500 }
501 }
502
503 if (ch === '.') {
504 number += advance();
505 while (index < length) {
506 ch = source[index];
507 if (!isDecimalDigit(ch)) {
508 break;
509 }
510 number += advance();
511 }
512 }
513
514 if (ch === 'e' || ch === 'E') {
515 number += advance();
516
517 ch = source[index];
518 if (ch === '+' || ch === '-') {
519 number += advance();
520 }
521
522 ch = source[index];
523 if (isDecimalDigit(ch)) {
524 number += advance();
525 while (index < length) {
526 ch = source[index];
527 if (!isDecimalDigit(ch)) {
528 break;
529 }
530 number += advance();
531 }
532 } else {
533 throwError('unexpected token');
534 }
535 }
536
537 if (index < length) {
538 ch = source[index];
539 if (isIdentifierStart(ch)) {
540 throwError('unexpected token');
541 }
542 }
543
544 value = parseFloat(number);
545 return Token.NUMBER;
546 }
547
548
549 function scanTypeName() {
550 var ch, ch2;
551
552 value = advance();
553 while (index < length && isTypeName(source[index])) {
554 ch = source[index];
555 if (ch === '.') {
556 if ((index + 1) < length) {
557 ch2 = source[index + 1];
558 if (ch2 === '<') {
559 break;
560 }
561 }
562 }
563 value += advance();
564 }
565 return Token.NAME;
566 }
567
568 function next() {
569 var ch;
570
571 previous = index;
572
573 while (index < length && isWhiteSpace(source[index])) {
574 advance();
575 }
576 if (index >= length) {
577 token = Token.EOF;
578 return token;
579 }
580
581 ch = source[index];
582 switch (ch) {
583 case '"':
584 token = scanString();
585 return token;
586
587 case ':':
588 advance();
589 token = Token.COLON;
590 return token;
591
592 case ',':
593 advance();
594 token = Token.COMMA;
595 return token;
596
597 case '(':
598 advance();
599 token = Token.LPAREN;
600 return token;
601
602 case ')':
603 advance();
604 token = Token.RPAREN;
605 return token;
606
607 case '[':
608 advance();
609 token = Token.LBRACK;
610 return token;
611
612 case ']':
613 advance();
614 token = Token.RBRACK;
615 return token;
616
617 case '{':
618 advance();
619 token = Token.LBRACE;
620 return token;
621
622 case '}':
623 advance();
624 token = Token.RBRACE;
625 return token;
626
627 case '.':
628 advance();
629 if (index < length) {
630 ch = source[index];
631 if (ch === '<') {
632 advance();
633 token = Token.DOT_LT;
634 return token;
635 }
636
637 if (ch === '.' && index + 1 < length && source[index + 1] === '.') {
638 advance();
639 advance();
640 token = Token.REST;
641 return token;
642 }
643
644 if (isDecimalDigit(ch)) {
645 token = scanNumber();
646 return token;
647 }
648 }
649 token = Token.DOT;
650 return token;
651
652 case '<':
653 advance();
654 token = Token.LT;
655 return token;
656
657 case '>':
658 advance();
659 token = Token.GT;
660 return token;
661
662 case '*':
663 advance();
664 token = Token.STAR;
665 return token;
666
667 case '|':
668 advance();
669 token = Token.PIPE;
670 return token;
671
672 case '?':
673 advance();
674 token = Token.QUESTION;
675 return token;
676
677 case '!':
678 advance();
679 token = Token.BANG;
680 return token;
681
682 case '=':
683 advance();
684 token = Token.EQUAL;
685 return token;
686
687 default:
688 if (isDecimalDigit(ch)) {
689 token = scanNumber();
690 return token;
691 }
692
693 // type string permits following case,
694 //
695 // namespace.module.MyClass
696 //
697 // this reduced 1 token TK_NAME
698 if (isTypeName(ch)) {
699 token = scanTypeName();
700 return token;
701 }
702
703 token = Token.ILLEGAL;
704 return token;
705 }
706 }
707
708 function consume(target, text) {
709 assert(token === target, text || 'consumed token not matched');
710 next();
711 }
712
713 function expect(target) {
714 if (token !== target) {
715 throwError('unexpected token');
716 }
717 next();
718 }
719
720 // UnionType := '(' TypeUnionList ')'
721 //
722 // TypeUnionList :=
723 // <<empty>>
724 // | NonemptyTypeUnionList
725 //
726 // NonemptyTypeUnionList :=
727 // TypeExpression
728 // | TypeExpression '|' NonemptyTypeUnionList
729 function parseUnionType() {
730 var elements;
731 consume(Token.LPAREN, 'UnionType should start with (');
732 elements = [];
733 if (token !== Token.RPAREN) {
734 while (true) {
735 elements.push(parseTypeExpression());
736 if (token === Token.RPAREN) {
737 break;
738 }
739 expect(Token.PIPE);
740 }
741 }
742 consume(Token.RPAREN, 'UnionType should end with )');
743 return {
744 type: Syntax.UnionType,
745 elements: elements
746 };
747 }
748
749 // ArrayType := '[' ElementTypeList ']'
750 //
751 // ElementTypeList :=
752 // <<empty>>
753 // | TypeExpression
754 // | '...' TypeExpression
755 // | TypeExpression ',' ElementTypeList
756 function parseArrayType() {
757 var elements;
758 consume(Token.LBRACK, 'ArrayType should start with [');
759 elements = [];
760 while (token !== Token.RBRACK) {
761 if (token === Token.REST) {
762 consume(Token.REST);
763 elements.push({
764 type: Syntax.RestType,
765 expression: parseTypeExpression()
766 });
767 break;
768 } else {
769 elements.push(parseTypeExpression());
770 }
771 if (token !== Token.RBRACK) {
772 expect(Token.COMMA);
773 }
774 }
775 expect(Token.RBRACK);
776 return {
777 type: Syntax.ArrayType,
778 elements: elements
779 };
780 }
781
782 function parseFieldName() {
783 var v = value;
784 if (token === Token.NAME || token === Token.STRING) {
785 next();
786 return v;
787 }
788
789 if (token === Token.NUMBER) {
790 consume(Token.NUMBER);
791 return String(v);
792 }
793
794 throwError('unexpected token');
795 }
796
797 // FieldType :=
798 // FieldName
799 // | FieldName ':' TypeExpression
800 //
801 // FieldName :=
802 // NameExpression
803 // | StringLiteral
804 // | NumberLiteral
805 // | ReservedIdentifier
806 function parseFieldType() {
807 var key;
808
809 key = parseFieldName();
810 if (token === Token.COLON) {
811 consume(Token.COLON);
812 return {
813 type: Syntax.FieldType,
814 key: key,
815 value: parseTypeExpression()
816 };
817 }
818 return {
819 type: Syntax.FieldType,
820 key: key,
821 value: null
822 };
823 }
824
825 // RecordType := '{' FieldTypeList '}'
826 //
827 // FieldTypeList :=
828 // <<empty>>
829 // | FieldType
830 // | FieldType ',' FieldTypeList
831 function parseRecordType() {
832 var fields;
833
834 consume(Token.LBRACE, 'RecordType should start with {');
835 fields = [];
836 if (token === Token.COMMA) {
837 consume(Token.COMMA);
838 } else {
839 while (token !== Token.RBRACE) {
840 fields.push(parseFieldType());
841 if (token !== Token.RBRACE) {
842 expect(Token.COMMA);
843 }
844 }
845 }
846 expect(Token.RBRACE);
847 return {
848 type: Syntax.RecordType,
849 fields: fields
850 };
851 }
852
853 function parseNameExpression() {
854 var name = value;
855 expect(Token.NAME);
856 return {
857 type: Syntax.NameExpression,
858 name: name
859 };
860 }
861
862 // TypeExpressionList :=
863 // TopLevelTypeExpression
864 // | TopLevelTypeExpression ',' TypeExpressionList
865 function parseTypeExpressionList() {
866 var elements = [];
867
868 elements.push(parseTop());
869 while (token === Token.COMMA) {
870 consume(Token.COMMA);
871 elements.push(parseTop());
872 }
873 return elements;
874 }
875
876 // TypeName :=
877 // NameExpression
878 // | NameExpression TypeApplication
879 //
880 // TypeApplication :=
881 // '.<' TypeExpressionList '>'
882 // | '<' TypeExpressionList '>' // this is extension of doctrine
883 function parseTypeName() {
884 var expr, applications;
885
886 expr = parseNameExpression();
887 if (token === Token.DOT_LT || token === Token.LT) {
888 next();
889 applications = parseTypeExpressionList();
890 expect(Token.GT);
891 return {
892 type: Syntax.TypeApplication,
893 expression: expr,
894 applications: applications
895 };
896 }
897 return expr;
898 }
899
900 // ResultType :=
901 // <<empty>>
902 // | ':' void
903 // | ':' TypeExpression
904 //
905 // BNF is above
906 // but, we remove <<empty>> pattern, so token is always TypeToken::COLON
907 function parseResultType() {
908 consume(Token.COLON, 'ResultType should start with :');
909 if (token === Token.NAME && value === 'void') {
910 consume(Token.NAME);
911 return {
912 type: Syntax.VoidLiteral
913 };
914 }
915 return parseTypeExpression();
916 }
917
918 // ParametersType :=
919 // RestParameterType
920 // | NonRestParametersType
921 // | NonRestParametersType ',' RestParameterType
922 //
923 // RestParameterType :=
924 // '...'
925 // '...' Identifier
926 //
927 // NonRestParametersType :=
928 // ParameterType ',' NonRestParametersType
929 // | ParameterType
930 // | OptionalParametersType
931 //
932 // OptionalParametersType :=
933 // OptionalParameterType
934 // | OptionalParameterType, OptionalParametersType
935 //
936 // OptionalParameterType := ParameterType=
937 //
938 // ParameterType := TypeExpression | Identifier ':' TypeExpression
939 //
940 // Identifier is "new" or "this"
941 function parseParametersType() {
942 var params = [], normal = true, expr, rest = false;
943
944 while (token !== Token.RPAREN) {
945 if (token === Token.REST) {
946 // RestParameterType
947 consume(Token.REST);
948 rest = true;
949 }
950
951 expr = parseTypeExpression();
952 if (expr.type === Syntax.NameExpression && token === Token.COLON) {
953 // Identifier ':' TypeExpression
954 consume(Token.COLON);
955 expr = {
956 type: Syntax.ParameterType,
957 name: expr.name,
958 expression: parseTypeExpression()
959 };
960 }
961 if (token === Token.EQUAL) {
962 consume(Token.EQUAL);
963 expr = {
964 type: Syntax.OptionalType,
965 expression: expr
966 };
967 normal = false;
968 } else {
969 if (!normal) {
970 throwError('unexpected token');
971 }
972 }
973 if (rest) {
974 expr = {
975 type: Syntax.RestType,
976 expression: expr
977 };
978 }
979 params.push(expr);
980 if (token !== Token.RPAREN) {
981 expect(Token.COMMA);
982 }
983 }
984 return params;
985 }
986
987 // FunctionType := 'function' FunctionSignatureType
988 //
989 // FunctionSignatureType :=
990 // | TypeParameters '(' ')' ResultType
991 // | TypeParameters '(' ParametersType ')' ResultType
992 // | TypeParameters '(' 'this' ':' TypeName ')' ResultType
993 // | TypeParameters '(' 'this' ':' TypeName ',' ParametersType ')' ResultType
994 function parseFunctionType() {
995 var isNew, thisBinding, params, result, fnType;
996 assert(token === Token.NAME && value === 'function', 'FunctionType should start with \'function\'');
997 consume(Token.NAME);
998
999 // Google Closure Compiler is not implementing TypeParameters.
1000 // So we do not. if we don't get '(', we see it as error.
1001 expect(Token.LPAREN);
1002
1003 isNew = false;
1004 params = [];
1005 thisBinding = null;
1006 if (token !== Token.RPAREN) {
1007 // ParametersType or 'this'
1008 if (token === Token.NAME &&
1009 (value === 'this' || value === 'new')) {
1010 // 'this' or 'new'
1011 // 'new' is Closure Compiler extension
1012 isNew = value === 'new';
1013 consume(Token.NAME);
1014 expect(Token.COLON);
1015 thisBinding = parseTypeName();
1016 if (token === Token.COMMA) {
1017 consume(Token.COMMA);
1018 params = parseParametersType();
1019 }
1020 } else {
1021 params = parseParametersType();
1022 }
1023 }
1024
1025 expect(Token.RPAREN);
1026
1027 result = null;
1028 if (token === Token.COLON) {
1029 result = parseResultType();
1030 }
1031
1032 fnType = {
1033 type: Syntax.FunctionType,
1034 params: params,
1035 result: result
1036 };
1037 if (thisBinding) {
1038 // avoid adding null 'new' and 'this' properties
1039 fnType['this'] = thisBinding;
1040 if (isNew) {
1041 fnType['new'] = true;
1042 }
1043 }
1044 return fnType;
1045 }
1046
1047 // BasicTypeExpression :=
1048 // '*'
1049 // | 'null'
1050 // | 'undefined'
1051 // | TypeName
1052 // | FunctionType
1053 // | UnionType
1054 // | RecordType
1055 // | ArrayType
1056 function parseBasicTypeExpression() {
1057 var context;
1058 switch (token) {
1059 case Token.STAR:
1060 consume(Token.STAR);
1061 return {
1062 type: Syntax.AllLiteral
1063 };
1064
1065 case Token.LPAREN:
1066 return parseUnionType();
1067
1068 case Token.LBRACK:
1069 return parseArrayType();
1070
1071 case Token.LBRACE:
1072 return parseRecordType();
1073
1074 case Token.NAME:
1075 if (value === 'null') {
1076 consume(Token.NAME);
1077 return {
1078 type: Syntax.NullLiteral
1079 };
1080 }
1081
1082 if (value === 'undefined') {
1083 consume(Token.NAME);
1084 return {
1085 type: Syntax.UndefinedLiteral
1086 };
1087 }
1088
1089 context = Context.save();
1090 if (value === 'function') {
1091 try {
1092 return parseFunctionType();
1093 } catch (e) {
1094 context.restore();
1095 }
1096 }
1097
1098 return parseTypeName();
1099
1100 default:
1101 throwError('unexpected token');
1102 }
1103 }
1104
1105 // TypeExpression :=
1106 // BasicTypeExpression
1107 // | '?' BasicTypeExpression
1108 // | '!' BasicTypeExpression
1109 // | BasicTypeExpression '?'
1110 // | BasicTypeExpression '!'
1111 // | '?'
1112 // | BasicTypeExpression '[]'
1113 function parseTypeExpression() {
1114 var expr;
1115
1116 if (token === Token.QUESTION) {
1117 consume(Token.QUESTION);
1118 if (token === Token.COMMA || token === Token.EQUAL || token === Token.RBRACE ||
1119 token === Token.RPAREN || token === Token.PIPE || token === Token.EOF ||
1120 token === Token.RBRACK) {
1121 return {
1122 type: Syntax.NullableLiteral
1123 };
1124 }
1125 return {
1126 type: Syntax.NullableType,
1127 expression: parseBasicTypeExpression(),
1128 prefix: true
1129 };
1130 }
1131
1132 if (token === Token.BANG) {
1133 consume(Token.BANG);
1134 return {
1135 type: Syntax.NonNullableType,
1136 expression: parseBasicTypeExpression(),
1137 prefix: true
1138 };
1139 }
1140
1141 expr = parseBasicTypeExpression();
1142 if (token === Token.BANG) {
1143 consume(Token.BANG);
1144 return {
1145 type: Syntax.NonNullableType,
1146 expression: expr,
1147 prefix: false
1148 };
1149 }
1150
1151 if (token === Token.QUESTION) {
1152 consume(Token.QUESTION);
1153 return {
1154 type: Syntax.NullableType,
1155 expression: expr,
1156 prefix: false
1157 };
1158 }
1159
1160 if (token === Token.LBRACK) {
1161 consume(Token.LBRACK);
1162 consume(Token.RBRACK, 'expected an array-style type declaration (' + value + '[])');
1163 return {
1164 type: Syntax.TypeApplication,
1165 expression: {
1166 type: Syntax.NameExpression,
1167 name: 'Array'
1168 },
1169 applications: [expr]
1170 };
1171 }
1172
1173 return expr;
1174 }
1175
1176 // TopLevelTypeExpression :=
1177 // TypeExpression
1178 // | TypeUnionList
1179 //
1180 // This rule is Google Closure Compiler extension, not ES4
1181 // like,
1182 // { number | string }
1183 // If strict to ES4, we should write it as
1184 // { (number|string) }
1185 function parseTop() {
1186 var expr, elements;
1187
1188 expr = parseTypeExpression();
1189 if (token !== Token.PIPE) {
1190 return expr;
1191 }
1192
1193 elements = [ expr ];
1194 consume(Token.PIPE);
1195 while (true) {
1196 elements.push(parseTypeExpression());
1197 if (token !== Token.PIPE) {
1198 break;
1199 }
1200 consume(Token.PIPE);
1201 }
1202
1203 return {
1204 type: Syntax.UnionType,
1205 elements: elements
1206 };
1207 }
1208
1209 function parseTopParamType() {
1210 var expr;
1211
1212 if (token === Token.REST) {
1213 consume(Token.REST);
1214 return {
1215 type: Syntax.RestType,
1216 expression: parseTop()
1217 };
1218 }
1219
1220 expr = parseTop();
1221 if (token === Token.EQUAL) {
1222 consume(Token.EQUAL);
1223 return {
1224 type: Syntax.OptionalType,
1225 expression: expr
1226 };
1227 }
1228
1229 return expr;
1230 }
1231
1232 function parseType(src, opt) {
1233 var expr;
1234
1235 source = src;
1236 length = source.length;
1237 index = 0;
1238 previous = 0;
1239
1240 if (!CanAccessStringByIndex) {
1241 source = source.split('');
1242 }
1243
1244 next();
1245 expr = parseTop();
1246
1247 if (opt && opt.midstream) {
1248 return {
1249 expression: expr,
1250 index: previous
1251 };
1252 }
1253
1254 if (token !== Token.EOF) {
1255 throwError('not reach to EOF');
1256 }
1257
1258 return expr;
1259 }
1260
1261 function parseParamType(src, opt) {
1262 var expr;
1263
1264 source = src;
1265 length = source.length;
1266 index = 0;
1267 previous = 0;
1268
1269 if (!CanAccessStringByIndex) {
1270 source = source.split('');
1271 }
1272
1273 next();
1274 expr = parseTopParamType();
1275
1276 if (opt && opt.midstream) {
1277 return {
1278 expression: expr,
1279 index: previous
1280 };
1281 }
1282
1283 if (token !== Token.EOF) {
1284 throwError('not reach to EOF');
1285 }
1286
1287 return expr;
1288 }
1289
1290 function stringifyImpl(node, compact, topLevel) {
1291 var result, i, iz;
1292
1293 switch (node.type) {
1294 case Syntax.NullableLiteral:
1295 result = '?';
1296 break;
1297
1298 case Syntax.AllLiteral:
1299 result = '*';
1300 break;
1301
1302 case Syntax.NullLiteral:
1303 result = 'null';
1304 break;
1305
1306 case Syntax.UndefinedLiteral:
1307 result = 'undefined';
1308 break;
1309
1310 case Syntax.VoidLiteral:
1311 result = 'void';
1312 break;
1313
1314 case Syntax.UnionType:
1315 if (!topLevel) {
1316 result = '(';
1317 } else {
1318 result = '';
1319 }
1320
1321 for (i = 0, iz = node.elements.length; i < iz; ++i) {
1322 result += stringifyImpl(node.elements[i], compact);
1323 if ((i + 1) !== iz) {
1324 result += '|';
1325 }
1326 }
1327
1328 if (!topLevel) {
1329 result += ')';
1330 }
1331 break;
1332
1333 case Syntax.ArrayType:
1334 result = '[';
1335 for (i = 0, iz = node.elements.length; i < iz; ++i) {
1336 result += stringifyImpl(node.elements[i], compact);
1337 if ((i + 1) !== iz) {
1338 result += compact ? ',' : ', ';
1339 }
1340 }
1341 result += ']';
1342 break;
1343
1344 case Syntax.RecordType:
1345 result = '{';
1346 for (i = 0, iz = node.fields.length; i < iz; ++i) {
1347 result += stringifyImpl(node.fields[i], compact);
1348 if ((i + 1) !== iz) {
1349 result += compact ? ',' : ', ';
1350 }
1351 }
1352 result += '}';
1353 break;
1354
1355 case Syntax.FieldType:
1356 if (node.value) {
1357 result = node.key + (compact ? ':' : ': ') + stringifyImpl(node.value, compact);
1358 } else {
1359 result = node.key;
1360 }
1361 break;
1362
1363 case Syntax.FunctionType:
1364 result = compact ? 'function(' : 'function (';
1365
1366 if (node['this']) {
1367 if (node['new']) {
1368 result += (compact ? 'new:' : 'new: ');
1369 } else {
1370 result += (compact ? 'this:' : 'this: ');
1371 }
1372
1373 result += stringifyImpl(node['this'], compact);
1374
1375 if (node.params.length !== 0) {
1376 result += compact ? ',' : ', ';
1377 }
1378 }
1379
1380 for (i = 0, iz = node.params.length; i < iz; ++i) {
1381 result += stringifyImpl(node.params[i], compact);
1382 if ((i + 1) !== iz) {
1383 result += compact ? ',' : ', ';
1384 }
1385 }
1386
1387 result += ')';
1388
1389 if (node.result) {
1390 result += (compact ? ':' : ': ') + stringifyImpl(node.result, compact);
1391 }
1392 break;
1393
1394 case Syntax.ParameterType:
1395 result = node.name + (compact ? ':' : ': ') + stringifyImpl(node.expression, compact);
1396 break;
1397
1398 case Syntax.RestType:
1399 result = '...';
1400 if (node.expression) {
1401 result += stringifyImpl(node.expression, compact);
1402 }
1403 break;
1404
1405 case Syntax.NonNullableType:
1406 if (node.prefix) {
1407 result = '!' + stringifyImpl(node.expression, compact);
1408 } else {
1409 result = stringifyImpl(node.expression, compact) + '!';
1410 }
1411 break;
1412
1413 case Syntax.OptionalType:
1414 result = stringifyImpl(node.expression, compact) + '=';
1415 break;
1416
1417 case Syntax.NullableType:
1418 if (node.prefix) {
1419 result = '?' + stringifyImpl(node.expression, compact);
1420 } else {
1421 result = stringifyImpl(node.expression, compact) + '?';
1422 }
1423 break;
1424
1425 case Syntax.NameExpression:
1426 result = node.name;
1427 break;
1428
1429 case Syntax.TypeApplication:
1430 result = stringifyImpl(node.expression, compact) + '.<';
1431 for (i = 0, iz = node.applications.length; i < iz; ++i) {
1432 result += stringifyImpl(node.applications[i], compact);
1433 if ((i + 1) !== iz) {
1434 result += compact ? ',' : ', ';
1435 }
1436 }
1437 result += '>';
1438 break;
1439
1440 default:
1441 throwError('Unknown type ' + node.type);
1442 }
1443
1444 return result;
1445 }
1446
1447 function stringify(node, options) {
1448 if (options == null) {
1449 options = {};
1450 }
1451 return stringifyImpl(node, options.compact, options.topLevel);
1452 }
1453
1454 exports.parseType = parseType;
1455 exports.parseParamType = parseParamType;
1456 exports.stringify = stringify;
1457 exports.Syntax = Syntax;
1458 }(typed = {}));
1459
1460 // JSDoc Tag Parser
1461
1462 (function (exports) {
1463 var Rules,
1464 index,
1465 lineNumber,
1466 length,
1467 source,
1468 recoverable,
1469 sloppy,
1470 strict;
1471
1472 function advance() {
1473 var ch = source[index];
1474 index += 1;
1475 if (isLineTerminator(ch)) {
1476 lineNumber += 1;
1477 }
1478 return ch;
1479 }
1480
1481 function scanTitle() {
1482 var title = '';
1483 // waste '@'
1484 advance();
1485
1486 while (index < length && isASCIIAlphanumeric(source[index])) {
1487 title += advance();
1488 }
1489
1490 return title;
1491 }
1492
1493 function seekContent() {
1494 var ch, waiting, last = index;
1495
1496 waiting = false;
1497 while (last < length) {
1498 ch = source[last];
1499 if (isLineTerminator(ch)) {
1500 lineNumber += 1;
1501 waiting = true;
1502 } else if (waiting) {
1503 if (ch === '@') {
1504 break;
1505 }
1506 if (!isWhiteSpace(ch)) {
1507 waiting = false;
1508 }
1509 }
1510 last += 1;
1511 }
1512 return last;
1513 }
1514
1515 // type expression may have nest brace, such as,
1516 // { { ok: string } }
1517 //
1518 // therefore, scanning type expression with balancing braces.
1519 function parseType(title, last) {
1520 var ch, brace, type, direct = false;
1521
1522 // search '{'
1523 while (index < last) {
1524 ch = source[index];
1525 if (isWhiteSpace(ch)) {
1526 advance();
1527 } else if (ch === '{') {
1528 advance();
1529 break;
1530 } else {
1531 // this is direct pattern
1532 direct = true;
1533 break;
1534 }
1535 }
1536
1537 if (!direct) {
1538 // type expression { is found
1539 brace = 1;
1540 type = '';
1541 while (index < last) {
1542 ch = source[index];
1543 if (isLineTerminator(ch)) {
1544 advance();
1545 } else {
1546 if (ch === '}') {
1547 brace -= 1;
1548 if (brace === 0) {
1549 advance();
1550 break;
1551 }
1552 } else if (ch === '{') {
1553 brace += 1;
1554 }
1555 type += advance();
1556 }
1557 }
1558
1559 if (brace !== 0) {
1560 // braces is not balanced
1561 return throwError('Braces are not balanced');
1562 }
1563
1564 try {
1565 if (isParamTitle(title)) {
1566 return typed.parseParamType(type);
1567 }
1568 return typed.parseType(type);
1569 } catch (e1) {
1570 // parse failed
1571 return null;
1572 }
1573 } else {
1574 return null;
1575 }
1576 }
1577
1578 function scanIdentifier(last) {
1579 var identifier;
1580 if (!isIdentifierStart(source[index])) {
1581 return null;
1582 }
1583 identifier = advance();
1584 while (index < last && isIdentifierPart(source[index])) {
1585 identifier += advance();
1586 }
1587 return identifier;
1588 }
1589
1590 function skipWhiteSpace(last) {
1591 while (index < last && (isWhiteSpace(source[index]) || isLineTerminator(source[index]))) {
1592 advance();
1593 }
1594 }
1595
1596 function parseName(last, allowBrackets, allowNestedParams) {
1597 var name = '', useBrackets;
1598
1599 skipWhiteSpace(last);
1600
1601 if (index >= last) {
1602 return null;
1603 }
1604
1605 if (allowBrackets && source[index] === '[') {
1606 useBrackets = true;
1607 name = advance();
1608 }
1609
1610 if (!isIdentifierStart(source[index])) {
1611 return null;
1612 }
1613
1614 name += scanIdentifier(last);
1615
1616 if (allowNestedParams) {
1617 while (source[index] === '.') {
1618 name += '.';
1619 index += 1;
1620 name += scanIdentifier(last);
1621 }
1622 }
1623
1624 if (useBrackets) {
1625 // do we have a default value for this?
1626 if (source[index] === '=') {
1627
1628 // consume the '='' symbol
1629 name += advance();
1630 // scan in the default value
1631 while (index < last && source[index] !== ']') {
1632 name += advance();
1633 }
1634 }
1635
1636 if (index >= last || source[index] !== ']') {
1637 // we never found a closing ']'
1638 return null;
1639 }
1640
1641 // collect the last ']'
1642 name += advance();
1643 }
1644
1645 return name;
1646 }
1647
1648 function skipToTag() {
1649 while (index < length && source[index] !== '@') {
1650 advance();
1651 }
1652 if (index >= length) {
1653 return false;
1654 }
1655 assert(source[index] === '@');
1656 return true;
1657 }
1658
1659 function TagParser(options, title) {
1660 this._options = options;
1661 this._title = title;
1662 this._tag = {
1663 title: title,
1664 description: null
1665 };
1666 if (this._options.lineNumbers) {
1667 this._tag.lineNumber = lineNumber;
1668 }
1669 this._last = 0;
1670 // space to save special information for title parsers.
1671 this._extra = { };
1672 }
1673
1674 // addError(err, ...)
1675 TagParser.prototype.addError = function addError(errorText) {
1676 var args = Array.prototype.slice.call(arguments, 1),
1677 msg = errorText.replace(
1678 /%(\d)/g,
1679 function (whole, index) {
1680 assert(index < args.length, 'Message reference must be in range');
1681 return args[index];
1682 }
1683 );
1684
1685 if (!this._tag.errors) {
1686 this._tag.errors = [];
1687 }
1688 if (strict) {
1689 throwError(msg);
1690 }
1691 this._tag.errors.push(msg);
1692 return recoverable;
1693 };
1694
1695 TagParser.prototype.parseType = function () {
1696 // type required titles
1697 if (isTypeParameterRequired(this._title)) {
1698 try {
1699 this._tag.type = parseType(this._title, this._last);
1700 if (!this._tag.type) {
1701 if (!isParamTitle(this._title)) {
1702 if (!this.addError('Missing or invalid tag type')) {
1703 return false;
1704 }
1705 }
1706 }
1707 } catch (error) {
1708 this._tag.type = null;
1709 if (!this.addError(error.message)) {
1710 return false;
1711 }
1712 }
1713 } else if (isAllowedType(this._title)) {
1714 // optional types
1715 try {
1716 this._tag.type = parseType(this._title, this._last);
1717 } catch (e) {
1718 //For optional types, lets drop the thrown error when we hit the end of the file
1719 }
1720 }
1721 return true;
1722 };
1723
1724 TagParser.prototype._parseNamePath = function (optional) {
1725 var name;
1726 name = parseName(this._last, sloppy && isParamTitle(this._title), true);
1727 if (!name) {
1728 if (!optional) {
1729 if (!this.addError('Missing or invalid tag name')) {
1730 return false;
1731 }
1732 }
1733 }
1734 this._tag.name = name;
1735 return true;
1736 };
1737
1738 TagParser.prototype.parseNamePath = function () {
1739 return this._parseNamePath(false);
1740 };
1741
1742 TagParser.prototype.parseNamePathOptional = function () {
1743 return this._parseNamePath(true);
1744 };
1745
1746
1747 TagParser.prototype.parseName = function () {
1748 var assign, name;
1749
1750 // param, property requires name
1751 if (isAllowedName(this._title)) {
1752 this._tag.name = parseName(this._last, sloppy && isParamTitle(this._title), isAllowedNested(this._title));
1753 if (!this._tag.name) {
1754 if (!isNameParameterRequired(this._title)) {
1755 return true;
1756 }
1757
1758 // it's possible the name has already been parsed but interpreted as a type
1759 // it's also possible this is a sloppy declaration, in which case it will be
1760 // fixed at the end
1761 if (isParamTitle(this._title) && this._tag.type.name) {
1762 this._extra.name = this._tag.type;
1763 this._tag.name = this._tag.type.name;
1764 this._tag.type = null;
1765 } else {
1766 if (!this.addError('Missing or invalid tag name')) {
1767 return false;
1768 }
1769 }
1770 } else {
1771 name = this._tag.name;
1772 if (name.charAt(0) === '[' && name.charAt(name.length - 1) === ']') {
1773 // extract the default value if there is one
1774 // example: @param {string} [somebody=John Doe] description
1775 assign = name.substring(1, name.length - 1).split('=');
1776 if (assign[1]) {
1777 this._tag['default'] = assign[1];
1778 }
1779 this._tag.name = assign[0];
1780
1781 // convert to an optional type
1782 if (this._tag.type.type !== 'OptionalType') {
1783 this._tag.type = {
1784 type: 'OptionalType',
1785 expression: this._tag.type
1786 };
1787 }
1788 }
1789 }
1790 }
1791
1792 return true;
1793 };
1794
1795 TagParser.prototype.parseDescription = function parseDescription() {
1796 var description = trim(sliceSource(source, index, this._last));
1797 if (description) {
1798 if ((/^-\s+/).test(description)) {
1799 description = description.substring(2);
1800 }
1801 this._tag.description = description;
1802 }
1803 return true;
1804 };
1805
1806 TagParser.prototype.parseKind = function parseKind() {
1807 var kind, kinds;
1808 kinds = {
1809 'class': true,
1810 'constant': true,
1811 'event': true,
1812 'external': true,
1813 'file': true,
1814 'function': true,
1815 'member': true,
1816 'mixin': true,
1817 'module': true,
1818 'namespace': true,
1819 'typedef': true
1820 };
1821 kind = trim(sliceSource(source, index, this._last));
1822 this._tag.kind = kind;
1823 if (!hasOwnProperty(kinds, kind)) {
1824 if (!this.addError('Invalid kind name \'%0\'', kind)) {
1825 return false;
1826 }
1827 }
1828 return true;
1829 };
1830
1831 TagParser.prototype.parseAccess = function parseAccess() {
1832 var access;
1833 access = trim(sliceSource(source, index, this._last));
1834 this._tag.access = access;
1835 if (access !== 'private' && access !== 'protected' && access !== 'public') {
1836 if (!this.addError('Invalid access name \'%0\'', access)) {
1837 return false;
1838 }
1839 }
1840 return true;
1841 };
1842
1843 TagParser.prototype.parseVariation = function parseVariation() {
1844 var variation, text;
1845 text = trim(sliceSource(source, index, this._last));
1846 variation = parseFloat(text, 10);
1847 this._tag.variation = variation;
1848 if (isNaN(variation)) {
1849 if (!this.addError('Invalid variation \'%0\'', text)) {
1850 return false;
1851 }
1852 }
1853 return true;
1854 };
1855
1856 TagParser.prototype.ensureEnd = function () {
1857 var shouldBeEmpty = trim(sliceSource(source, index, this._last));
1858 if (shouldBeEmpty) {
1859 if (!this.addError('Unknown content \'%0\'', shouldBeEmpty)) {
1860 return false;
1861 }
1862 }
1863 return true;
1864 };
1865
1866 TagParser.prototype.epilogue = function epilogue() {
1867 var description;
1868
1869 description = this._tag.description;
1870 // un-fix potentially sloppy declaration
1871 if (isParamTitle(this._title) && !this._tag.type && description && description.charAt(0) === '[') {
1872 this._tag.type = this._extra.name;
1873 this._tag.name = undefined;
1874
1875 if (!sloppy) {
1876 if (!this.addError('Missing or invalid tag name')) {
1877 return false;
1878 }
1879 }
1880 }
1881
1882 return true;
1883 };
1884
1885 Rules = {
1886 // http://usejsdoc.org/tags-access.html
1887 'access': ['parseAccess'],
1888 // http://usejsdoc.org/tags-alias.html
1889 'alias': ['parseNamePath', 'ensureEnd'],
1890 // http://usejsdoc.org/tags-augments.html
1891 'augments': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
1892 // http://usejsdoc.org/tags-constructor.html
1893 'constructor': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
1894 // Synonym: http://usejsdoc.org/tags-constructor.html
1895 'class': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
1896 // Synonym: http://usejsdoc.org/tags-extends.html
1897 'extends': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
1898 // http://usejsdoc.org/tags-deprecated.html
1899 'deprecated': ['parseDescription'],
1900 // http://usejsdoc.org/tags-global.html
1901 'global': ['ensureEnd'],
1902 // http://usejsdoc.org/tags-inner.html
1903 'inner': ['ensureEnd'],
1904 // http://usejsdoc.org/tags-instance.html
1905 'instance': ['ensureEnd'],
1906 // http://usejsdoc.org/tags-kind.html
1907 'kind': ['parseKind'],
1908 // http://usejsdoc.org/tags-mixes.html
1909 'mixes': ['parseNamePath', 'ensureEnd'],
1910 // http://usejsdoc.org/tags-mixin.html
1911 'mixin': ['parseNamePathOptional', 'ensureEnd'],
1912 // http://usejsdoc.org/tags-member.html
1913 'member': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
1914 // http://usejsdoc.org/tags-method.html
1915 'method': ['parseNamePathOptional', 'ensureEnd'],
1916 // http://usejsdoc.org/tags-module.html
1917 'module': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
1918 // Synonym: http://usejsdoc.org/tags-method.html
1919 'func': ['parseNamePathOptional', 'ensureEnd'],
1920 // Synonym: http://usejsdoc.org/tags-method.html
1921 'function': ['parseNamePathOptional', 'ensureEnd'],
1922 // Synonym: http://usejsdoc.org/tags-member.html
1923 'var': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
1924 // http://usejsdoc.org/tags-name.html
1925 'name': ['parseNamePath', 'ensureEnd'],
1926 // http://usejsdoc.org/tags-namespace.html
1927 'namespace': ['parseType', 'parseNamePathOptional', 'ensureEnd'],
1928 // http://usejsdoc.org/tags-private.html
1929 'private': ['parseType', 'parseDescription'],
1930 // http://usejsdoc.org/tags-protected.html
1931 'protected': ['parseType', 'parseDescription'],
1932 // http://usejsdoc.org/tags-public.html
1933 'public': ['parseType', 'parseDescription'],
1934 // http://usejsdoc.org/tags-readonly.html
1935 'readonly': ['ensureEnd'],
1936 // http://usejsdoc.org/tags-requires.html
1937 'requires': ['parseNamePath', 'ensureEnd'],
1938 // http://usejsdoc.org/tags-since.html
1939 'since': ['parseDescription'],
1940 // http://usejsdoc.org/tags-static.html
1941 'static': ['ensureEnd'],
1942 // http://usejsdoc.org/tags-summary.html
1943 'summary': ['parseDescription'],
1944 // http://usejsdoc.org/tags-this.html
1945 'this': ['parseNamePath', 'ensureEnd'],
1946 // http://usejsdoc.org/tags-todo.html
1947 'todo': ['parseDescription'],
1948 // http://usejsdoc.org/tags-variation.html
1949 'variation': ['parseVariation'],
1950 // http://usejsdoc.org/tags-version.html
1951 'version': ['parseDescription']
1952 };
1953
1954 TagParser.prototype.parse = function parse() {
1955 var i, iz, sequences, method;
1956
1957 // empty title
1958 if (!this._title) {
1959 if (!this.addError('Missing or invalid title')) {
1960 return null;
1961 }
1962 }
1963
1964 // Seek to content last index.
1965 this._last = seekContent(this._title);
1966
1967 if (hasOwnProperty(Rules, this._title)) {
1968 sequences = Rules[this._title];
1969 } else {
1970 // default sequences
1971 sequences = ['parseType', 'parseName', 'parseDescription', 'epilogue'];
1972 }
1973
1974 for (i = 0, iz = sequences.length; i < iz; ++i) {
1975 method = sequences[i];
1976 if (!this[method]()) {
1977 return null;
1978 }
1979 }
1980
1981 // Seek global index to end of this tag.
1982 index = this._last;
1983 return this._tag;
1984 };
1985
1986 function parseTag(options) {
1987 var title, parser;
1988
1989 // skip to tag
1990 if (!skipToTag()) {
1991 return null;
1992 }
1993
1994 // scan title
1995 title = scanTitle();
1996
1997 // construct tag parser
1998 parser = new TagParser(options, title);
1999 return parser.parse();
2000 }
2001
2002 //
2003 // Parse JSDoc
2004 //
2005
2006 function scanJSDocDescription() {
2007 var description = '', ch, atAllowed;
2008
2009 atAllowed = true;
2010 while (index < length) {
2011 ch = source[index];
2012
2013 if (atAllowed && ch === '@') {
2014 break;
2015 }
2016
2017 if (isLineTerminator(ch)) {
2018 atAllowed = true;
2019 } else if (atAllowed && !isWhiteSpace(ch)) {
2020 atAllowed = false;
2021 }
2022
2023 description += advance();
2024 }
2025 return trim(description);
2026 }
2027
2028 function parse(comment, options) {
2029 var tags = [], tag, description, interestingTags, i, iz;
2030
2031 if (options === undefined) {
2032 options = {};
2033 }
2034
2035 if (typeof options.unwrap === 'boolean' && options.unwrap) {
2036 source = unwrapComment(comment);
2037 } else {
2038 source = comment;
2039 }
2040
2041 // array of relevant tags
2042 if (options.tags) {
2043 if (isArray(options.tags)) {
2044 interestingTags = { };
2045 for (i = 0, iz = options.tags.length; i < iz; i++) {
2046 if (typeof options.tags[i] === 'string') {
2047 interestingTags[options.tags[i]] = true;
2048 } else {
2049 throwError('Invalid "tags" parameter: ' + options.tags);
2050 }
2051 }
2052 } else {
2053 throwError('Invalid "tags" parameter: ' + options.tags);
2054 }
2055 }
2056
2057 if (!CanAccessStringByIndex) {
2058 source = source.split('');
2059 }
2060
2061 length = source.length;
2062 index = 0;
2063 lineNumber = 0;
2064 recoverable = options.recoverable;
2065 sloppy = options.sloppy;
2066 strict = options.strict;
2067
2068 description = scanJSDocDescription();
2069
2070 while (true) {
2071 tag = parseTag(options);
2072 if (!tag) {
2073 break;
2074 }
2075 if (!interestingTags || interestingTags.hasOwnProperty(tag.title)) {
2076 tags.push(tag);
2077 }
2078 }
2079
2080 return {
2081 description: description,
2082 tags: tags
2083 };
2084 }
2085 exports.parse = parse;
2086 }(jsdoc = {}));
2087
2088 exports.version = VERSION;
2089 exports.parse = jsdoc.parse;
2090 exports.parseType = typed.parseType;
2091 exports.parseParamType = typed.parseParamType;
2092 exports.unwrapComment = unwrapComment;
2093 exports.Syntax = shallowCopy(typed.Syntax);
2094 exports.Error = DoctrineError;
2095 exports.type = {
2096 Syntax: exports.Syntax,
2097 parseType: typed.parseType,
2098 parseParamType: typed.parseParamType,
2099 stringify: typed.stringify
2100 };
2101}(typeof exports === 'undefined' ? (doctrine = {}) : exports));
2102/* vim: set sw=4 ts=4 et tw=80 : */