HenryWilder / amitygxmod-calculator

A browser extension designed for Opera GX, made for helping with algebra
1 stars 0 forks source link

Parameter expressions #3

Open HenryWilder opened 11 months ago

HenryWilder commented 11 months ago

Parameter expressions

A highly useful feature currently implemented in AlgebraBalancer is the ability to write expressions in the parameter inputs, which are evaluated before being passed to calculations. image In AlgebraBalancer, this feature is safe because it uses DataTable, which evaluates specifically math and not code. I'm unsure whether a similar feature exists in Node, and I definitely want to avoid giving the user access to potentially dangerous options like eval().

In order to enforce the desired safety, it may be necessary to write a custom math evaluation function which sanitizes inputs before passing them to Function().

The sanitized inputs might be checked by matching them against a regex pattern. There could also be a second pattern (breaking them up to simplify the overall pattern) for checking that parentheses are balanced, if any are detected in the pattern.

// Tests whether the expression uses only whitelisted operations, and is therefore safe to pass to Function()
const isMathExpressionSafe = (expr: string): boolean => {
    // Any instance of open or close parentheses
    const parentheses = /\(|\)/g;
    // The string is safe if it:
    // 1. Has at least one integer
    //   1.1. Which may be preceded with a + or -
    // 2. Optionally followed by exactly one of the following:
    //   a)
    //     2a1. One of the following operations:
    //       - Exponentiation
    //     2a2. Which, if an operation exists, must always be followed by another integer
    //       2a2.1. Which may be preceded with a +
    //   b)
    //     2b1. One of the following operations:
    //       - Multiplication
    //       - Addition
    //       - Subtraction
    //     2b2. Which, if an operation exists, must always be followed by another integer
    //       2b2.1. Which may be preceded with a + or -
    // 3. Repeating section 2 for as many operations as are necessary
    const whitelistedMath = /^[+-]?[0-9]+(?:(?:\^\+?[0-9]+)|(?:[*+-](?:[+-]?[0-9]+)))*$/;
    let isExpressionValid = expr.replace(parentheses, '').match(whitelistedMath);

    if (expr.includes(parentheses)) {
        // For the cases where parentheses are encountered, they must either
        // A) Contain no instance of parentheses within them
        // B) Contain only parentheses which also follow this pattern, recursively
        const balancedParentheses = /\((?:[^)(]+|(?R))*+\)/;
        isExpressionValid &&= expr.match(balancedParentheses);
    }

    return isExpressionValid;
}

While exponents in parameter expressions are not supported in AlgebraBalancer, I believe they should be in this project considering the evaluator will be custom and can be made to map operations to their respective functions.

Negative exponents in parameter expressions should be disallowed. At the time of writing, it is an unwritten guideline for this project that parameters may not contain fractions nor radicals; only integers and math which simplifies to integers.

Division is not currently an expectation of the feature. Testing to confirm that the result doesn't resolve to a fraction nor undefined may be more effort than it is worth. It would also be frustrating to users for rule-following expressions to simply not evaluate sometimes.

HenryWilder commented 11 months ago

Problem 1: Exponent expressions

If the feature is implemented such that sub-expressions are valid arguments for exponent powers, it is possible that a user may pass an expression which, while not preceded with a minus, still evaluates to a negative.

For example: $5^{(6-8)}$, which resolves to $5^{-2}$ or $\frac{1}{25}$. This clearly results in a fraction, which goes against the unwritten guidelines.

While disallowing exponents to contain parentheses may work, it might also reduce the overall utility of the application.

Problem 2: Inconsistent application of rules between fractions and exponents

The very fact that exponents would be permitted in parameter expressions creates some inconsistency in rules. Whether they are allowed or not, their problems mirror those of fractions.

Specifically, consider the arguments against inclusion of fractions in parameters:

And then consider the arguments regarding inclusion of exponents in parameters:

While exponents create fewer overall issues than fractions, they have the potential to directly create fractions, while simultaneously sharing (arguably) the most important reason against including fractional parameters.

HenryWilder commented 11 months ago

Problem 3: Parentheses in parameter expressions

While I am certain that parentheses in parameter expressions would be extremely useful, there are some reasons I think they should not be added.

  1. The JS-screwery parentheses could allow might be potentially dangerous, likely requiring a custom parser itself (which I really don't wanna have to write myself). Unpredictable inputs like ((()())+()(-())())()(*) may, for all I know, open users up to unsafe edge cases; or errors at the very least.

  2. Testing for validity would be excessively complex, considering cases like $(5)+8$ which are balanced, but syntactically invalid*. And persistent input lag is almost certainly a greater concern for most users than needing to copy and paste the results of their sub-expressions into the notes section.

  3. Inputs may not be able to proactively prevent invalid strings, as an open parenthesis may simply be awaiting its closing partner.

    • It is possible this could be remedied by creating both parentheses simultaneously when the user types an open parenthesis ((). However, this creates a problem when the user wants to delete the parentheses, in that the program may not be 100% accurate in selecting what parenthesis corresponds to the one being deleted.\ \ Not an impossible problem to solve, but if Visual Studio and Visual Studio Code themselves have trouble deleting matching parentheses pairs, it's probably safe to assume it shouldn't underestimated, and there may be more complexity to this problem than is visible at a glance. Then again, maybe it's not difficulty, but rather a mere lack of implementation.