Code Monkey home page Code Monkey logo

expression's People

Contributors

basthomas avatar carter-thaxton avatar flangel avatar foscomputerservices avatar frranck avatar maximkotliar avatar nicklockwood avatar nighthawk avatar traviskirton avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

expression's Issues

Swift 5 support

Hi,

It seems like there is a warning on Swift 5:

Expression.swift:153:20: 'Hashable.hashValue' is deprecated as a protocol requirement; conform type 'Expression.Symbol' to 'Hashable' by implementing 'hash(into:)' instead

Thanks

Support ^ as pow operator

I don't think that pow(2, 3) is actually understood by anyone. Instead it feels more naturally to write 2^3.

Is it possible to support this using symbols? If not, would it be possible to include this as a standard operator?

String literals/variables

Great expression tool.

Do you have plans to support string processing such as concatenation, length, substrings, etc. ?

If not, how would you suggest I handle this as strings are a component of the expressions I'm working with?

variables with index

It would be great if we could support "myarray[1]" as a symbol. If you don't think its a good idea for the main branch could you offer a pointer to the best way to implement it anyway? :-)
thanks.

inaccurate result for 7+6.3+5.3+4.42

the result of 7+6.3+5.3+4.42 is 23.02, but what it returns is 23.020000000000003, I know it's because Double is not accurate enough for this result.

Test suite crashes on Xcode 13.3 / Swift 5.6

Running this repo's test suite on macOS 12.3 with Xcode 13.3 and Swift 5.6 crashes on my M1 MacBook Air with an EXC_BAD_ACCESS.

This happens in tests such as testTooFewArgumentsForCustomFunctionWithAdvancedInitializer and a number of other ones which test a mismatching number of arguments.

#0	0x000000016fdfcd98 in 0x16fdfcd98 ()
#1	0x00000001078bc278 in thunk for @escaping @callee_guaranteed (@in_guaranteed [Double]) -> (@out Double, @error @owned Error) ()
#2	0x00000001078f92e0 in partial apply for thunk for @escaping @callee_guaranteed (@in_guaranteed [Double]) -> (@out Double, @error @owned Error) ()
#3	0x00000001078dfba8 in Subexpression.optimized(withImpureSymbols:pureSymbols:) at /Users/adrian/Development/forks/Expression/Sources/Expression.swift:892
#4	0x00000001078de7ac in Expression.init(_:impureSymbols:pureSymbols:) at /Users/adrian/Development/forks/Expression/Sources/Expression.swift:351
#5	0x00000001078de6e0 in Expression.__allocating_init(_:impureSymbols:pureSymbols:) ()
#6	0x00000001078dfe20 in Expression.__allocating_init(_:pureSymbols:) at /Users/adrian/Development/forks/Expression/Sources/Expression.swift:372
#7	0x000000010788b350 in ExpressionTests.testTooFewArgumentsForCustomFunctionWithAdvancedInitializer() at /Users/adrian/Development/forks/Expression/Tests/ExpressionTests.swift:934

I couldn't yet narrow down what might be causing it and if it's an issue introduced with Swift 5.6 or with this library, or what to do about it. It happens both when running through Xcode or with swift test from the command line. Any advise appreciated!

AnyExpression using array variables can't be evaluated with error 0

Hello,

Maybe I haven't read the readme well enough, but it mentions that for AnyExpression, I can just add a variable in the dictionary of constants as below, and explicitly cast the return. I'm not sure if the evaluation can consider say an array with a constant term (X + 1 where X say is an array), but I also tried something like X + X which would normally add the elements of X individually, like a numpy array addition. Maybe I'm being overtly optimistic with what Expression can do?

func expression(_ X: [CGFloat], _ expression: String) {
    let x = X.map{Double($0)}
    let expression = expression.lowercased()
    let eval = AnyExpression(expression, constants: ["x": x])
    do {
    let result:[CGFloat] = try eval.evaluate()
    print(result)
    } catch let error {
        print(error.localizedDescription)
    }
}

