0

Given an input string, I need to find and replace this:

funccall(x, y, z, w)

With this:

call(x, y, 0, z, w)

Everything between the commas, as well between a comma and a bracket, is unknown (i.e., may be different on each input). This includes the values of x, y, z and w, as well as the amount of spaces in between.

But the funccall part is constant (i.e., that's the actual substring that I should be looking for), and the fact that I need to insert 0, after the 2nd comma is also constant.

Here is my method:

function fix(str) {
    const index0 = str.indexOf('funccall');
    if (index0 >= 0) {
        const index1 = str.indexOf(',', str.indexOf(',', index0) + 1);
        const part0 = str.substring(0, index0);
        const part1 = str.substring(index0 + 'func'.length, index1) + ', 0';
        const part2 = str.substring(index1);
        return part0 + part1 + part2;
    }
    return str;
}

I was really hoping for a one-liner solution (or at least something close to a one-liner).

1
  • 2
    I was really hoping for a one-liner solution” - do remember that readability is a feature, not a bug (as clever as one-line solutions look, they often need to be commented with explanations so you, or your, colleagues can later understand what it’s doing, and how) :) Commented Jun 27, 2019 at 9:47

2 Answers 2

2

If you can count on the input string not containing commas outside of the argument separators, you can use a regular expression instead - match the x, y, part, and the z, w part, and put a 0, in the middle:

const fix = str => str.replace(
  /(funccall\((?:[^,]+,){2})([^,]+,[^,]+\))/g,
  (_, g1, g2) => `${g1} 0,${g2}`
);

console.log(fix('funccall(x, y, z, w)'));

The pattern is composed of 2 groups:

  • (funccall\((?:[^,]+,){2}):
    • funccall\( - Match funccall followed by (
    • (?:[^,]+,){2}) - Match (anything but a comma, followed by a comma) twice
  • ([^,]+,[^,]+\)):
    • ([^,]+, - Match anything but a comma, followed by a comma
    • [^,]+ - Followed by anything but a comma (final argument has no trailing comma)
    • \) - Followed by )
Sign up to request clarification or add additional context in comments.

3 Comments

You cannot use regex to do this reliably. For instance, funccall(x, y * foo(1, 2), z, w) breaks it.
Please do not delete your answer, I will likely use it (after testing it of course). My overall expression is not expected to be that complicated. It's essentially a string which represents a function call with 4 input arguments, and a known function name.
@goodvibration - I used to say that about doing the same thing to modify HTML. Every single time I thought "Oh, my example is simple enough there won't be a problem"...there was a problem. :-) Now I just remind myself not to do that.
1

Everything between the commas, as well between a comma and a bracket, is unknown (i.e., may be different on each input).

and

I was really hoping for a one-liner solution (or at least something close to a one-liner).

There isn't one, JavaScript (I'm assuming the input string is JavaScript) is too complex for that. You're going to get regular expression "one-liner" answers. They will be wrong. You need to balance parens, braces, square brackets, single quotes, double quotes, and backticks (at least); and you need to handle comments (including multi-line comments); etc., etc., etc.

You need a JavaScript parser for this. There are several available that can be used with Node.js, such as @babel/parser (the parser used by Babel) and Esprima.

In the tree the parser provides, you'd look for a function call with the name funccall. The node in the tree (in a good parser) will tell you the starting and ending locations within the string of that call's source text, and of course have nested nodes for all of the individual parts of this.

It will not be as complex as you may think.

4 Comments

@RokoC.Buljan - LOL
Even my own solution will not necessarily work (I now understand) if, for example, x is replace with someFunc(a, b).
@goodvibration - Exactly. And of course, even that's relatively simple. Consider funccall(x, y * array.reduce((acc, x) => { /*...complex function logic...*/ return acc;}, 0), z, w) :-)
"In the tree the parser provides, you'd look for a function call with the name funccall". OK, this is an awesome idea. I just found the place in the code (a node module which I fetch via npm install) where that tree is generated. I will look further into this. I believe that it will allow me to insert my 0 parameter right before this tree is converted into a code string. Thank you for that!!!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.