UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

182 lines 8.35 kB
"use strict"; 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