nicklockwood / Expression

A cross-platform Swift library for evaluating mathematical expressions at runtime
MIT License
829 stars 51 forks source link

Function expression failed with string paramater which contains \ #41

Open lemanhtien opened 2 years ago

lemanhtien commented 2 years ago

Here is the default test case, which the result is correct

func testCallAnyExpressionSymbolEvaluatorConstant() {
        let expression = AnyExpression("foo('foo', 'bar')", constants: [
            "foo": { ($0[0] as! String) + ($0[1] as! String) } as AnyExpression.SymbolEvaluator,
        ])
        XCTAssertEqual(try expression.evaluate(), "foobar")
        XCTAssertEqual(expression.symbols, [.function("foo", arity: 2)])
    }

Modify the parameter value a bit, by adding "\", then the result is wrong

  func testCallAnyExpressionSymbolEvaluatorConstant() {
        let expression = AnyExpression("foo('foo\\s', 'bar')", constants: [
            "foo": { ($0[0] as! String) + ($0[1] as! String) } as AnyExpression.SymbolEvaluator,
        ])
        XCTAssertEqual(try expression.evaluate(), "foo\\sbar")
        XCTAssertEqual(expression.symbols, [.function("foo", arity: 2)])
    }

I tested several test case, and noticed that if the parameter is string, and it contains \\, the result will be wrong.

Do you have any ideas to update this one? I think it relates to the function parseEscapedIdentifier.

nicklockwood commented 1 year ago

@lemanhtien sorry for the late reply. From what I can see, this is behaving correctly. What's confusing is the double escaping.

In Swift strings literals \ is an escaped slash, so you get a single \ in the output.

But when that swift string contains an expression, then that single \ is passed to the expression parser. "\s" is not a known escape sequence so the \ is silently dropped, resulting in "foobar" as the output. To include a literal \ in the value you would need to double-escape the slash, like this:

let expression = AnyExpression("foo('foo\\\\s', 'bar')", constants: [...])