UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

220 lines 9.38 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.firstAndRest = void 0; exports.parseRoxygenCommentsOfNode = parseRoxygenCommentsOfNode; exports.parseRoxygenComment = parseRoxygenComment; const roxygen_ast_1 = require("./roxygen-ast"); const r_comment_1 = require("../lang-4.x/ast/model/nodes/r-comment"); const assert_1 = require("../../util/assert"); const args_1 = require("../../util/text/args"); const range_1 = require("../../util/range"); function prepareCommentContext(commentText) { const contents = []; for (const line of commentText) { const trimmed = line.trim(); if (trimmed.length === 0) { continue; } else if (!trimmed.startsWith('#')) { // we are done with the roxygen comment break; } contents.push((trimmed.startsWith('#\'') ? trimmed.slice(2) : trimmed.slice(1)).trimEnd()); } return contents; } /** * Parses the roxygen comments attached to a node into a RoxygenBlock AST node. * Will return `undefined` if there are no valid roxygen comments attached to the node. * Please note that this does *not* do any clever mapping of parameters or requests. * For a higher-level function that also traverses up the AST to find comments attached to parent nodes, see {@link getDocumentationOf}. * @param node - The node to parse the roxygen comments for * @param idMap - An optional id map to traverse up the AST to find comments attached to parent nodes */ function parseRoxygenCommentsOfNode(node, idMap) { let comments; let cur = node; do { comments = cur?.info.adToks ?.filter(r_comment_1.RComment.is).filter(r => (0, assert_1.isNotUndefined)(r.lexeme)); cur = cur?.info.parent ? idMap?.get(cur.info.parent) : undefined; } while ((comments === undefined || comments.length === 0) && cur !== undefined); if (comments === undefined || comments.length === 0) { return undefined; } const commentContent = comments.map(c => c.lexeme); return { type: 'roxygen-block', tags: parseRoxygenComment(commentContent), attachedTo: cur?.info.id, requestNode: node.info.id, range: [ ...range_1.SourceRange.merge(comments.map(c => c.location)), comments.find(c => c.info.file)?.info.file ] }; } /** * Parses a roxygen comment into a RoxygenBlock AST node. * Will return an empty array if the comment has no valid roxygen comment. * @see {@link parseRoxygenCommentsOfNode} - to parse comments attached to a node */ function parseRoxygenComment(commentText) { const contents = prepareCommentContext(commentText); const state = { lines: contents, tags: [], idx: 0 }; let tag = val(state, [roxygen_ast_1.KnownRoxygenTags.Text]); while (tag) { tag = parseRoxygenTag(state, tag); } return state.tags; } function atEnd(state) { return state.idx >= state.lines.length; } function atTag(state) { if (atEnd(state)) { return undefined; } const line = state.lines[state.idx].trim(); if (!line.startsWith('@')) { return undefined; } const firstSpace = line.indexOf(' '); return firstSpace === -1 ? [line.slice(1), undefined] : [line.slice(1, firstSpace), line.slice(firstSpace + 1).trim()]; } function advanceLine(state) { return state.lines[state.idx++]; } function collectUntilNextTag(state) { const collected = []; while (!atEnd(state)) { const mayTag = atTag(state); if (mayTag) { advanceLine(state); return [collected, mayTag]; } collected.push(advanceLine(state)); } return [collected, undefined]; } function addTag(state, tag) { state.tags.push(tag); } function val(state, tagName, lineToVal = l => l.join('\n').trim()) { const [lines, nextTag] = collectUntilNextTag(state); if (tagName[1]) { lines.unshift(tagName[1]); } if (tagName[0] !== roxygen_ast_1.KnownRoxygenTags.Text || lines.length > 0) { const n = tagName[0]; if ((0, roxygen_ast_1.isKnownRoxygenText)(n)) { const val = lineToVal(lines); addTag(state, (val !== undefined ? { type: n, value: val } : { type: n })); } else { addTag(state, { type: roxygen_ast_1.KnownRoxygenTags.Unknown, value: { tag: n, content: lines.join(' ') } }); } } return nextTag; } const spaceVals = (s, t) => val(s, t, l => (0, args_1.splitAtEscapeSensitive)(l.join(' '))); const flagVal = (s, t) => val(s, t, () => undefined); const section = (s, t) => val(s, t, l => { return { title: l[0].trim(), content: l.slice(1).join('\n').trim() }; }); const firstAndRest = (firstName, secondName) => (s, t) => val(s, t, l => { const vals = (0, args_1.splitAtEscapeSensitive)(l.join('\n')); return { [firstName]: vals[0], [secondName]: vals.slice(1).join(' ').trim() }; }); exports.firstAndRest = firstAndRest; const firstAndArrayRest = (firstName, secondName) => (s, t) => val(s, t, l => { const vals = (0, args_1.splitAtEscapeSensitive)(l.join('\n')); return { [firstName]: vals[0], [secondName]: vals.slice(1) }; }); const asNumber = (s, t) => val(s, t, l => { const num = Number(l.join(' ').trim()); return Number.isNaN(num) ? undefined : num; }); const TagMap = { [roxygen_ast_1.KnownRoxygenTags.Aliases]: spaceVals, [roxygen_ast_1.KnownRoxygenTags.Backref]: val, [roxygen_ast_1.KnownRoxygenTags.Concept]: val, [roxygen_ast_1.KnownRoxygenTags.Family]: val, [roxygen_ast_1.KnownRoxygenTags.Keywords]: spaceVals, [roxygen_ast_1.KnownRoxygenTags.References]: val, [roxygen_ast_1.KnownRoxygenTags.SeeAlso]: spaceVals, [roxygen_ast_1.KnownRoxygenTags.EvalNamespace]: val, [roxygen_ast_1.KnownRoxygenTags.Export]: flagVal, [roxygen_ast_1.KnownRoxygenTags.ExportClass]: val, [roxygen_ast_1.KnownRoxygenTags.ExportMethod]: val, [roxygen_ast_1.KnownRoxygenTags.ExportPattern]: val, [roxygen_ast_1.KnownRoxygenTags.ExportS3Method]: val, [roxygen_ast_1.KnownRoxygenTags.Import]: val, [roxygen_ast_1.KnownRoxygenTags.ImportClassesFrom]: firstAndArrayRest('package', 'classes'), [roxygen_ast_1.KnownRoxygenTags.ImportMethodsFrom]: firstAndArrayRest('package', 'methods'), [roxygen_ast_1.KnownRoxygenTags.ImportFrom]: firstAndArrayRest('package', 'symbols'), [roxygen_ast_1.KnownRoxygenTags.RawNamespace]: val, [roxygen_ast_1.KnownRoxygenTags.UseDynLib]: val, [roxygen_ast_1.KnownRoxygenTags.Md]: flagVal, [roxygen_ast_1.KnownRoxygenTags.NoMd]: flagVal, [roxygen_ast_1.KnownRoxygenTags.Section]: section, [roxygen_ast_1.KnownRoxygenTags.Field]: (0, exports.firstAndRest)('name', 'description'), [roxygen_ast_1.KnownRoxygenTags.Format]: val, [roxygen_ast_1.KnownRoxygenTags.Method]: (0, exports.firstAndRest)('generic', 'class'), [roxygen_ast_1.KnownRoxygenTags.Slot]: (0, exports.firstAndRest)('name', 'description'), [roxygen_ast_1.KnownRoxygenTags.Source]: val, [roxygen_ast_1.KnownRoxygenTags.Description]: val, [roxygen_ast_1.KnownRoxygenTags.Details]: val, [roxygen_ast_1.KnownRoxygenTags.Example]: val, [roxygen_ast_1.KnownRoxygenTags.Examples]: val, [roxygen_ast_1.KnownRoxygenTags.ExamplesIf]: (0, exports.firstAndRest)('condition', 'content'), [roxygen_ast_1.KnownRoxygenTags.NoRd]: flagVal, [roxygen_ast_1.KnownRoxygenTags.Param]: (0, exports.firstAndRest)('name', 'description'), [roxygen_ast_1.KnownRoxygenTags.RawRd]: val, [roxygen_ast_1.KnownRoxygenTags.Return]: val, [roxygen_ast_1.KnownRoxygenTags.Returns]: val, [roxygen_ast_1.KnownRoxygenTags.Title]: val, [roxygen_ast_1.KnownRoxygenTags.Usage]: val, [roxygen_ast_1.KnownRoxygenTags.DescribeIn]: (0, exports.firstAndRest)('dest', 'description'), [roxygen_ast_1.KnownRoxygenTags.Eval]: val, [roxygen_ast_1.KnownRoxygenTags.EvalRd]: val, [roxygen_ast_1.KnownRoxygenTags.IncludeRmd]: val, [roxygen_ast_1.KnownRoxygenTags.Inherit]: firstAndArrayRest('source', 'components'), [roxygen_ast_1.KnownRoxygenTags.InheritDotParams]: firstAndArrayRest('source', 'args'), [roxygen_ast_1.KnownRoxygenTags.InheritParams]: val, [roxygen_ast_1.KnownRoxygenTags.InheritSection]: (0, exports.firstAndRest)('source', 'section'), [roxygen_ast_1.KnownRoxygenTags.Order]: asNumber, [roxygen_ast_1.KnownRoxygenTags.RdName]: val, [roxygen_ast_1.KnownRoxygenTags.Template]: val, [roxygen_ast_1.KnownRoxygenTags.TemplateVar]: (0, exports.firstAndRest)('name', 'value'), [roxygen_ast_1.KnownRoxygenTags.Text]: val, [roxygen_ast_1.KnownRoxygenTags.Name]: val, [roxygen_ast_1.KnownRoxygenTags.DocType]: val, [roxygen_ast_1.KnownRoxygenTags.Author]: val, [roxygen_ast_1.KnownRoxygenTags.Unknown]: (s, t) => val(s, t, l => ({ tag: t[0], content: l.join(' ') })) }; /** returns the next tag */ function parseRoxygenTag(state, tagName) { if (tagName === undefined) { return undefined; } const parser = TagMap[tagName[0]] ?? val; return parser(state, tagName); } //# sourceMappingURL=roxygen-parse.js.map