UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

141 lines 5.96 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.fileProtocol = void 0; exports.requestFromInput = requestFromInput; exports.requestProviderFromFile = requestProviderFromFile; exports.requestProviderFromText = requestProviderFromText; exports.isEmptyRequest = isEmptyRequest; exports.retrieveParseDataFromRCode = retrieveParseDataFromRCode; exports.retrieveNormalizedAstFromRCode = retrieveNormalizedAstFromRCode; exports.removeRQuotes = removeRQuotes; exports.retrieveNumberOfRTokensOfLastParse = retrieveNumberOfRTokensOfLastParse; const strings_1 = require("../util/text/strings"); const assert_1 = require("../util/assert"); const shell_executor_1 = require("./shell-executor"); const parser_1 = require("./lang-4.x/ast/parser/json/parser"); const init_1 = require("./init"); const convert_values_1 = require("./lang-4.x/convert-values"); const decorate_1 = require("./lang-4.x/ast/model/processing/decorate"); const type_1 = require("./lang-4.x/ast/model/type"); const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); exports.fileProtocol = 'file://'; /** * Creates a {@link RParseRequests} from a given input. * If your input starts with {@link fileProtocol}, it is assumed to be a file path and will be processed as such. * Giving an array, you can mix file paths and text content (again using the {@link fileProtocol}). * */ function requestFromInput(input) { if (Array.isArray(input)) { return input.flatMap(requestFromInput); } const content = input; const file = content.startsWith(exports.fileProtocol); return { request: file ? 'file' : 'text', content: file ? content.slice(7) : content }; } function requestProviderFromFile() { return { exists(p, ignoreCase) { try { if (!ignoreCase) { return fs_1.default.existsSync(p) ? p : undefined; } // walk the directory and find the first match const dir = path_1.default.dirname(p); const file = path_1.default.basename(p); const files = fs_1.default.readdirSync(dir); const found = files.find(f => f.toLowerCase() === file.toLowerCase()); return found ? path_1.default.join(dir, found) : undefined; } catch { return undefined; } }, createRequest(path) { return { request: 'file', content: path, }; } }; } function requestProviderFromText(text) { return { exists(path, ignoreCase) { if (ignoreCase) { return Object.keys(text).find(p => p.toLowerCase() === path.toLowerCase()); } return text[path] !== undefined ? path : undefined; }, createRequest(path) { return { request: 'text', content: text[path] ?? '' }; } }; } function isEmptyRequest(request) { return request.content.trim().length === 0; } /** * Provides the capability to parse R files/R code using the R parser. * Depends on {@link RShell} to provide a connection to R. * <p> * Throws if the file could not be parsed. * If successful, allows further querying the last result with {@link retrieveNumberOfRTokensOfLastParse}. */ function retrieveParseDataFromRCode(request, shell) { if (isEmptyRequest(request)) { return Promise.resolve(''); } const suffix = request.request === 'file' ? ', encoding="utf-8"' : ''; /* call the function with the request */ const command = `flowr_get_ast(${request.request}=${JSON.stringify(request.content)}${suffix})`; if (shell instanceof shell_executor_1.RShellExecutor) { return guardRetrievedOutput(shell.run(command), request); } else { return shell.sendCommandWithOutput(command).then(result => guardRetrievedOutput(result.join(shell.options.eol), request)); } } /** * Uses {@link retrieveParseDataFromRCode} and returns the nicely formatted object-AST. * If successful, allows further querying the last result with {@link retrieveNumberOfRTokensOfLastParse}. */ async function retrieveNormalizedAstFromRCode(request, shell) { const data = await retrieveParseDataFromRCode(request, shell); return (0, parser_1.normalize)({ parsed: data }, (0, decorate_1.deterministicCountingIdGenerator)(0), request.request === 'file' ? request.content : undefined); } /** * If the string has (R-)quotes around it, they will be removed; otherwise the string is returned unchanged. */ function removeRQuotes(str) { if (str.length > 1 && ((0, strings_1.startAndEndsWith)(str, '\'') || (0, strings_1.startAndEndsWith)(str, '"'))) { return str.slice(1, -1); } else { return str; } } /** * Needs to be called *after* {@link retrieveParseDataFromRCode} (or {@link retrieveNormalizedAstFromRCode}) */ async function retrieveNumberOfRTokensOfLastParse(shell, ignoreComments = false) { const rows = ignoreComments ? `flowr_output[flowr_output$token != "${type_1.RawRType.Comment}", ]` : 'flowr_output'; const result = await shell.sendCommandWithOutput(`cat(nrow(${rows}),${(0, convert_values_1.ts2r)(shell.options.eol)})`); (0, assert_1.guard)(result.length === 1, () => `expected exactly one line to obtain the number of R tokens, but got: ${JSON.stringify(result)}`); return Number(result[0]); } function guardRetrievedOutput(output, request) { (0, assert_1.guard)(output !== init_1.ErrorMarker, () => `unable to parse R code (see the log for more information) for request ${JSON.stringify(request)}}`); return output; } //# sourceMappingURL=retriever.js.map