| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898 | /* * @fileoverview Main Doctrine object * @author Yusuke Suzuki <utatane.tea@gmail.com> * @author Dan Tao <daniel.tao@gmail.com> * @author Andrew Eisenberg <andrew@eisenberg.as> */(function () {    'use strict';    var typed,        utility,        jsdoc,        esutils,        hasOwnProperty;    esutils = require('esutils');    typed = require('./typed');    utility = require('./utility');    function sliceSource(source, index, last) {        return source.slice(index, last);    }    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 isASCIIAlphanumeric(ch) {        return (ch >= 0x61  /* 'a' */ && ch <= 0x7A  /* 'z' */) ||            (ch >= 0x41  /* 'A' */ && ch <= 0x5A  /* 'Z' */) ||            (ch >= 0x30  /* '0' */ && ch <= 0x39  /* '9' */);    }    function isParamTitle(title) {        return title === 'param' || title === 'argument' || title === 'arg';    }    function isReturnTitle(title) {        return title === 'return' || title === 'returns';    }    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 isAllowedOptional(title) {        return isProperty(title) || isParamTitle(title);    }    function isTypeParameterRequired(title) {        return isParamTitle(title) || isReturnTitle(title) ||            title === 'define' || title === 'enum' ||            title === 'implements' || title === 'this' ||            title === 'type' || title === 'typedef' || 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';    }    // A regex character class that contains all whitespace except linebreak characters (\r, \n, \u2028, \u2029)    var WHITESPACE = '[ \\f\\t\\v\\u00a0\\u1680\\u180e\\u2000-\\u200a\\u202f\\u205f\\u3000\\ufeff]';    var STAR_MATCHER = '(' + WHITESPACE + '*(?:\\*' + WHITESPACE + '?)?)(.+|[\r\n\u2028\u2029])';    function unwrapComment(doc) {        // JSDoc comment is following form        //   /**        //    * .......        //    */        return doc.            // remove /**            replace(/^\/\*\*?/, '').            // remove */            replace(/\*\/$/, '').            // remove ' * ' at the beginning of a line            replace(new RegExp(STAR_MATCHER, 'g'), '$2').            // remove trailing whitespace            replace(/\s*$/, '');    }    /**     * Converts an index in an "unwrapped" JSDoc comment to the corresponding index in the original "wrapped" version     * @param {string} originalSource The original wrapped comment     * @param {number} unwrappedIndex The index of a character in the unwrapped string     * @returns {number} The index of the corresponding character in the original wrapped string     */    function convertUnwrappedCommentIndex(originalSource, unwrappedIndex) {        var replacedSource = originalSource.replace(/^\/\*\*?/, '');        var numSkippedChars = 0;        var matcher = new RegExp(STAR_MATCHER, 'g');        var match;        while ((match = matcher.exec(replacedSource))) {            numSkippedChars += match[1].length;            if (match.index + match[0].length > unwrappedIndex + numSkippedChars) {                return unwrappedIndex + numSkippedChars + originalSource.length - replacedSource.length;            }        }        return originalSource.replace(/\*\/$/, '').replace(/\s*$/, '').length;    }    // JSDoc Tag Parser    (function (exports) {        var Rules,            index,            lineNumber,            length,            source,            originalSource,            recoverable,            sloppy,            strict;        function advance() {            var ch = source.charCodeAt(index);            index += 1;            if (esutils.code.isLineTerminator(ch) && !(ch === 0x0D  /* '\r' */ && source.charCodeAt(index) === 0x0A  /* '\n' */)) {                lineNumber += 1;            }            return String.fromCharCode(ch);        }        function scanTitle() {            var title = '';            // waste '@'            advance();            while (index < length && isASCIIAlphanumeric(source.charCodeAt(index))) {                title += advance();            }            return title;        }        function seekContent() {            var ch, waiting, last = index;            waiting = false;            while (last < length) {                ch = source.charCodeAt(last);                if (esutils.code.isLineTerminator(ch) && !(ch === 0x0D  /* '\r' */ && source.charCodeAt(last + 1) === 0x0A  /* '\n' */)) {                    waiting = true;                } else if (waiting) {                    if (ch === 0x40  /* '@' */) {                        break;                    }                    if (!esutils.code.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, addRange) {            var ch, brace, type, startIndex, direct = false;            // search '{'            while (index < last) {                ch = source.charCodeAt(index);                if (esutils.code.isWhiteSpace(ch)) {                    advance();                } else if (ch === 0x7B  /* '{' */) {                    advance();                    break;                } else {                    // this is direct pattern                    direct = true;                    break;                }            }            if (direct) {                return null;            }            // type expression { is found            brace = 1;            type = '';            while (index < last) {                ch = source.charCodeAt(index);                if (esutils.code.isLineTerminator(ch)) {                    advance();                } else {                    if (ch === 0x7D  /* '}' */) {                        brace -= 1;                        if (brace === 0) {                            advance();                            break;                        }                    } else if (ch === 0x7B  /* '{' */) {                        brace += 1;                    }                    if (type === '') {                        startIndex = index;                    }                    type += advance();                }            }            if (brace !== 0) {                // braces is not balanced                return utility.throwError('Braces are not balanced');            }            if (isAllowedOptional(title)) {                return typed.parseParamType(type, {startIndex: convertIndex(startIndex), range: addRange});            }            return typed.parseType(type, {startIndex: convertIndex(startIndex), range: addRange});        }        function scanIdentifier(last) {            var identifier;            if (!esutils.code.isIdentifierStartES5(source.charCodeAt(index)) && !source[index].match(/[0-9]/)) {                return null;            }            identifier = advance();            while (index < last && esutils.code.isIdentifierPartES5(source.charCodeAt(index))) {                identifier += advance();            }            return identifier;        }        function skipWhiteSpace(last) {            while (index < last && (esutils.code.isWhiteSpace(source.charCodeAt(index)) || esutils.code.isLineTerminator(source.charCodeAt(index)))) {                advance();            }        }        function parseName(last, allowBrackets, allowNestedParams) {            var name = '',                useBrackets,                insideString;            skipWhiteSpace(last);            if (index >= last) {                return null;            }            if (source.charCodeAt(index) === 0x5B  /* '[' */) {                if (allowBrackets) {                    useBrackets = true;                    name = advance();                } else {                    return null;                }            }            name += scanIdentifier(last);            if (allowNestedParams) {                if (source.charCodeAt(index) === 0x3A /* ':' */ && (                        name === 'module' ||                        name === 'external' ||                        name === 'event')) {                    name += advance();                    name += scanIdentifier(last);                }                if(source.charCodeAt(index) === 0x5B  /* '[' */ && source.charCodeAt(index + 1) === 0x5D  /* ']' */){                    name += advance();                    name += advance();                }                while (source.charCodeAt(index) === 0x2E  /* '.' */ ||                        source.charCodeAt(index) === 0x2F  /* '/' */ ||                        source.charCodeAt(index) === 0x23  /* '#' */ ||                        source.charCodeAt(index) === 0x2D  /* '-' */ ||                        source.charCodeAt(index) === 0x7E  /* '~' */) {                    name += advance();                    name += scanIdentifier(last);                }            }            if (useBrackets) {                skipWhiteSpace(last);                // do we have a default value for this?                if (source.charCodeAt(index) === 0x3D  /* '=' */) {                    // consume the '='' symbol                    name += advance();                    skipWhiteSpace(last);                    var ch;                    var bracketDepth = 1;                    // scan in the default value                    while (index < last) {                        ch = source.charCodeAt(index);                        if (esutils.code.isWhiteSpace(ch)) {                            if (!insideString) {                                skipWhiteSpace(last);                                ch = source.charCodeAt(index);                            }                        }                        if (ch === 0x27 /* ''' */) {                            if (!insideString) {                                insideString = '\'';                            } else {                                if (insideString === '\'') {                                    insideString = '';                                }                            }                        }                        if (ch === 0x22 /* '"' */) {                            if (!insideString) {                                insideString = '"';                            } else {                                if (insideString === '"') {                                    insideString = '';                                }                            }                        }                        if (ch === 0x5B /* '[' */) {                            bracketDepth++;                        } else if (ch === 0x5D  /* ']' */ &&                            --bracketDepth === 0) {                            break;                        }                        name += advance();                    }                }                skipWhiteSpace(last);                if (index >= last || source.charCodeAt(index) !== 0x5D  /* ']' */) {                    // we never found a closing ']'                    return null;                }                // collect the last ']'                name += advance();            }            return name;        }        function skipToTag() {            while (index < length && source.charCodeAt(index) !== 0x40  /* '@' */) {                advance();            }            if (index >= length) {                return false;            }            utility.assert(source.charCodeAt(index) === 0x40  /* '@' */);            return true;        }        function convertIndex(rangeIndex) {            if (source === originalSource) {                return rangeIndex;            }            return convertUnwrappedCommentIndex(originalSource, rangeIndex);        }        function TagParser(options, title) {            this._options = options;            this._title = title.toLowerCase();            this._tag = {                title: title,                description: null            };            if (this._options.lineNumbers) {                this._tag.lineNumber = lineNumber;            }            this._first = index - title.length - 1;            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) {                        utility.assert(index < args.length, 'Message reference must be in range');                        return args[index];                    }                );            if (!this._tag.errors) {                this._tag.errors = [];            }            if (strict) {                utility.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, this._options.range);                    if (!this._tag.type) {                        if (!isParamTitle(this._title) && !isReturnTitle(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, this._options.range);                } 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 && isAllowedOptional(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 && isAllowedOptional(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.length > 1) {                            this._tag['default'] = assign.slice(1).join('=');                        }                        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 = sliceSource(source, index, this._last).trim();            if (description) {                if ((/^-\s+/).test(description)) {                    description = description.substring(2);                }                this._tag.description = description;            }            return true;        };        TagParser.prototype.parseCaption = function parseDescription() {            var description = sliceSource(source, index, this._last).trim();            var captionStartTag = '<caption>';            var captionEndTag = '</caption>';            var captionStart = description.indexOf(captionStartTag);            var captionEnd = description.indexOf(captionEndTag);            if (captionStart >= 0 && captionEnd >= 0) {                this._tag.caption = description.substring(                    captionStart + captionStartTag.length, captionEnd).trim();                this._tag.description = description.substring(captionEnd + captionEndTag.length).trim();            } else {                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 = sliceSource(source, index, this._last).trim();            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 = sliceSource(source, index, this._last).trim();            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.parseThis = function parseThis() {            // this name may be a name expression (e.g. {foo.bar}),            // an union (e.g. {foo.bar|foo.baz}) or a name path (e.g. foo.bar)            var value = sliceSource(source, index, this._last).trim();            if (value && value.charAt(0) === '{') {                var gotType = this.parseType();                if (gotType && this._tag.type.type === 'NameExpression' || this._tag.type.type === 'UnionType') {                    this._tag.name = this._tag.type.name;                    return true;                } else {                    return this.addError('Invalid name for this');                }            } else {                return this.parseNamePath();            }        };        TagParser.prototype.parseVariation = function parseVariation() {            var variation, text;            text = sliceSource(source, index, this._last).trim();            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 = sliceSource(source, index, this._last).trim();            if (shouldBeEmpty) {                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 (isAllowedOptional(this._title) && !this._tag.type && description && description.charAt(0) === '[') {                this._tag.type = this._extra.name;                if (!this._tag.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-example.html            'example': ['parseCaption'],            // 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': ['parseThis', 'ensureEnd'],            // http://usejsdoc.org/tags-todo.html            'todo': ['parseDescription'],            // http://usejsdoc.org/tags-typedef.html            'typedef': ['parseType', 'parseNamePathOptional'],            // 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;            // empty title            if (!this._title) {                if (!this.addError('Missing or invalid title')) {                    return null;                }            }            // Seek to content last index.            this._last = seekContent(this._title);            if (this._options.range) {                this._tag.range = [this._first, source.slice(0, this._last).replace(/\s*$/, '').length].map(convertIndex);            }            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;                }            }            return this._tag;        };        function parseTag(options) {            var title, parser, tag;            // skip to tag            if (!skipToTag()) {                return null;            }            // scan title            title = scanTitle();            // construct tag parser            parser = new TagParser(options, title);            tag = parser.parse();            // Seek global index to end of this tag.            while (index < parser._last) {                advance();            }            return tag;        }        //        // Parse JSDoc        //        function scanJSDocDescription(preserveWhitespace) {            var description = '', ch, atAllowed;            atAllowed = true;            while (index < length) {                ch = source.charCodeAt(index);                if (atAllowed && ch === 0x40  /* '@' */) {                    break;                }                if (esutils.code.isLineTerminator(ch)) {                    atAllowed = true;                } else if (atAllowed && !esutils.code.isWhiteSpace(ch)) {                    atAllowed = false;                }                description += advance();            }            return preserveWhitespace ? description : description.trim();        }        function parse(comment, options) {            var tags = [], tag, description, interestingTags, i, iz;            if (options === undefined) {                options = {};            }            if (typeof options.unwrap === 'boolean' && options.unwrap) {                source = unwrapComment(comment);            } else {                source = comment;            }            originalSource = comment;            // array of relevant tags            if (options.tags) {                if (Array.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 {                            utility.throwError('Invalid "tags" parameter: ' + options.tags);                        }                    }                } else {                    utility.throwError('Invalid "tags" parameter: ' + options.tags);                }            }            length = source.length;            index = 0;            lineNumber = 0;            recoverable = options.recoverable;            sloppy = options.sloppy;            strict = options.strict;            description = scanJSDocDescription(options.preserveWhitespace);            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 = utility.VERSION;    exports.parse = jsdoc.parse;    exports.parseType = typed.parseType;    exports.parseParamType = typed.parseParamType;    exports.unwrapComment = unwrapComment;    exports.Syntax = shallowCopy(typed.Syntax);    exports.Error = utility.DoctrineError;    exports.type = {        Syntax: exports.Syntax,        parseType: typed.parseType,        parseParamType: typed.parseParamType,        stringify: typed.stringify    };}());/* vim: set sw=4 ts=4 et tw=80 : */
 |