UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

149 lines 5.07 kB
"use strict"; /** * Just to avoid another library for splitting arguments, we use this module to provide what we need. * @module */ Object.defineProperty(exports, "__esModule", { value: true }); exports.splitAtEscapeSensitive = splitAtEscapeSensitive; exports.splitOnNestingSensitive = splitOnNestingSensitive; /** * This splits an input string on the given split string (e.g., ` `), but checks if the string is quoted or escaped. * * Given an input string like `a "b c" d`, with a space character as split, and escapeQuote set to true, * this splits the arguments similar to common shell interpreters (i.e., `a`, `b c`, and `d`). * * When escapeQuote is set to false instead, we keep quotation marks in the result (i.e., `a`, `"b c"`, and `d`.). * @param inputString - The string to split * @param escapeQuote - Keep quotes in args * @param split - The character or character sequence to split on (can not be backslash or quote!) */ function splitAtEscapeSensitive(inputString, escapeQuote = true, split = ' ') { const args = []; let current = ''; let inQuotes = false; let escaped = false; for (let i = 0; i < inputString.length; i++) { const c = inputString[i]; const sub = inputString.slice(i); if (escaped) { escaped = false; switch (c) { case 'n': current += '\n'; break; case 't': current += '\t'; break; case 'r': current += '\r'; break; case 'v': current += '\v'; break; case 'f': current += '\f'; break; case 'b': current += '\b'; break; default: current += c; } } else if (!inQuotes && current !== '' && (split instanceof RegExp ? split.test(sub) : inputString.slice(i, i + split.length) === split)) { args.push(current); current = ''; } else if (c === '"' || c === "'") { if (!inQuotes) { inQuotes = c; if (escapeQuote) { continue; } } else if (inQuotes === c) { inQuotes = false; if (escapeQuote) { continue; } } current += c; } else if (c === '\\' && escapeQuote) { escaped = true; } else { current += c; } } if (current !== '') { args.push(current); } return args; } const MatchingClose = { '<': '>', '[': ']', '(': ')', '"': '"', "'": "'" }; /** * Splits the given string on 'and', but only if not nested inside `<>`, `[]`, `()`, or quotes. * This also handles escaped quotes. * @param str - The string to split * @param splitOn - The string to split on (default: 'and') * @param closes - The matching of closing characters for nesting, structures with different open and close characters only can be nested * of each other, while those with the same open and close character (like quotes) can not be nested inside themselves. */ function splitOnNestingSensitive(str, splitOn = 'and', closes = MatchingClose) { const result = []; const openCloseSame = new Set(Object.entries(closes).filter(([open, close]) => open === close).map(([open]) => open)); let current = ''; const nestStack = []; for (let i = 0; i < str.length; i++) { const c = str[i]; if (c === '\\' && i + 1 < str.length) { // skip escaped characters current += c + str[i + 1]; i++; } else if (nestStack.length > 0) { if (!openCloseSame.has(c) && c in closes) { // opening a new nest nestStack.push(c); } else { const top = nestStack[nestStack.length - 1]; if (c === closes[top]) { nestStack.pop(); } } current += c; } else { if (c in closes) { nestStack.push(c); current += c; continue; } // check for 'and' split if (str.slice(i, i + splitOn.length) === splitOn && (i === 0 || /\s/.test(str[i - 1])) && (i + splitOn.length >= str.length || /\s/.test(str[i + splitOn.length]))) { // split here result.push(current.trim()); current = ''; i += splitOn.length - 1; continue; } current += c; } } if (current.trim().length > 0) { result.push(current.trim()); } return result; } //# sourceMappingURL=args.js.map