Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Default Operators are not appended when new operators added #26

Open
Rod8LE opened this issue Feb 8, 2017 · 2 comments
Open

Default Operators are not appended when new operators added #26

Rod8LE opened this issue Feb 8, 2017 · 2 comments

Comments

@Rod8LE
Copy link

Rod8LE commented Feb 8, 2017

Documentation on your simpleeval package (on github) has an explanation on how add ^ operator, but lacks an example of how operators paramater work

is through a dict with lambda, just like functions paramater work?
already tested the following and it was the only way I could make it work

s = SimpleEval()
s.operators[ast.BitOr] = op.or_

the problem with this is that Or can not be added as or
Also custom functions that are not available in ast package are trickier (could not make it work)
for example lets take operator juggler:
1 juggler 7 returns 5, since juggler takes 1 from 7, and since the result is higher that 1, takes another 1 from the result (6) ending in 5

if the example does not make much sense is because I'm forcing it to be a custom operator

This triggers the fact that when any operator is added DEFAULT OPERATORS are ignored:
if anything is added through parameter operator= a conditional on the constructor (__init__) makes it imposible to append new operators to the default operators

Is this a feature? an optimization feature? maybe I am not using the package correctly

I thought the documentation could use something like:

adding operator Or would require the following:

simple_eval("True Or False", operators={"Or": lambda x, y: x | y})

returns True

simple_eval("1 juggler 7", operators={"juggler": lambda x, y: 
                                       y - 2 *x if (y - x) > x else y - x })

returns 5

this becomes extremely handy when using complex operators for time series when comparisons are time wise (maybe sent in tuple form)

simple_eval("(t0, t1) crosses_above (s0, s1)", 
            operators={"crosses_above": lambda t, s: t[1] > s[1] if t[0] < s[0] else False})

returns True or False depending on the case
when: t[0, 1] = 2, 4 and s[0, 1] = 2.5, 3.5; crosses_above returns True
when: t[0, 1] = 2, 3 and s[0, 1] = 2.5, 3.5; crosses_above returns False

When this issue is clear to me I could open a fork and send the PR with the respective changes into the documentation

Also I am impressed at your work, wish you a wonderful day sir

@danthedeckie
Copy link
Owner

Hi Rod,
Thanks for your kind words.

Yeah, the operators and functions parameters are a bit ugly, it must be said. They do completely over-ride the defaults, rather than appending. In some ways appending would be frequently more useful, I guess. I wonder if a 'append_defaults' would be a useful parameter to add...?

So you can do:

my_ops = simpleeval.DEFAULT_OPERATORS.copy()
my_ops[ast.BitOr] = operators.or_

s = simpleeval.SimpleEval(expr, operators=my_ops)

just like with functions.

Sadly, Python, and so the ast module that we're using, doesn't allow adding truly custom operators, such as 'crosses_above'. However. Fear not! We can be sneaky.

The BitXor operator '^' is not tooooo bad looking, so we can overload it:

def custop(x, y):
    ''' Overload the ^ BitXor operator to fold functions into inline calls '''
    if callable(x):
        return x(y)
    elif callable(y):
        return lambda z:y(x,z)
    else:
        return x ^ y

s.operators[ast.BitXor] = custop

So now, we can use it to join expressions/function/expression phrases together:

x ^func^ y

gets transformed into:

func(x,y)

(via a little lambda indirection).

Here's a working example:

Custom Operators

You could possibly figure out another way to do it using '.' attribute access to compose things together... I don't know if it would work much better though.

Does that help / make sense?

@danthedeckie danthedeckie self-assigned this Feb 9, 2017
@danthedeckie
Copy link
Owner

danthedeckie commented Feb 9, 2017

Oh, and I had another thought...
If you then want to get rid of the ^func^ Xor wings, since we're now in plain text land, you can use a string .replace to remove them. I've added that to the Example link from above too.

I'm playing with the default settings now... it does make sense to have some kind of append_defaults option, and I think realistically it should be the actual default too. Once I've written tests, and a few extras, I'll do that. It's in the dev branch now. Thanks for raising this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants