Skip to content

Commit

Permalink
Merge pull request #59 from arpeely/support-in-operator
Browse files Browse the repository at this point in the history
Add IN operator
  • Loading branch information
cansadadeserfeliz authored Aug 14, 2020
2 parents 96dee23 + a71daca commit fffc6d3
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 26 deletions.
50 changes: 27 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,33 +89,34 @@ Out: '(-1.0+x)'

### Available operators, constants and functions

| Expression | Example | Output
| ---------- | ------- | ------
| + | ``parser.parse('2 + 2').evaluate({})`` | 4
| - | ``parser.parse('3 - 1').evaluate({})`` | 2
| `*` | ``parser.parse('2 * 3').evaluate({})`` | 6
| / | ``parser.parse('5 / 2').evaluate({})`` | 2.5
| % | ``parser.parse('5 % 2').evaluate({})`` | 1
| ^ | ``parser.parse('5 ^ 2').evaluate({})`` | 25.0
| PI | ``parser.parse('PI').evaluate({})`` | 3.141592653589793
| E | ``parser.parse('E').evaluate({})`` | 2.718281828459045
| sin(x) | ``parser.parse('sin(0)').evaluate({})`` | 0.0
| cos(x) | ``parser.parse('cos(PI)').evaluate({})`` | - 1.0
| tan(x) | ``parser.parse('tan(0)').evaluate({})`` | 0.0
| Expression | Example | Output
| ---------- | ------- | ------
| + | ``parser.parse('2 + 2').evaluate({})`` | 4
| - | ``parser.parse('3 - 1').evaluate({})`` | 2
| `*` | ``parser.parse('2 * 3').evaluate({})`` | 6
| / | ``parser.parse('5 / 2').evaluate({})`` | 2.5
| % | ``parser.parse('5 % 2').evaluate({})`` | 1
| ^ | ``parser.parse('5 ^ 2').evaluate({})`` | 25.0
| PI | ``parser.parse('PI').evaluate({})`` | 3.141592653589793
| E | ``parser.parse('E').evaluate({})`` | 2.718281828459045
| sin(x) | ``parser.parse('sin(0)').evaluate({})`` | 0.0
| cos(x) | ``parser.parse('cos(PI)').evaluate({})`` | - 1.0
| tan(x) | ``parser.parse('tan(0)').evaluate({})`` | 0.0
| asin(x) | ``parser.parse('asin(0)').evaluate({})`` | 0.0
| acos(x) | ``parser.parse('acos(-1)').evaluate({})`` | 3.141592653589793
| atan(x) | ``parser.parse('atan(PI)').evaluate({})`` | 1.2626272556789118
| log(x) | ``parser.parse('log(1)').evaluate({})`` | 0.0
| log(x, base) | ``parser.parse('log(16, 2)').evaluate({})`` | 4.0
| abs(x) | ``parser.parse('abs(-1)').evaluate({})`` | 1
| ceil(x) | ``parser.parse('ceil(2.7)').evaluate({})`` | 3.0
| atan(x) | ``parser.parse('atan(PI)').evaluate({})`` | 1.2626272556789118
| log(x) | ``parser.parse('log(1)').evaluate({})`` | 0.0
| log(x, base)| ``parser.parse('log(16, 2)').evaluate({})`` | 4.0
| abs(x) | ``parser.parse('abs(-1)').evaluate({})`` | 1
| ceil(x) | ``parser.parse('ceil(2.7)').evaluate({})`` | 3.0
| floor(x) | ``parser.parse('floor(2.7)').evaluate({})`` | 2.0
| round(x) | ``parser.parse('round(2.7)').evaluate({})`` | 3.0
| exp(x) | ``parser.parse('exp(2)').evaluate({})`` | 7.38905609893065
| and | ``parser.parse('a and b').evaluate({'a':True, 'b':True})`` | True
| or | ``parser.parse('a or b').evaluate({'a':True, 'b':True})`` | True
| xor | ``parser.parse('a xor b').evaluate({'a':True, 'b':True})`` | False
| not | ``parser.parse('a and not b').evaluate({'a':True, 'b':True})`` | False
| exp(x) | ``parser.parse('exp(2)').evaluate({})`` | 7.38905609893065
| and | ``parser.parse('a and b').evaluate({'a':True, 'b':True})`` | True
| or | ``parser.parse('a or b').evaluate({'a':True, 'b':True})`` | True
| xor | ``parser.parse('a xor b').evaluate({'a':True, 'b':True})`` | False
| not | ``parser.parse('a and not b').evaluate({'a':True, 'b':True})`` | False
| in | ``parser.parse('1 in (1,2,3)').evaluate({})`` | True

