@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
220 lines • 9.38 kB
JavaScript
;
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