UNPKG

@eagleoutice/flowr-dev

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

77 lines 3.39 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.matchCallArguments = matchCallArguments; const r_function_call_1 = require("../../../r-bridge/lang-4.x/ast/model/nodes/r-function-call"); const type_1 = require("../../../r-bridge/lang-4.x/ast/model/type"); /** * The arguments of a call, one slot per declared parameter, matched the way R does: a named argument goes to * the parameter it names, the rest fill the remaining slots left to right, and a `...` parameter collects * everything that is left over (positional arguments from its position on, and named ones matching no other * parameter). Parameters in `ignored` may be supplied but get no slot, for the ones that change nothing about * what the call yields. * * `undefined` when the call cannot be the one `params` describes: an argument that names an unknown parameter * with no `...` to absorb it, one parameter given twice, or more arguments than there are slots. The value * solver relies on that, since a call it cannot match is one whose result it must not guess at. */ function matchCallArguments(node, params, ignored = []) { if (node.type === type_1.RType.UnaryOp) { return [node.operand]; } else if (node.type === type_1.RType.BinaryOp) { return [node.lhs, node.rhs]; } else if (node.type !== type_1.RType.FunctionCall || !node.named) { return undefined; } const rest = params.indexOf('...'); const matched = params.map(() => undefined); // what `...` collects, remembered with the position it was written at, since R keeps them in source order const collected = []; const positional = []; // R matches every named argument before it gives a slot to a positional one, so this takes two passes for (const [at, arg] of node.arguments.entries()) { if (arg === r_function_call_1.EmptyArgument || arg.value === undefined) { continue; } else if (arg.name === undefined) { positional.push({ at, value: arg.value }); continue; } const name = arg.name.content; if (ignored.includes(name)) { continue; } const to = params.indexOf(name); if (to >= 0 && to !== rest) { if (matched[to] !== undefined) { return undefined; // the same parameter twice, which R would reject too } matched[to] = arg.value; } else if (rest >= 0) { collected.push({ at, value: arg.value }); // a name no parameter carries falls into `...`, as it does in R } else { return undefined; } } // the slots before `...` take positional arguments in order, everything from there on `...` collects for (const entry of positional) { const free = matched.findIndex((m, i) => m === undefined && i !== rest); if (free >= 0 && (rest < 0 || free < rest)) { matched[free] = entry.value; } else if (rest >= 0) { collected.push(entry); } else { return undefined; // more arguments than the parameters describe, so this is another call } } if (rest >= 0) { matched[rest] = collected.sort((a, b) => a.at - b.at).map(e => e.value); } return matched; } //# sourceMappingURL=match-arguments.js.map