function grid(w, h) {
const arr = new Array(w);
for(let i = 0; i < w; i++) {
arr[i] = new Array(h);
}
return arr;
}
function canCoerce(param: string, arg: string) {
return param === arg;
}
function checkParameters(
params: string[],
args: string[]
) {
const compatibility: boolean[][] = grid(args.length, params.length);
let allCompatible: boolean = params.length === args.length; // Make sure our happy path is fast
for(let i = 0; i < Math.min(params.length, args.length); i++) {
let paramCompatible = canCoerce(params[i], args[i]);
allCompatible = allCompatible && paramCompatible;
compatibility[i][i] = paramCompatible;
}
if(allCompatible) {
console.log("Function call allowed!");
return;
}
// We had some kind of incompatibility. Either some params didn't match, or the lengths didn't match
// To provide good heuristics, probe to see if adjacent arguments can line up
// Start by filling in a compatibility grid
for(let i = 0; i < args.length; i++) {
for(let j = 0; j < params.length; j++) {
if(i === j) continue; // We've already computed these
compatibility[i][j] = canCoerce(args[i], params[j]);
}
}
// Now collapse the matrix into two arrays to represent whether the whole row or the whole column is true or false
// (A more efficient implementation could implement that inline, to save on memory)
// - If a column is all false, it means there is no input argument that satisfies that parameter
// - If a row is all false, it means there is no input parameter that an argument *could* satisfy
let rows: boolean[] = Array(args.length); for(let i = 0; i < args.length; i++) { rows[i] = false; }
let cols: boolean[] = Array(params.length); for(let i = 0; i < params.length; i++) { cols[i] = false; }
for(let i = 0; i < args.length; i++) {
for(let j = 0; j < params.length; j++) {
rows[i] = rows[i] || compatibility[i][j];
cols[j] = cols[j] || compatibility[i][j];
}
}
let keepGoing = true;
while(keepGoing) {
keepGoing = false;
// Check for *incorrect* parameters:
// If rows[i] and cols[i] are both false, assume the argument is just invalid
for(let i = 0; i < Math.min(args.length, params.length); i++) {
if(rows[i] === false && cols[i] === false) {
console.log(`Expected ${params[i]}, got ${args[i]}`);
rows.splice(i, 1);
cols.splice(i, 1);
args.splice(i, 1);
params.splice(i, 1);
keepGoing = true;
break;
}
}
if(keepGoing) { continue; }
for(let i = 0; i < Math.max(args.length, params.length); i++) {
if(i < rows.length && rows[i] === false) {
console.log(`Supplied argument ${args[i]} is extra!`);
rows.splice(i, 1);
args.splice(i, 1);
keepGoing = true;
break;
}
if(i < cols.length && cols[i] === false) {
console.log(`Expected parameter ${params[i]} is missing!`);
cols.splice(i, 1);
params.splice(i, 1);
keepGoing = true;
break;
}
}
}
}
checkParameters(["A", "B", "C"], ["A", "B", "C"]);
console.log('--');
checkParameters(["A", "B", "C"], ["A", "D", "C"]);
console.log('--');
checkParameters(["A", "B", "C"], ["A", "B", "D", "C"]);
console.log('--');
checkParameters(["A", "B", "C"], ["A", "D", "B", "C"]);
console.log('--');
checkParameters(["A", "B", "C"], ["A", "C"]);
console.log('--');
checkParameters(["A", "B", "C", "D", "E"], ["A", "C", "D", "F", "E"]);