UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

175 lines 7.06 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.isParseRequest = isParseRequest; 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://'; /** * Type guard for {@link RParseRequest} */ function isParseRequest(request) { if (typeof request !== 'object' || request === null) { return false; } return 'request' in request; } /** * 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}). * * To obtain a {@link FlowrAnalyzerContext} from such an input, use {@link contextFromInput}. */ function requestFromInput(input) { if (Array.isArray(input)) { return input.flatMap(requestFromInput); } const content = input; const file = content.startsWith(exports.fileProtocol); if (file) { return { request: 'file', content: content.substring(exports.fileProtocol.length), }; } else { return { request: 'text', content }; } } /** * Creates a {@link RParseRequestProvider} that reads from the file system. * Uses `fs.existsSync` to check for file existence. * @see {@link requestProviderFromText} for a provider that reads from a text map. */ 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, }; } }; } /** * Creates a {@link RParseRequestProvider} that reads from the given text map. * @see {@link requestProviderFromFile} for a provider that reads from the file system. */ 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] ?? '' }; } }; } /** * Checks whether the given {@link RParseRequest} is empty (has no content). */ 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}. * This function is outdated and should only be used for legacy reasons. Please use the {@link FlowrAnalyzer} instead. */ async function retrieveNormalizedAstFromRCode(request, shell) { const data = await retrieveParseDataFromRCode(request, shell); return (0, parser_1.normalize)({ files: [{ parsed: data, filePath: request.request === 'file' ? request.content : undefined }] }, (0, decorate_1.deterministicCountingIdGenerator)(0)); } /** * 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