## Examples

Expand Down Expand Up @@ -188,6 +189,9 @@ parser.parse('log(E)').evaluate({}) # 1.0
parser.parse('cos(PI)').evaluate({}) # -1.0

parser.parse('x||y').evaluate({'x': 2, 'y': 3}) # '23'

parser.parse('num in (1,2,3)').evaluate({'num': 1}) # True
parser.parse('"word" in "word in sentence"').evaluate({}) # True
```

## Upload package to PyPi
Expand Down
7 changes: 6 additions & 1 deletion py_expression_eval/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,9 @@ def orOperator (self, a, b ):
def xorOperator (self, a, b ):
return ( a ^ b )

def inOperator(self, a, b):
return a in b

def notOperator(self, a):
return not a

Expand Down Expand Up @@ -382,6 +385,7 @@ def __init__(self):
"and": self.andOperator,
"or": self.orOperator,
"xor": self.xorOperator,
"in": self.inOperator,
"D": self.roll
}

Expand Down Expand Up @@ -552,7 +556,7 @@ def evaluate(self, expr, variables):

def error_parsing(self, column, msg):
self.success = False
self.errormsg = 'parse error [column ' + str(column) + ']: ' + msg
self.errormsg = 'parse error [column ' + str(column) + ']: ' + msg + ', expression: ' + self.expression
raise Exception(self.errormsg)

def addfunc(self, tokenstack, operstack, type_):
Expand Down Expand Up @@ -706,6 +710,7 @@ def isOperator(self):
('>=', 3, '>='),
('<', 3, '<'),
('>', 3, '>'),
('in', 3, 'in'),
('not ', 2, 'not'),
('and ', 1, 'and'),
('xor ', 0, 'xor'),
Expand Down
11 changes: 9 additions & 2 deletions py_expression_eval/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,13 @@ def test_parser(self):
# check precedents: AND should evaluate before OR
self.assertExactEqual(parser.parse('a or b and not a').evaluate({'a': True, 'b': False}), True)

# in operations
self.assertExactEqual(parser.parse('"ab" in ("ab", "cd")').evaluate({}), True)
self.assertExactEqual(parser.parse('"ee" in ("ab", "cd")').evaluate({}), False)
self.assertExactEqual(parser.parse('1 in (1, 2, 3)').evaluate({}), True)
self.assertExactEqual(parser.parse('"ab" in ("ab", "cd") and 1 in (1,2,3)').evaluate({}), True)
self.assertExactEqual(parser.parse('"word" in "word in sentence"').evaluate({}), True)

# functions
self.assertExactEqual(parser.parse('pyt(2 , 0)').evaluate({}), 2.0)
self.assertEqual(parser.parse("concat('Hello',' ','world')").evaluate({}), 'Hello world')
Expand Down Expand Up @@ -200,8 +207,8 @@ def count(increment):
self.assertEqual(parser.parse("mean(xs)").variables(), ["xs"])
self.assertEqual(parser.parse("mean(xs)").symbols(), ["mean", "xs"])
self.assertEqual(parser.evaluate("mean(xs)", variables={"xs": [1, 2, 3]}), 2)
self.assertExactEqual(parser.evaluate("count(inc)", variables={"inc": 5}), 5)
self.assertExactEqual(parser.evaluate("count(inc)", variables={"inc": 5}), 10)
self.assertExactEqual(parser.evaluate("count(num)", variables={"num": 5}), 5)
self.assertExactEqual(parser.evaluate("count(num)", variables={"num": 5}), 10)

def test_custom_functions_with_inline_strings(self):
parser = Parser()
Expand Down

0 comments on commit fffc6d3

Please sign in to comment.