Teiem
Teiem

Reputation: 1619

RegEx for matching calculations in strings

I have a function which can take a string, interpret it as a calculation and return the result of the calculation, examples for valid calculations are:

3(log(e+pi^(22/3)))
44+3*(pi+2)/root(7)

the function is quite heavy so I would like to only run it if the String actually is a calculation. Before I added functions like log and root, pi and e and implicit multiplycation i used the following regex:

/^((-?([0-9]+?\.)?[0-9]+?)\s?([+\-*\/%^]|(\*\*))\s?)+?(-?([0-9]+?\.)?[0-9]+?)$/

which doesn't work anymore. At this point I am not even sure if a regex would make sense performance wise. I expect about 0.1% of strings to match (being a valid calculation).

Do you have any ideas on how to create a well performing regular expression (the function itself determines weather its a valid calculation itself, but it takes a long time, so no 100% accuracy needed) or a function which validates the calculation?

Upvotes: 0

Views: 987

Answers (2)

Teiem
Teiem

Reputation: 1619

I have written a function which verifies the calculation, here is the code:

    const isValidCalc = (calc) => {
    contains = {
        br: false,
        num: false,
        let: false,
        op: false,
    }
    let prev;
    let level = 0;

    return ![...calc.replace(/\*\*/g, "^").replace(/ /g, "").replace(/e/g, Math.E).replace(/pi/g, Math.PI)].some(el => {
        if (el === "(") {
            prev = "open";
            level++;
            return false;
        };
        if (el === ")") {
            if (level-- === 0 || prev === "letter") return true;

            prev = "close";
            contains.br = true;
            return false;
        }
        if (_.is.Operation(el)) {
            if (prev === "operator" || prev === "letter") return true;

            prev = "operator";
            contains.op = true;
            return false;
        }
        if (_.is.Numeric(el) || el === ".") {
            if (prev === "close" || prev === "letter") return true;

            prev = "numeric"
            contains.num = true;
            return false;
        }
        if (_.is.Letter(el)) {
            prev = "letter" 
            contains.let = true;
            return false;
        }

        return true;
    }) && level === 0 && contains.num && (!contains.let || contains.br) && (contains.let || contains.op);
};

Upvotes: 0

Peipei
Peipei

Reputation: 176

The question you are asking is in essence Regular Expression AND String Parsing. IMHO, your string calculation can be built as a syntax tree. It would be easier to build a parser for it than to create a rather complicated regex.

Upvotes: 1

Related Questions