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