@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
182 lines • 8.35 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.splitComments = splitComments;
exports.normalizeExpressions = normalizeExpressions;
exports.parseNodesWithUnknownType = parseNodesWithUnknownType;
const normalize_single_node_1 = require("./normalize-single-node");
const parser_1 = require("../../../json/parser");
const normalize_meta_1 = require("../../normalize-meta");
const log_1 = require("../../../../../../../util/log");
const assert_1 = require("../../../../../../../util/assert");
const json_1 = require("../../../../../../../util/json");
const normalize_unary_1 = require("../operators/normalize-unary");
const normalize_repeat_1 = require("../loops/normalize-repeat");
const normalize_binary_1 = require("../operators/normalize-binary");
const normalize_for_1 = require("../loops/normalize-for");
const normalize_symbol_1 = require("../values/normalize-symbol");
const normalize_if_then_1 = require("../control/normalize-if-then");
const normalize_while_1 = require("../loops/normalize-while");
const normalize_if_then_else_1 = require("../control/normalize-if-then-else");
const type_1 = require("../../../../model/type");
const normalize_comment_1 = require("../other/normalize-comment");
function normalizeMappedWithoutSemicolonBasedOnType(mappedWithName, data) {
let result = undefined;
switch (mappedWithName?.length) {
case 1:
result = (0, normalize_single_node_1.normalizeSingleNode)(data, mappedWithName[0]);
break;
case 2:
result = (0, normalize_unary_1.tryNormalizeUnary)(data, mappedWithName)
?? (0, normalize_repeat_1.tryNormalizeRepeat)(data, mappedWithName);
break;
case 3:
result = (0, normalize_binary_1.tryNormalizeBinary)(data, mappedWithName)
?? (0, normalize_for_1.tryNormalizeFor)(data, mappedWithName)
?? (0, normalize_symbol_1.tryNormalizeSymbol)(data, mappedWithName);
break;
case 5:
result = (0, normalize_if_then_1.tryNormalizeIfThen)(data, mappedWithName)
?? (0, normalize_while_1.tryNormalizeWhile)(data, mappedWithName);
break;
case 7:
result = (0, normalize_if_then_else_1.tryNormalizeIfThenElse)(data, mappedWithName);
break;
}
// otherwise perform default parsing
return result !== undefined ? [result] : parseNodesWithUnknownType(data, mappedWithName);
}
function splitComments(tokens) {
const comments = [];
const others = [];
for (const elem of tokens) {
if (elem.name === type_1.RawRType.Comment) {
comments.push(elem);
}
else {
others.push(elem);
}
}
return { comments, others };
}
function splitExprs(tokens) {
let last = 0, i = 0;
let lastExpr = false;
const segments = [];
for (const token of tokens) {
if (token.name === type_1.RawRType.Semicolon) {
segments.push(tokens.slice(last, i));
lastExpr = false;
last = i + 1;
}
else {
const thisExpr = token.name === type_1.RawRType.Expression || token.name === type_1.RawRType.ExprOfAssignOrHelp || token.name === type_1.RawRType.LegacyEqualAssign;
if (thisExpr && lastExpr) {
if (i > last) {
segments.push(tokens.slice(last, i));
}
segments.push([tokens[i]]);
last = i + 1;
}
lastExpr = thisExpr;
}
i++;
}
if (last < tokens.length) {
segments.push(tokens.slice(last, tokens.length));
}
return segments;
}
/**
* Handles semicolons within _and_ braces at the start and end of the expression
* @param raw - The tokens to split
*/
function handleExpressionList(raw) {
if (raw.length === 0) {
return { segments: [], comments: [], braces: undefined };
}
const { comments, others: tokens } = splitComments(raw);
const first = tokens[0]?.name;
if (first === type_1.RawRType.BraceLeft) {
const endType = tokens[tokens.length - 1].name;
(0, assert_1.guard)(endType === type_1.RawRType.BraceRight, () => `expected a brace at the end of the expression list as well, but ${endType} :: ${JSON.stringify(tokens[tokens.length - 1], json_1.jsonReplacer)}`);
return {
segments: [tokens.slice(1, tokens.length - 1)],
comments,
braces: [tokens[0], tokens[tokens.length - 1]]
};
}
else if (first === type_1.RawRType.ParenLeft) {
const endType = tokens[tokens.length - 1].name;
(0, assert_1.guard)(endType === type_1.RawRType.ParenRight, () => `expected a parenthesis at the end of the expression list as well, but ${endType} :: ${JSON.stringify(tokens[tokens.length - 1], json_1.jsonReplacer)}`);
return {
segments: [tokens.slice(1, tokens.length - 1)],
comments,
braces: [tokens[0], tokens[tokens.length - 1]]
};
}
else {
return { segments: splitExprs(tokens), comments, braces: undefined };
}
}
function processBraces([start, end], processed, comments, data) {
const [newStart, newEnd] = [(0, normalize_symbol_1.tryNormalizeSymbol)(data, [start]), (0, normalize_symbol_1.tryNormalizeSymbol)(data, [end])];
(0, assert_1.guard)(newStart !== undefined && newEnd !== undefined, () => `expected both start and end to be symbols, but ${JSON.stringify(start, json_1.jsonReplacer)} :: ${JSON.stringify(end, json_1.jsonReplacer)}`);
return {
type: type_1.RType.ExpressionList,
children: processed,
grouping: [newStart, newEnd],
lexeme: undefined,
location: undefined,
info: {
additionalTokens: comments,
}
};
}
function normalizeExpressions(data, tokens) {
if (tokens.length === 0) {
parser_1.parseLog.warn('no children received, skipping');
return [];
}
let mappedWithName = tokens[0].name ? tokens : (0, normalize_meta_1.getWithTokenType)(tokens);
(0, log_1.expensiveTrace)(log_1.log, () => `[parseBasedOnType] names: [${mappedWithName.map(({ name }) => name).join(', ')}]`);
let parsedComments = [];
if (mappedWithName.length > 1) {
// iterate over types, find all semicolons, and segment the tokens based on them.
// we could potentially optimize as not all expr may have semicolons but not for now
const { segments, braces, comments } = handleExpressionList(mappedWithName);
parsedComments = comments.map(c => (0, normalize_comment_1.normalizeComment)(data, c.content));
if (segments.length > 1 || braces) {
const processed = segments.flatMap(s => normalizeExpressions(data, s));
(0, assert_1.guard)(!processed.some(x => x.type === type_1.RType.Delimiter), () => `expected no delimiter tokens in ${JSON.stringify(processed)}`);
if (braces) {
return [processBraces(braces, processed, parsedComments, data)];
}
else if (processed.length > 0) {
if (parsedComments) {
processed[0].info.additionalTokens ??= [];
processed[0].info.additionalTokens.push(...parsedComments);
}
return processed;
}
else {
return parsedComments;
}
}
/*
* if splitOnSemicolon.length === 1, we can continue with the normal parsing, but we may have had a trailing semicolon, with this, it is removed as well.
* splitOnSemicolon.length === 0 is not possible, as we would have had an empty array before, split does not add elements.
*/
mappedWithName = segments[0];
}
return [...parsedComments, ...normalizeMappedWithoutSemicolonBasedOnType(mappedWithName, data)];
}
function parseNodesWithUnknownType(data, mappedWithName) {
const parsedNodes = [];
// used to indicate the new root node of this set of nodes
for (const elem of mappedWithName ?? []) {
const retrieved = (0, normalize_single_node_1.normalizeSingleNode)(data, elem);
parsedNodes.push(retrieved);
}
return parsedNodes;
}
//# sourceMappingURL=normalize-expressions.js.map