Trying to evaluate when the region of the device is set to french

Hi, I'm trying to evaluate a basic expression, like this one
2+3.5
so if I have the region set to some other country that the comma is the decimal separator, the method evaluate is throwing the error, not sure if there is a way to set the decimal separator
thanks in advance, you did a really great work with this library

Equality of strings, substrings and characters

Intuitively I'd expect 'Test'[0] == 'T' to return true but it evaluates to false.

In equalArgs this lands in this case:

            case let (lhs as AnyHashable, rhs as AnyHashable):
                return lhs == rhs

This is due to rhs is a String while lhs is a Character.

This could be fixed by adding:

            case let (lhs as _String, rhs as _String):
                return lhs.substring == rhs.substring

However, this makes me wonder if all the other AnyHashable cases should also get _Substring variants. Alternatively, the _String cases in arrayEvaluator could be changed to always return a String, but that could lead to inefficiencies.

Any suggestions on how best to address this? Or does this work as expected?

Crash in Swift 5.7 when creating stringBounds error

There's a crash in static func stringBounds(_ string: Substring, _ index: String.Index) -> AnyExpression.Error -> AnyExpression.Error happening on Swift 5.7 on macOS or Linux, which can be reproduced by running the Test Suite.

This happens for cases where index < _string.startIndex as let offset = _string.distance(from: _string.startIndex, to: index) will no longer return a negative value but instead crash at runtime as index is out of bounds of the string.

I tried a few approaches but couldn't figure out how to still get that correct negative value. Suggestion welcome! My workaround was to make stringBounds(_ string: String, _ index: Int) take instead an index: Int? and use generic wording in the error if no good index could be provided, but that's worse behaviour than what it did before...

problem with swift build, missing 0.3.0 tag ?

Hi,
If I'm trying to point to your repo, instead of my fork
.Package(url: "https://github.com/nicklockwood/Expression", Version(0,3,0))])
and run
$ swift build

Cloning https://github.com/nicklockwood/Expression
error: No version tag found in (/Users/franck/dev/mrboom_pi/asm2c/Packages/Expression) package. Add a version tag with "git tag" command. Example: "git tag 0.1.0"

I get this error, I think it's because you're missing a tag "0.3.0"
If I try to use a 0.3 tag instead:

    .Package(url: "https://github.com/nicklockwood/Expression", Version(0,3))])

I get this error: ^
Can't parse Package.swift manifest file because it contains invalid format. Fix Package.swift file format and try again.

Could you please try to add a "0.3.0" tag ?

Fact `!` postfix operator does not work

let expression = "5!"

let result = try Expression(
    expression,
    symbols: [
        .postfix("!"): { Double(fact(Int($0[0]))) }
    ]
).evaluate()

print(result)

private func fact(_ number: Int) -> Int {
   if number == 0 {
      return 1
   } else {
      return number * fact(number - 1)
   }
}

I would expect 120, but the infix operator isn't called.

Function expression failed with string paramater which contains \

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.

Expose AST

Not a super pressing need, but it would be kind of fun if the AST was exposed (I believe subexpression is fileprivate right now). We're using this library for procedurally generating textures, so we need all the performance we can get. We were thinking of writing a little compiler that will compile the expression to Swift code, or maybe to shader code. Thanks for the library!

Lazy evaluation of ternary operator's true/false cases

I've set up a bunch of expression functions and am hitting an issue where the ternary operator might check a condition and if that condition isn't met, the true part throws an error. Say I have a date: Date variable and a format/2 function that expects a Date and String and throws an error if the first parameter is not a Date. If I then use that in an expression like date != nil ? format(date, "yyyy-MM-dd") : "None" when date = nil, it'll throw that error rather than returning "None".

I could adjust my functions to not throw errors and return nil instead, but then I'm losing useful information when I use them elsewhere. And I'd have to change most functions to always accept nil for every parameter. Not ideal.

I'm wondering if it's possible to have the ternary operator evaluate the true/false cases lazily and, in particular, only evaluate the necessary part.

Or is there another possibility to address this? Feedback appreciated.

Complex expressions are not working

I have an expression like this -> "(0.341 * 8000.0) / (1 - (1 + 0.341) ^ -84)" , I used below code but giving wrong answer.

Expected result - "109.71908469357446"
Actual result - "1.538906384258725e-36"

Can someone please correct me?

do {
            let result = try Expression(
                "(0.341 * 8000.0) / (1 - (1 + 0.341) ^ -84)",
                symbols: [
                    .infix("^"): { pow($0[0], $0[1]) }
                ]
            ).evaluate()

            print(result) 

        } catch {

        }

Expression.parse("'\\n'") fails

I've hit a funny little bug with this excellent library. I'm trying to evaluate an expression such as AnyExpression.parse(var1 + "'\\n'" + var2, ...), but this fails with an unexpectedToken("\\n\'"). If I modify it slightly to ``AnyExpression.parse(var1 + "' \n'" + var2, ...)` it works -- notice the additional space.

The simplest way to reproduce this is Expression.parse("'\\n'").

It seems to be an issue with parseEscapedIdentifier and in particular this line:

while let part = scanCharacters({ $0 != delimiter && $0 != "\\" }) {

Unfortunately this implementation is going a bit over my head, so I haven't yet figured out how to fix this. Any advice would be much appreciated.

Question: Overloading existing operators

I'm experimenting with using AnyExpression and its powerful support for custom data types and operators. This is working well, though I'm facing an issue when I want to use the same operator as defined by Expression (or AnyExpression) itself, but only use my overload for the custom data types and falling back to the default implementations otherwise.

Say, for example in have a "date" datatype which is really just Swift's Date, and I want to allow comparisons using <, <= and their friends, but if I define those as .infix("<") then this now works for my date types, but regular numeric expressions now fail.

I've tried following this from the README:

If you are using the init(impureSymbols:pureSymbols:) initializer, you can fall back to the standard library functions and operators by returning nil for unrecognized symbols.

However, I can't return nil at that stage, as I'd have to return nil from within the operator's SymbolEvaluator as it depends on the specific arguments.

Is this possible or do I have to reimplement the default behaviour in my custom operator?

Build failure on Swift 5.1 - partial application of 'mutating' method is not allowed

When attempting to build the library using Swift 5.1 the following error is generated.

/.../Sources/AnyExpression.swift:1096:72: error: partial application of 'mutating' method is not allowed
        return (AnyExpression.arrayCast(value) as [Element]?).map(self.init)

Steps to repeat:

  1. Install Swift 5.1 (or Xcode 11)
  2. git clone https://github.com/nicklockwood/Expression.git
  3. cd Expression
  4. swift build

The offending code is as follows:

extension ArraySlice: _SwiftArray {
    fileprivate var values: [Any] {
        return Array(self)
    }

    static func cast(_ value: Any) -> Any? {
        return (AnyExpression.arrayCast(value) as [Element]?).map(self.init) // <-- self.init fails to compile Swift 5.1
    }
}

Working with Vectors and Matrixes

Are there any plans for vectors/matrixes compatibility?
making this work:
sin([1, 2, 3])
--> [0.8414, 0.9092, 0.1411]

Same for matrixes. Basically changing evaluate() -> Double to either evaluate() -> T or evaluate() -> [Double]

Getting sub expressions during evaluation

Problem:
I want to add a postfix % operator that takes in the last valid expression and transform it, however (and I'm still learning this library) it seems without a lot of parsing it's kind of challenging -- is there a way to transform something like this

10 + 2% to equal 10 + (10*2/100)

or

20 * 2 + 10% would give access to the last evaluated expression of (20 * 2) and the main argument 10 when getting the post fix callback for %

Question: Is implicit multiplication possible with Expression?

Is implicit multiplication possible with Expression? I see according to #39 that implicit multiplication is sort of possible using a custom operator for parenthesis, but I haven't seen any indication that implicit multiplication without parenthesis is possible.

For example: try Expression("2 * pi") is valid, but try Expression("2pi") is not.

Using the recommendation in #39, I could add the custom operator for parenthesis, but in my case (a scientific calculator that takes user input), I'd have to parse the input beforehand and intelligently wrap anything that should be implicitly multiplied in parenthesis before passing it to Expression.

So is there any other way to enable implicit multiplication, or will I have to use the recommendation in #39 along with parsing the input separately to intelligently wrap certain parts of the expression in parenthesis?

Order of operations question

First off let me say I am really impressed with this library. I do have a question though. I am trying to include "^" as an alternative infix operator to the pow() function. Do you have any recommendation on how one could designate the order of operation for new math operators?

This is how I added the operator:

        Expression(
         Expression.parse(stringValue),
            impureSymbols: { symbol in
                switch symbol {
                case .infix("^"): return {  args in
                    return pow(args[0], args[1])
                }
                default: return nil
                }
            }
        )

Multiplication followed by dot followed by digit issue

Hello,

I'm getting the following error:
13:18:25 ERROR Error in expression X/((16.7-W)*.0000212*Y*Z): Undefined infix operator *.

I was hoping this commit would fix the issue: 08fef32 but it doesn't seems like it does.

Probably a simpler expression like 1*.5 would fails as well.

Hopefully this can be fixed somewhat easily.

Thank you

Unexpected result related to whitespace and operators

I'm running into an issue where the absence of a whitespace character before a '+' operator in some cases has an affect on the result. For instance:

Expression("3 * 4 +5").evaluate() // 27.0
Expression("3 * 4 + 5").evaluate() // 17.0
Expression("3*4+5").evaluate() // 17.0

I believe this to be a bug.

Square brackets

Hello, I have problem with evaluating expression with square brackets.

In math it's OK to write [(10,3+10,1)*7]+3, but flee will not evaluate such expression..

Is there a way to make it work?

Thank you

P.S. of course I can replace square with round brackets, but it is not comfortable

when using the sample code it won't execute

the sample code cut from the description:
var bar = 7 // variable
let expression = Expression("bas + baz(5) + rnd()", symbols: [
.variable("bar"): { _ in bar },
.function("baz", arity: 1): { args in args[0] as! Int + 1 },
.function("rnd", arity: 0): { _ in Double(arc4random()) }
])

Has this issue: "Type of expression is ambiguous without more context" "Expression" is imported

Ability to use ^ instead of pow(x, y)

I believe that using ^ as an alternate operator for powers would be a very beneficial addition. For example, instead of typing pow(4, 2), one could type 4^2 instead.

Usage for macOS Apps (Swift 5.1/Xcode 11.2.1)

Hi there,

this might be kinda awkward but I'm totally stuck here.

I wanted to try Expression for a little macOS App written in Swift 5 and Xcode 11. I have tried both just copying Expression.swift or installing as a framework by using cocoapods.

What am I doing wrong when Xcode keeps displaying

No such module 'Expression' ?

I have a totally naked project so no complex stuff in here - just a new project.

I saw in the Example folder that these were made for iOS (import UIKit, import Expression) and it works fine but there must be a(n awkward) thing I'm missing.

Sorry for this one. Just a beginner trying his best :-/

Why isn't "=" an operator?

Looking at your REPL example, I was wondering why you manually subdivide the expression around the "=" sign rather than making the "=" a proper operator, executing the same logic in its definition.

Maybe there is something I'm not understanding as to how the framework works.

Thanks.

Question: Can multiplication be done with parentheses?

I am currently working on creating a calculator and have been using this framework for expression parsing, which has been going wonderfully so far. I am wondering if there is a way to implement multiplication with parentheses (for example "4(2)" = 8). If this is not possible with the current state of the framework, is it something worth adding in?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.