@eagleoutice/flowr-dev
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
70 lines • 3.06 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.DotsParameterName = void 0;
exports.matchArgumentsToParameters = matchArgumentsToParameters;
const prefix_1 = require("./prefix");
/** the name of the special `...` parameter which collects everything that finds no formal of its own */
exports.DotsParameterName = '...';
/**
* Bind the arguments of a call to the formal parameters of the called function, following R's argument matching
* rules (see https://cran.r-project.org/doc/manuals/R-lang.html#Argument-matching):
*
* 1. every named argument that *exactly* matches a formal takes it,
* 2. every remaining named argument that is a *unique prefix* of a still-free formal takes it (`pmatch`),
* 3. the remaining unnamed arguments fill the still-free formals from left to right, stopping at `...`
* (formals behind `...` can only be matched by name), and
* 4. everything that is still unmatched goes to `...` if the function has one.
*
* Both sides are given by their names alone (`undefined` for an unnamed argument or an anonymous formal), so this
* works for the normalized AST, the dataflow graph, and a plain signature list alike. An empty argument (`f(1, ,3)`)
* is an unnamed one: it takes its formal like any other, the caller just has nothing to bind to it.
* @returns for every argument (by index) the index of the parameter it binds to, `undefined` if it stays unbound
*/
function matchArgumentsToParameters(argNames, paramNames) {
const bound = Array.from(argNames, () => undefined);
const names = paramNames.map(p => p ?? '');
const dots = names.indexOf(exports.DotsParameterName);
const taken = new Set();
const take = (arg, param) => {
bound[arg] = param;
taken.add(param);
};
/** binds each still-free named argument to the formal `pick` names for it, as long as that one is still free too */
const byName = (pick) => {
for (let i = 0; i < argNames.length; i++) {
const name = argNames[i];
if (name === undefined || bound[i] !== undefined) {
continue;
}
const target = pick(name);
const param = target === undefined ? -1 : names.indexOf(target);
if (param >= 0 && !taken.has(param)) {
take(i, param);
}
}
};
byName(name => name); // (1) exact
byName(name => (0, prefix_1.findByPrefixIfUnique)(name, names)); // (2) pmatch
// (3) positional
let formal = 0;
for (let i = 0; i < argNames.length; i++) {
if (argNames[i] !== undefined || bound[i] !== undefined) {
continue;
}
while (taken.has(formal)) {
formal++;
}
if (formal >= names.length || formal === dots) {
break;
}
take(i, formal);
}
// (4) `...`
if (dots >= 0) {
for (let i = 0; i < argNames.length; i++) {
bound[i] ??= dots;
}
}
return bound;
}
//# sourceMappingURL=arg-matching.js.map