illogical


Nameillogical JSON
Version 1.0.2 PyPI version JSON
download
home_pageNone
SummaryA micro conditional engine used to parse raw logical and comparison expressions, evaluate the expression in the given data context, and provide access to a text form of the given expressions.
upload_time2024-08-13 23:18:31
maintainerNone
docs_urlNone
authorNone
requires_python>=3.7
licenseCopyright (c) 2023, David Horak (info@davidhorak.com) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
keywords expression logical expression-evaluator
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # (py)illogical

A micro conditional engine used to parse the logical and comparison expressions, evaluate an expression in data context, and provide access to a text form of the given expression.

[![codecov](https://codecov.io/gh/spaceavocado/pyillogical/branch/master/graph/badge.svg?token=NC8CX62BMC)](https://codecov.io/gh/spaceavocado/pyillogical)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)​

> Revision: Mar 10, 2023.

Other implementations:
- [TS/JS](https://github.com/spaceavocado/illogical)
- [GO](https://github.com/spaceavocado/goillogical)
- [C#](https://github.com/spaceavocado/cillogical)

## About

This project has been developed to provide Python implementation of [spaceavocado/illogical](https://github.com/spaceavocado/illogical).


## Getting Started

You can install the **(py)illogical** from [PyPI](https://pypi.org/project/illogical/):

```sh
python -m pip install illogical
```

The reader is supported on Python 3.7 and above.

**Table of Content**

---

- [(py)illogical](#pyillogical)
  - [About](#about)
  - [Getting Started](#getting-started)
  - [Basic Usage](#basic-usage)
    - [Evaluate](#evaluate)
    - [Statement](#statement)
    - [Parse](#parse)
    - [Evaluable](#evaluable)
      - [Simplify](#simplify)
      - [Serialize](#serialize)
  - [Working with Expressions](#working-with-expressions)
    - [Evaluation Data Context](#evaluation-data-context)
      - [Accessing Array Element:](#accessing-array-element)
      - [Accessing Array Element via Reference:](#accessing-array-element-via-reference)
      - [Nested Referencing](#nested-referencing)
      - [Composite Reference Key](#composite-reference-key)
      - [Data Type Casting](#data-type-casting)
    - [Operand Types](#operand-types)
      - [Value](#value)
      - [Reference](#reference)
      - [Collection](#collection)
    - [Comparison Expressions](#comparison-expressions)
      - [Equal](#equal)
      - [Not Equal](#not-equal)
      - [Greater Than](#greater-than)
      - [Greater Than or Equal](#greater-than-or-equal)
      - [Less Than](#less-than)
      - [Less Than or Equal](#less-than-or-equal)
      - [In](#in)
      - [Not In](#not-in)
      - [Prefix](#prefix)
      - [Suffix](#suffix)
      - [Overlap](#overlap)
      - [None](#none)
      - [Present](#present)
    - [Logical Expressions](#logical-expressions)
      - [And](#and)
      - [Or](#or)
      - [Nor](#nor)
      - [Xor](#xor)
      - [Not](#not)
  - [Engine Options](#engine-options)
    - [Reference Serialize Options](#reference-serialize-options)
      - [From](#from)
      - [To](#to)
    - [Collection Serialize Options](#collection-serialize-options)
      - [Escape Character](#escape-character)
    - [Simplify Options](#simplify-options)
      - [Ignored Paths](#ignored-paths)
      - [Ignored Paths RegEx](#ignored-paths-regex)
    - [Operator Mapping](#operator-mapping)
  - [Contributing](#contributing)
  - [License](#license)

---


## Basic Usage

```py
from illogical.illogical import Illogical

# Create a new instance of the engine
illogical = Illogical()

# Evaluate an expression
illogical.evaluate(["==", 1, 1], {})
```

> For advanced usage, please [Engine Options](#engine-options).

### Evaluate

Evaluate comparison or logical expression:

`illogical.evaluate(`[Comparison Expression](#comparison-expressions) or [Logical Expression](#logical-expressions), [Evaluation Data Context](#evaluation-data-context)`)` => `bool`

**Example**

```py
context = {
  "name": "peter",
}

# Comparison expression
illogical.evaluate(["==", 5, 5], context)
illogical.evaluate(["==", "circle", "circle"], context)
illogical.evaluate(["==", True, True], context)
illogical.evaluate(["==", "$name", "peter"], context)
illogical.evaluate(["NIL", "$RefA"], context)

# Logical expression
illogical.evaluate(["AND", ["==", 5, 5], ["==", 10, 10]], context)
illogical.evaluate(["AND", ["==", "circle", "circle"], ["==", 10, 10]], context)
illogical.evaluate(["OR", ["==", "$name", "peter"], ["==", 5, 10]], context)
```

### Statement

Get expression string representation:

`illogical.statement(`[Comparison Expression](#comparison-expressions) or [Logical Expression](#logical-expressions)`)` => `str`

**Example**

```py
# Comparison expression

illogical.statement(["==", 5, 5])
# (5 == 5)

illogical.statement(["==", "circle", "circle"])
# ("circle" == "circle")

illogical.statement(["==", True, True])
# (True == True)

illogical.statement(["==", "$name", "peter"])
# ({name} == "peter")

illogical.statement(["NIL", "$RefA"])
# ({RefA} <is nil>)

# Logical expression

illogical.statement(["AND", ["==", 5, 5], ["==", 10, 10]])
# ((5 == 5) AND (10 == 10))

illogical.statement(["AND", ["==", "circle", "circle"], ["==", 10, 10]])
# (("circle" == "circle") AND (10 == 10))

illogical.statement(["OR", ["==", "$name", "peter"], ["==", 5, 10]])
# (({name} == "peter") OR (5 == 10))
```

### Parse

Parse the expression into a **Evaluable** object, i.e. it returns the parsed self-evaluable condition expression.

`illogical.parse(`[Comparison Expression](#comparison-expressions) or [Logical Expression](#logical-expressions)`)` => `Evaluable`

### Evaluable

- `evaluable.evaluate(context)` please see [Evaluation Data Context](#evaluation-data-context).
- `evaluable.simplify(context)` please see [Simplify](#simplify).
- `evaluable.serialize()` please see [Serialize](#serialize).
- `str(evaluable) | evaluable.__str__()` please see [Statement](#statement).

**Example**

```py
evaluable = illogical.parse(["==", "$name", "peter"])

evaluable.evaluate({"name": "peter"})
# True

print(evaluable)
# ({name} == "peter")
```

#### Simplify

Simplifies an expression with a given context. This is useful when you already have some of
the properties of context and wants to try to evaluate the expression.

**Example**

```py
evaluable = illogical.parse(["AND", ["==", "$a", 10], ["==", "$b", 20]])

evaluable.simplify({"a": 10})
# ({b} == 20)

evaluable.simplify({"a": 20})
# False
```

Values not found in the context will cause the parent operand not to be evaluated and returned
as part of the simplified expression.

In some situations we might want to evaluate the expression even if referred value is not
present. You can provide a list of keys that will be strictly evaluated even if they are not
present in the context.

**Example**

```py
from illogical.illogical import Illogical
from illogical.parser.parse import Options

ignored_paths = ["ignored"],
ignored_path_rx = [r"^ignored"],

illogical = Illogical(Options(ignored_paths=ignored_paths, ignored_path_rx=ignored_path_rx))

evaluable = illogical.parse(["AND", ["==", "$a", 10], ["==", "$ignored", 20]])

evaluable.simplify({"a": 10})
# False
# $ignored" will be evaluated to None.
```

Alternatively we might want to do the opposite and strictly evaluate the expression for all referred
values not present in the context except for a specified list of optional keys.

**Example**

```py
from illogical.illogical import Illogical
from illogical.parser.parse import Options

ignored_paths = ["b"]

illogical = Illogical(Options(ignored_paths=ignored_paths))

evaluable = illogical.parse(["OR", ["==", "$a", 10], ["==", "$b", 20}, ["==", "$c", 20]])

evaluable.simplify({"c": 10})
# ({a} == 10)
# except for "$b" everything not in context will be evaluated to None.
```

#### Serialize

Serializes an expression into the raw expression form, reverse the parse operation.

**Example**

```py
evaluable = illogical.parse(["AND", ["==", "$a", 10], ["==", 10, 20]])

evaluable.serialize()
# ["AND", ["==", "$a", 10], ["==", 10, 20]]
```

## Working with Expressions

### Evaluation Data Context

The evaluation data context is used to provide the expression with variable references, i.e. this allows for the dynamic expressions. The data context is object with properties used as the references keys, and its values as reference values.

> Valid reference values: dist, str, int, float, list; set; tuple of (bool, string, int, float).

To reference the nested reference, please use "." delimiter, e.g.:
`$address.city`

#### Accessing Array Element:

`$options[1]`

#### Accessing Array Element via Reference:

`$options[{index}]`

- The **index** reference is resolved within the data context as an array index.

#### Nested Referencing

`$address.{segment}`

- The **segment** reference is resolved within the data context as a property key.

#### Composite Reference Key

`$shape{shapeType}`

- The **shapeType** reference is resolved within the data context, and inserted into the outer reference key.
- E.g. **shapeType** is resolved as "**B**" and would compose the **$shapeB** outer reference.
- This resolution could be n-nested.

#### Data Type Casting

`$payment.amount.(Type)`

Cast the given data context into the desired data type before being used as an operand in the evaluation.

> Note: If the conversion is invalid, then a warning message is being logged.

Supported data type conversions:

- .(String): cast a given reference to String.
- .(Number): cast a given reference to Number.
- .(Integer): cast a given reference to Integer.
- .(Float): cast a given reference to Float.
- .(Boolean): cast a given reference to Boolean.

**Example**

```py
# Data context
context = {
  "name":    "peter",
  "country": "canada",
  "age":     21,
  "options": [1, 2, 3],
  "address": {
    city:    "Toronto",
    country: "Canada",
  },
  "index":     2,
  "segment":   "city",
  "shapeA":    "box",
  "shapeB":    "circle",
  "shapeType": "B",
}

# Evaluate an expression in the given data context

illogical.evaluate([">", "$age", 20], context)
# True

illogical.evaluate(["==", "$address.city", "Toronto"], context)
# True

# Accessing Array Element
illogical.evaluate(["==", "$options[1]", 2], context)
# True

# Accessing Array Element via Reference
illogical.evaluate(["==", "$options[{index}]", 3], context)
# True

# Nested Referencing
illogical.evaluate(["==", "$address.{segment}", "Toronto"], context)
# True

# Composite Reference Key
illogical.evaluate(["==", "$shape{shapeType}", "circle"], context)
# True

# Data Type Casting
illogical.evaluate(["==", "$age.(String)", "21"], context)
# True
```

### Operand Types

The [Comparison Expression](#comparison-expression) expect operands to be one of the below:

#### Value

Simple value types: string, int, float, bool, None.

**Example**

```py
val1 = 5
var2 = "cirle"
var3 = True

illogical.parse(["AND", ["==", val1, var2], ["==", var3, var3]])
```

#### Reference

The reference operand value is resolved from the [Evaluation Data Context](#evaluation-data-context), where the the operands name is used as key in the context.

The reference operand must be prefixed with `$` symbol, e.g.: `$name`. This might be customized via [Reference Predicate Parser Option](#reference-predicate).

**Example**

| Expression                    | Data Context      |
| ----------------------------- | ----------------- |
| `["==", "$age", 21]`          | `{age: 21}`       |
| `["==", "circle", "$shape"] ` | `{shape: "circle"}` |
| `["==", "$visible", True]`    | `{visible: True}` |

#### Collection

The operand could be an array mixed from [Value](#value) and [Reference](#reference).

**Example**

| Expression                               | Data Context                        |
| ---------------------------------------- | ----------------------------------- |
| `["IN", [1, 2], 1]`                      | `{}`                                |
| `["IN", "circle", ["$shapeA", "$shapeB"] ` | `{shapeA: "circle", shapeB: "box"}` |
| `["IN", ["$number", 5], 5]`                | `{number: 3}`                       |

### Comparison Expressions

#### Equal

Expression format: `["==", `[Left Operand](#operand-types), [Right Operand](#operand-types)`]`.

> Valid operand types: string, int, float, bool, None.

```json
["==", 5, 5]
```

```py
illogical.evaluate(["==", 5, 5], context)
# True
```

#### Not Equal

Expression format: `["!=", `[Left Operand](#operand-types), [Right Operand](#operand-types)`]`.

> Valid operand types: string, int, float, bool, None.

```json
["!=", "circle", "square"]
```

```py
illogical.evaluate(["!=", "circle", "square"], context)
# True
```

#### Greater Than

Expression format: `[">", `[Left Operand](#operand-types), [Right Operand](#operand-types)`]`.

> Valid operand types: int, float.

```json
[">", 10, 5]
```

```py
illogical.evaluate([">", 10, 5], context)
# True
```

#### Greater Than or Equal

Expression format: `[">=", `[Left Operand](#operand-types), [Right Operand](#operand-types)`]`.

> Valid operand types: int, float.

```json
[">=", 5, 5]
```

```py
illogical.evaluate([">=", 5, 5], context)
# True
```

#### Less Than

Expression format: `["<", `[Left Operand](#operand-types), [Right Operand](#operand-types)`]`.

> Valid operand types: int, float.

```json
["<", 5, 10]
```

```py
illogical.evaluate(["<", 5, 10], context)
# True
```

#### Less Than or Equal

Expression format: `["<=", `[Left Operand](#operand-types), [Right Operand](#operand-types)`]`.

> Valid operand types: int, float.

```json
["<=", 5, 5]
```

```py
illogical.evaluate(["<=", 5, 5], context)
# True
```

#### In

Expression format: `["IN", `[Left Operand](#operand-types), [Right Operand](#operand-types)`]`.

> Valid operand types: string, int, float, bool, None and list; set; tuple of (string, int, float, bool, None).

```json
["IN", 5, [1, 2, 3, 4, 5]]
["IN", ["circle", "square", "triangle"], "square"]
```

```py
illogical.evaluate(["IN", 5, [1, 2, 3, 4, 5]], context)
# True

illogical.evaluate(["IN", ["circle", "square", "triangle"], "square"], context)
# True
```

#### Not In

Expression format: `["NOT IN", `[Left Operand](#operand-types), [Right Operand](#operand-types)`]`.

> Valid operand types: string, int, float, bool, None and list; set; tuple of (string, int, float, bool, None).

```json
["IN", 10, [1, 2, 3, 4, 5]]
["IN", ["circle", "square", "triangle"], "oval"]
```

```py
illogical.evaluate(["NOT IN", 10, [1, 2, 3, 4, 5]], context)
# True

illogical.evaluate(["NOT IN", ["circle", "square", "triangle"], "oval"], context)
# True
```

#### Prefix

Expression format: `["PREFIX", `[Left Operand](#operand-types), [Right Operand](#operand-types)`]`.

> Valid operand types: string.

- Left operand is the PREFIX term.
- Right operand is the tested word.

```json
["PREFIX", "hemi", "hemisphere"]
```

```py
illogical.evaluate(["PREFIX", "hemi", "hemisphere"], context)
# True

illogical.evaluate(["PREFIX", "hemi", "sphere"], context)
# False
```

#### Suffix

Expression format: `["SUFFIX", `[Left Operand](#operand-types), [Right Operand](#operand-types)`]`.

> Valid operand types: string.

- Left operand is the tested word.
- Right operand is the SUFFIX term.

```json
["SUFFIX", "establishment", "ment"]
```

```py
illogical.evaluate(["SUFFIX", "establishment", "ment"], context)
# True

illogical.evaluate(["SUFFIX", "establish", "ment"], context)
# False
```

#### Overlap

Expression format: `["OVERLAP", `[Left Operand](#operand-types), [Right Operand](#operand-types)`]`.

> Valid operand types: list; set; tuple of (string, int, float, bool, None).

```json
["OVERLAP", [1, 2], [1, 2, 3, 4, 5]]
["OVERLAP", ["circle", "square", "triangle"], ["square"]]
```

```py
illogical.evaluate(["OVERLAP", [1, 2, 6], [1, 2, 3, 4, 5]], context)
# True

illogical.evaluate(["OVERLAP", ["circle", "square", "triangle"], ["square", "oval"]], context)
# True
```

#### None

Expression format: `["NONE", `[Reference Operand](#reference)`]`.

```json
["NONE", "$RefA"]
```

```py
illogical.evaluate(["NONE", "RefA"], {})
# True

illogical.evaluate(["NONE", "RefA"], {"RefA": 10})
# False
```

#### Present

Evaluates as FALSE when the operand is UNDEFINED or NULL.

Expression format: `["PRESENT", `[Reference Operand](#reference)`]`.

```json
["PRESENT", "$RefA"]
```

```py
illogical.evaluate(["PRESENT", "RefA"], {})
# False

illogical.evaluate(["PRESENT", "RefA"], {"RefA": 10})
# True

illogical.evaluate(["PRESENT", "RefA"], {"RefA": False})
# True

illogical.evaluate(["PRESENT", "RefA"], {"RefA": "val"})
# True
```

### Logical Expressions

#### And

The logical AND operator returns the bool value TRUE if both operands are TRUE and returns FALSE otherwise.

Expression format: `["AND", Left Operand 1, Right Operand 2, ... , Right Operand N]`.

> Valid operand types: [Comparison Expression](#comparison-expressions) or [Nested Logical Expression](#logical-expressions).

```json
["AND", ["==", 5, 5], ["==", 10, 10]]
```

```py
illogical.evaluate(["AND", ["==", 5, 5], ["==", 10, 10]], context)
# True
```

#### Or

The logical OR operator returns the bool value TRUE if either or both operands is TRUE and returns FALSE otherwise.

Expression format: `["OR", Left Operand 1, Right Operand 2, ... , Right Operand N]`.

> Valid operand types: [Comparison Expression](#comparison-expressions) or [Nested Logical Expression](#logical-expressions).

```json
["OR", ["==", 5, 5], ["==", 10, 5]]
```

```py
illogical.evaluate(["OR", ["==", 5, 5], ["==", 10, 5]], context)
# True
```

#### Nor

The logical NOR operator returns the bool value TRUE if both operands are FALSE and returns FALSE otherwise.

Expression format: `["NOR", Left Operand 1, Right Operand 2, ... , Right Operand N]`

> Valid operand types: [Comparison Expression](#comparison-expressions) or [Nested Logical Expression](#logical-expressions).

```json
["NOR", ["==", 5, 1], ["==", 10, 5]]
```

```py
illogical.evaluate(["NOR", ["==", 5, 1], ["==", 10, 5]], context)
# True
```

#### Xor

The logical NOR operator returns the bool value TRUE if both operands are FALSE and returns FALSE otherwise.

Expression format: `["XOR", Left Operand 1, Right Operand 2, ... , Right Operand N]`

> Valid operand types: [Comparison Expression](#comparison-expressions) or [Nested Logical Expression](#logical-expressions).

```json
["XOR", ["==", 5, 5], ["==", 10, 5]]
```

```py
illogical.evaluate(["XOR", ["==", 5, 5], ["==", 10, 5]], context)
# True
```

```json
["XOR", ["==", 5, 5], ["==", 10, 10]]
```

```py
illogical.evaluate(["XOR", ["==", 5, 5], ["==", 10, 10]], context)
# False
```

#### Not

The logical NOT operator returns the bool value TRUE if the operand is FALSE, TRUE otherwise.

Expression format: `["NOT", Operand]`

> Valid operand types: [Comparison Expression](#comparison-expressions) or [Nested Logical Expression](#logical-expressions).

```json
["NOT", ["==", 5, 5]]
```

```py
illogical.evaluate(["NOT", ["==", 5, 5]], context)
# True
```

## Engine Options

### Reference Serialize Options

**Usage**

```py

from illogical.illogical import Illogical
from illogical.parser.parse import Options

illogical = Illogical(Options(reference_from=reference_from, reference_to=reference_to))
```

#### From

A function used to determine if the operand is a reference type, otherwise evaluated as a static value.

```py
Callable[[str], str]
```

**Return value:**

- `True` = reference type
- `False` = value type

**Default reference predicate:**

> The `$` symbol at the begging of the operand is used to predicate the reference type., E.g. `$State`, `$Country`.

#### To

A function used to transform the operand into the reference annotation stripped form. I.e. remove any annotation used to detect the reference type. E.g. "$Reference" => "Reference".

```py
Callable[[str], str]
```

> **Default reference transform:**
> It removes the `$` symbol at the begging of the operand name.

### Collection Serialize Options

**Usage**

```py
from illogical.illogical import Illogical
from illogical.parser.parse import Options

escape_character = "\\"

illogical = Illogical(Options(escape_character=escape_character))
```

#### Escape Character

Charter used to escape fist value within a collection, if the value contains operator value.

**Example**
- `["==", 1, 1]` # interpreted as EQ expression
- `["\==", 1, 1]` # interpreted as a collection

> **Default escape character:**
> `\`

### Simplify Options

Options applied while an expression is being simplified.

**Usage**

```py
from illogical.illogical import Illogical
from illogical.parser.parse import Options

ignored_paths = ["ignored"]
ignored_path_rx = [r"^prefix"]

illogical = Illogical(Options(ignored_paths=ignored_paths, ignored_path_rx=ignored_path_rx))
```

#### Ignored Paths

Reference paths which should be ignored while simplification is applied. Must be an exact match.

#### Ignored Paths RegEx

Reference paths which should be ignored while simplification is applied. Matching regular expression patterns.

### Operator Mapping

Mapping of the operators. The key is unique operator key, and the value is the key used to represent the given operator in the raw expression.

**Usage**

```py
from illogical.illogical import Illogical
from illogical.parser.parse import Options, DEFAULT_OPERATOR_MAPPING, EQ

operator_mapping = DEFAULT_OPERATOR_MAPPING.copy()
operator_mapping[EQ] = "IS"

illogical = Illogical(Options(operator_mapping=operator_mapping))
```

**Default operator mapping:**

```py
DEFAULT_OPERATOR_MAPPING = {
    # Logical
    AND:     "AND",
    OR:      "OR",
    NOR:     "NOR",
    XOR:     "XOR",
    NOT:     "NOT",
    # Comparison
    EQ:      "==",
    NE:      "!=",
    GT:      ">",
    GE:      ">=",
    LT:      "<",
    LE:      "<=",
    NONE:     "NONE",
    PRESENT: "PRESENT",
    IN:      "IN",
    NIN:     "NOT IN",
    OVERLAP: "OVERLAP",
    PREFIX:  "PREFIX",
    SUFFIX:  "SUFFIX",
}
```

---

## Contributing

See [contributing.md](https://github.com/spaceavocado/pyillogical/blob/master/contributing.md).

## License

Illogical is released under the MIT license. See [license.md](https://github.com/spaceavocado/pyillogical/blob/master/LICENSE.md).

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "illogical",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.7",
    "maintainer_email": null,
    "keywords": "expression, logical, expression-evaluator",
    "author": null,
    "author_email": "David Horak <info@davidhorak.com>",
    "download_url": "https://files.pythonhosted.org/packages/24/7b/c5234220069c1f90ad7b983d5ce0b1e36d91c79471e0c28ee921112c14fe/illogical-1.0.2.tar.gz",
    "platform": null,
    "description": "# (py)illogical\r\n\r\nA micro conditional engine used to parse the logical and comparison expressions, evaluate an expression in data context, and provide access to a text form of the given expression.\r\n\r\n[![codecov](https://codecov.io/gh/spaceavocado/pyillogical/branch/master/graph/badge.svg?token=NC8CX62BMC)](https://codecov.io/gh/spaceavocado/pyillogical)\r\n[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)\u200b\r\n\r\n> Revision: Mar 10, 2023.\r\n\r\nOther implementations:\r\n- [TS/JS](https://github.com/spaceavocado/illogical)\r\n- [GO](https://github.com/spaceavocado/goillogical)\r\n- [C#](https://github.com/spaceavocado/cillogical)\r\n\r\n## About\r\n\r\nThis project has been developed to provide Python implementation of [spaceavocado/illogical](https://github.com/spaceavocado/illogical).\r\n\r\n\r\n## Getting Started\r\n\r\nYou can install the **(py)illogical** from [PyPI](https://pypi.org/project/illogical/):\r\n\r\n```sh\r\npython -m pip install illogical\r\n```\r\n\r\nThe reader is supported on Python 3.7 and above.\r\n\r\n**Table of Content**\r\n\r\n---\r\n\r\n- [(py)illogical](#pyillogical)\r\n  - [About](#about)\r\n  - [Getting Started](#getting-started)\r\n  - [Basic Usage](#basic-usage)\r\n    - [Evaluate](#evaluate)\r\n    - [Statement](#statement)\r\n    - [Parse](#parse)\r\n    - [Evaluable](#evaluable)\r\n      - [Simplify](#simplify)\r\n      - [Serialize](#serialize)\r\n  - [Working with Expressions](#working-with-expressions)\r\n    - [Evaluation Data Context](#evaluation-data-context)\r\n      - [Accessing Array Element:](#accessing-array-element)\r\n      - [Accessing Array Element via Reference:](#accessing-array-element-via-reference)\r\n      - [Nested Referencing](#nested-referencing)\r\n      - [Composite Reference Key](#composite-reference-key)\r\n      - [Data Type Casting](#data-type-casting)\r\n    - [Operand Types](#operand-types)\r\n      - [Value](#value)\r\n      - [Reference](#reference)\r\n      - [Collection](#collection)\r\n    - [Comparison Expressions](#comparison-expressions)\r\n      - [Equal](#equal)\r\n      - [Not Equal](#not-equal)\r\n      - [Greater Than](#greater-than)\r\n      - [Greater Than or Equal](#greater-than-or-equal)\r\n      - [Less Than](#less-than)\r\n      - [Less Than or Equal](#less-than-or-equal)\r\n      - [In](#in)\r\n      - [Not In](#not-in)\r\n      - [Prefix](#prefix)\r\n      - [Suffix](#suffix)\r\n      - [Overlap](#overlap)\r\n      - [None](#none)\r\n      - [Present](#present)\r\n    - [Logical Expressions](#logical-expressions)\r\n      - [And](#and)\r\n      - [Or](#or)\r\n      - [Nor](#nor)\r\n      - [Xor](#xor)\r\n      - [Not](#not)\r\n  - [Engine Options](#engine-options)\r\n    - [Reference Serialize Options](#reference-serialize-options)\r\n      - [From](#from)\r\n      - [To](#to)\r\n    - [Collection Serialize Options](#collection-serialize-options)\r\n      - [Escape Character](#escape-character)\r\n    - [Simplify Options](#simplify-options)\r\n      - [Ignored Paths](#ignored-paths)\r\n      - [Ignored Paths RegEx](#ignored-paths-regex)\r\n    - [Operator Mapping](#operator-mapping)\r\n  - [Contributing](#contributing)\r\n  - [License](#license)\r\n\r\n---\r\n\r\n\r\n## Basic Usage\r\n\r\n```py\r\nfrom illogical.illogical import Illogical\r\n\r\n# Create a new instance of the engine\r\nillogical = Illogical()\r\n\r\n# Evaluate an expression\r\nillogical.evaluate([\"==\", 1, 1], {})\r\n```\r\n\r\n> For advanced usage, please [Engine Options](#engine-options).\r\n\r\n### Evaluate\r\n\r\nEvaluate comparison or logical expression:\r\n\r\n`illogical.evaluate(`[Comparison Expression](#comparison-expressions) or [Logical Expression](#logical-expressions), [Evaluation Data Context](#evaluation-data-context)`)` => `bool`\r\n\r\n**Example**\r\n\r\n```py\r\ncontext = {\r\n  \"name\": \"peter\",\r\n}\r\n\r\n# Comparison expression\r\nillogical.evaluate([\"==\", 5, 5], context)\r\nillogical.evaluate([\"==\", \"circle\", \"circle\"], context)\r\nillogical.evaluate([\"==\", True, True], context)\r\nillogical.evaluate([\"==\", \"$name\", \"peter\"], context)\r\nillogical.evaluate([\"NIL\", \"$RefA\"], context)\r\n\r\n# Logical expression\r\nillogical.evaluate([\"AND\", [\"==\", 5, 5], [\"==\", 10, 10]], context)\r\nillogical.evaluate([\"AND\", [\"==\", \"circle\", \"circle\"], [\"==\", 10, 10]], context)\r\nillogical.evaluate([\"OR\", [\"==\", \"$name\", \"peter\"], [\"==\", 5, 10]], context)\r\n```\r\n\r\n### Statement\r\n\r\nGet expression string representation:\r\n\r\n`illogical.statement(`[Comparison Expression](#comparison-expressions) or [Logical Expression](#logical-expressions)`)` => `str`\r\n\r\n**Example**\r\n\r\n```py\r\n# Comparison expression\r\n\r\nillogical.statement([\"==\", 5, 5])\r\n# (5 == 5)\r\n\r\nillogical.statement([\"==\", \"circle\", \"circle\"])\r\n# (\"circle\" == \"circle\")\r\n\r\nillogical.statement([\"==\", True, True])\r\n# (True == True)\r\n\r\nillogical.statement([\"==\", \"$name\", \"peter\"])\r\n# ({name} == \"peter\")\r\n\r\nillogical.statement([\"NIL\", \"$RefA\"])\r\n# ({RefA} <is nil>)\r\n\r\n# Logical expression\r\n\r\nillogical.statement([\"AND\", [\"==\", 5, 5], [\"==\", 10, 10]])\r\n# ((5 == 5) AND (10 == 10))\r\n\r\nillogical.statement([\"AND\", [\"==\", \"circle\", \"circle\"], [\"==\", 10, 10]])\r\n# ((\"circle\" == \"circle\") AND (10 == 10))\r\n\r\nillogical.statement([\"OR\", [\"==\", \"$name\", \"peter\"], [\"==\", 5, 10]])\r\n# (({name} == \"peter\") OR (5 == 10))\r\n```\r\n\r\n### Parse\r\n\r\nParse the expression into a **Evaluable** object, i.e. it returns the parsed self-evaluable condition expression.\r\n\r\n`illogical.parse(`[Comparison Expression](#comparison-expressions) or [Logical Expression](#logical-expressions)`)` => `Evaluable`\r\n\r\n### Evaluable\r\n\r\n- `evaluable.evaluate(context)` please see [Evaluation Data Context](#evaluation-data-context).\r\n- `evaluable.simplify(context)` please see [Simplify](#simplify).\r\n- `evaluable.serialize()` please see [Serialize](#serialize).\r\n- `str(evaluable) | evaluable.__str__()` please see [Statement](#statement).\r\n\r\n**Example**\r\n\r\n```py\r\nevaluable = illogical.parse([\"==\", \"$name\", \"peter\"])\r\n\r\nevaluable.evaluate({\"name\": \"peter\"})\r\n# True\r\n\r\nprint(evaluable)\r\n# ({name} == \"peter\")\r\n```\r\n\r\n#### Simplify\r\n\r\nSimplifies an expression with a given context. This is useful when you already have some of\r\nthe properties of context and wants to try to evaluate the expression.\r\n\r\n**Example**\r\n\r\n```py\r\nevaluable = illogical.parse([\"AND\", [\"==\", \"$a\", 10], [\"==\", \"$b\", 20]])\r\n\r\nevaluable.simplify({\"a\": 10})\r\n# ({b} == 20)\r\n\r\nevaluable.simplify({\"a\": 20})\r\n# False\r\n```\r\n\r\nValues not found in the context will cause the parent operand not to be evaluated and returned\r\nas part of the simplified expression.\r\n\r\nIn some situations we might want to evaluate the expression even if referred value is not\r\npresent. You can provide a list of keys that will be strictly evaluated even if they are not\r\npresent in the context.\r\n\r\n**Example**\r\n\r\n```py\r\nfrom illogical.illogical import Illogical\r\nfrom illogical.parser.parse import Options\r\n\r\nignored_paths = [\"ignored\"],\r\nignored_path_rx = [r\"^ignored\"],\r\n\r\nillogical = Illogical(Options(ignored_paths=ignored_paths, ignored_path_rx=ignored_path_rx))\r\n\r\nevaluable = illogical.parse([\"AND\", [\"==\", \"$a\", 10], [\"==\", \"$ignored\", 20]])\r\n\r\nevaluable.simplify({\"a\": 10})\r\n# False\r\n# $ignored\" will be evaluated to None.\r\n```\r\n\r\nAlternatively we might want to do the opposite and strictly evaluate the expression for all referred\r\nvalues not present in the context except for a specified list of optional keys.\r\n\r\n**Example**\r\n\r\n```py\r\nfrom illogical.illogical import Illogical\r\nfrom illogical.parser.parse import Options\r\n\r\nignored_paths = [\"b\"]\r\n\r\nillogical = Illogical(Options(ignored_paths=ignored_paths))\r\n\r\nevaluable = illogical.parse([\"OR\", [\"==\", \"$a\", 10], [\"==\", \"$b\", 20}, [\"==\", \"$c\", 20]])\r\n\r\nevaluable.simplify({\"c\": 10})\r\n# ({a} == 10)\r\n# except for \"$b\" everything not in context will be evaluated to None.\r\n```\r\n\r\n#### Serialize\r\n\r\nSerializes an expression into the raw expression form, reverse the parse operation.\r\n\r\n**Example**\r\n\r\n```py\r\nevaluable = illogical.parse([\"AND\", [\"==\", \"$a\", 10], [\"==\", 10, 20]])\r\n\r\nevaluable.serialize()\r\n# [\"AND\", [\"==\", \"$a\", 10], [\"==\", 10, 20]]\r\n```\r\n\r\n## Working with Expressions\r\n\r\n### Evaluation Data Context\r\n\r\nThe evaluation data context is used to provide the expression with variable references, i.e. this allows for the dynamic expressions. The data context is object with properties used as the references keys, and its values as reference values.\r\n\r\n> Valid reference values: dist, str, int, float, list; set; tuple of (bool, string, int, float).\r\n\r\nTo reference the nested reference, please use \".\" delimiter, e.g.:\r\n`$address.city`\r\n\r\n#### Accessing Array Element:\r\n\r\n`$options[1]`\r\n\r\n#### Accessing Array Element via Reference:\r\n\r\n`$options[{index}]`\r\n\r\n- The **index** reference is resolved within the data context as an array index.\r\n\r\n#### Nested Referencing\r\n\r\n`$address.{segment}`\r\n\r\n- The **segment** reference is resolved within the data context as a property key.\r\n\r\n#### Composite Reference Key\r\n\r\n`$shape{shapeType}`\r\n\r\n- The **shapeType** reference is resolved within the data context, and inserted into the outer reference key.\r\n- E.g. **shapeType** is resolved as \"**B**\" and would compose the **$shapeB** outer reference.\r\n- This resolution could be n-nested.\r\n\r\n#### Data Type Casting\r\n\r\n`$payment.amount.(Type)`\r\n\r\nCast the given data context into the desired data type before being used as an operand in the evaluation.\r\n\r\n> Note: If the conversion is invalid, then a warning message is being logged.\r\n\r\nSupported data type conversions:\r\n\r\n- .(String): cast a given reference to String.\r\n- .(Number): cast a given reference to Number.\r\n- .(Integer): cast a given reference to Integer.\r\n- .(Float): cast a given reference to Float.\r\n- .(Boolean): cast a given reference to Boolean.\r\n\r\n**Example**\r\n\r\n```py\r\n# Data context\r\ncontext = {\r\n  \"name\":    \"peter\",\r\n  \"country\": \"canada\",\r\n  \"age\":     21,\r\n  \"options\": [1, 2, 3],\r\n  \"address\": {\r\n    city:    \"Toronto\",\r\n    country: \"Canada\",\r\n  },\r\n  \"index\":     2,\r\n  \"segment\":   \"city\",\r\n  \"shapeA\":    \"box\",\r\n  \"shapeB\":    \"circle\",\r\n  \"shapeType\": \"B\",\r\n}\r\n\r\n# Evaluate an expression in the given data context\r\n\r\nillogical.evaluate([\">\", \"$age\", 20], context)\r\n# True\r\n\r\nillogical.evaluate([\"==\", \"$address.city\", \"Toronto\"], context)\r\n# True\r\n\r\n# Accessing Array Element\r\nillogical.evaluate([\"==\", \"$options[1]\", 2], context)\r\n# True\r\n\r\n# Accessing Array Element via Reference\r\nillogical.evaluate([\"==\", \"$options[{index}]\", 3], context)\r\n# True\r\n\r\n# Nested Referencing\r\nillogical.evaluate([\"==\", \"$address.{segment}\", \"Toronto\"], context)\r\n# True\r\n\r\n# Composite Reference Key\r\nillogical.evaluate([\"==\", \"$shape{shapeType}\", \"circle\"], context)\r\n# True\r\n\r\n# Data Type Casting\r\nillogical.evaluate([\"==\", \"$age.(String)\", \"21\"], context)\r\n# True\r\n```\r\n\r\n### Operand Types\r\n\r\nThe [Comparison Expression](#comparison-expression) expect operands to be one of the below:\r\n\r\n#### Value\r\n\r\nSimple value types: string, int, float, bool, None.\r\n\r\n**Example**\r\n\r\n```py\r\nval1 = 5\r\nvar2 = \"cirle\"\r\nvar3 = True\r\n\r\nillogical.parse([\"AND\", [\"==\", val1, var2], [\"==\", var3, var3]])\r\n```\r\n\r\n#### Reference\r\n\r\nThe reference operand value is resolved from the [Evaluation Data Context](#evaluation-data-context), where the the operands name is used as key in the context.\r\n\r\nThe reference operand must be prefixed with `$` symbol, e.g.: `$name`. This might be customized via [Reference Predicate Parser Option](#reference-predicate).\r\n\r\n**Example**\r\n\r\n| Expression                    | Data Context      |\r\n| ----------------------------- | ----------------- |\r\n| `[\"==\", \"$age\", 21]`          | `{age: 21}`       |\r\n| `[\"==\", \"circle\", \"$shape\"] ` | `{shape: \"circle\"}` |\r\n| `[\"==\", \"$visible\", True]`    | `{visible: True}` |\r\n\r\n#### Collection\r\n\r\nThe operand could be an array mixed from [Value](#value) and [Reference](#reference).\r\n\r\n**Example**\r\n\r\n| Expression                               | Data Context                        |\r\n| ---------------------------------------- | ----------------------------------- |\r\n| `[\"IN\", [1, 2], 1]`                      | `{}`                                |\r\n| `[\"IN\", \"circle\", [\"$shapeA\", \"$shapeB\"] ` | `{shapeA: \"circle\", shapeB: \"box\"}` |\r\n| `[\"IN\", [\"$number\", 5], 5]`                | `{number: 3}`                       |\r\n\r\n### Comparison Expressions\r\n\r\n#### Equal\r\n\r\nExpression format: `[\"==\", `[Left Operand](#operand-types), [Right Operand](#operand-types)`]`.\r\n\r\n> Valid operand types: string, int, float, bool, None.\r\n\r\n```json\r\n[\"==\", 5, 5]\r\n```\r\n\r\n```py\r\nillogical.evaluate([\"==\", 5, 5], context)\r\n# True\r\n```\r\n\r\n#### Not Equal\r\n\r\nExpression format: `[\"!=\", `[Left Operand](#operand-types), [Right Operand](#operand-types)`]`.\r\n\r\n> Valid operand types: string, int, float, bool, None.\r\n\r\n```json\r\n[\"!=\", \"circle\", \"square\"]\r\n```\r\n\r\n```py\r\nillogical.evaluate([\"!=\", \"circle\", \"square\"], context)\r\n# True\r\n```\r\n\r\n#### Greater Than\r\n\r\nExpression format: `[\">\", `[Left Operand](#operand-types), [Right Operand](#operand-types)`]`.\r\n\r\n> Valid operand types: int, float.\r\n\r\n```json\r\n[\">\", 10, 5]\r\n```\r\n\r\n```py\r\nillogical.evaluate([\">\", 10, 5], context)\r\n# True\r\n```\r\n\r\n#### Greater Than or Equal\r\n\r\nExpression format: `[\">=\", `[Left Operand](#operand-types), [Right Operand](#operand-types)`]`.\r\n\r\n> Valid operand types: int, float.\r\n\r\n```json\r\n[\">=\", 5, 5]\r\n```\r\n\r\n```py\r\nillogical.evaluate([\">=\", 5, 5], context)\r\n# True\r\n```\r\n\r\n#### Less Than\r\n\r\nExpression format: `[\"<\", `[Left Operand](#operand-types), [Right Operand](#operand-types)`]`.\r\n\r\n> Valid operand types: int, float.\r\n\r\n```json\r\n[\"<\", 5, 10]\r\n```\r\n\r\n```py\r\nillogical.evaluate([\"<\", 5, 10], context)\r\n# True\r\n```\r\n\r\n#### Less Than or Equal\r\n\r\nExpression format: `[\"<=\", `[Left Operand](#operand-types), [Right Operand](#operand-types)`]`.\r\n\r\n> Valid operand types: int, float.\r\n\r\n```json\r\n[\"<=\", 5, 5]\r\n```\r\n\r\n```py\r\nillogical.evaluate([\"<=\", 5, 5], context)\r\n# True\r\n```\r\n\r\n#### In\r\n\r\nExpression format: `[\"IN\", `[Left Operand](#operand-types), [Right Operand](#operand-types)`]`.\r\n\r\n> Valid operand types: string, int, float, bool, None and list; set; tuple of (string, int, float, bool, None).\r\n\r\n```json\r\n[\"IN\", 5, [1, 2, 3, 4, 5]]\r\n[\"IN\", [\"circle\", \"square\", \"triangle\"], \"square\"]\r\n```\r\n\r\n```py\r\nillogical.evaluate([\"IN\", 5, [1, 2, 3, 4, 5]], context)\r\n# True\r\n\r\nillogical.evaluate([\"IN\", [\"circle\", \"square\", \"triangle\"], \"square\"], context)\r\n# True\r\n```\r\n\r\n#### Not In\r\n\r\nExpression format: `[\"NOT IN\", `[Left Operand](#operand-types), [Right Operand](#operand-types)`]`.\r\n\r\n> Valid operand types: string, int, float, bool, None and list; set; tuple of (string, int, float, bool, None).\r\n\r\n```json\r\n[\"IN\", 10, [1, 2, 3, 4, 5]]\r\n[\"IN\", [\"circle\", \"square\", \"triangle\"], \"oval\"]\r\n```\r\n\r\n```py\r\nillogical.evaluate([\"NOT IN\", 10, [1, 2, 3, 4, 5]], context)\r\n# True\r\n\r\nillogical.evaluate([\"NOT IN\", [\"circle\", \"square\", \"triangle\"], \"oval\"], context)\r\n# True\r\n```\r\n\r\n#### Prefix\r\n\r\nExpression format: `[\"PREFIX\", `[Left Operand](#operand-types), [Right Operand](#operand-types)`]`.\r\n\r\n> Valid operand types: string.\r\n\r\n- Left operand is the PREFIX term.\r\n- Right operand is the tested word.\r\n\r\n```json\r\n[\"PREFIX\", \"hemi\", \"hemisphere\"]\r\n```\r\n\r\n```py\r\nillogical.evaluate([\"PREFIX\", \"hemi\", \"hemisphere\"], context)\r\n# True\r\n\r\nillogical.evaluate([\"PREFIX\", \"hemi\", \"sphere\"], context)\r\n# False\r\n```\r\n\r\n#### Suffix\r\n\r\nExpression format: `[\"SUFFIX\", `[Left Operand](#operand-types), [Right Operand](#operand-types)`]`.\r\n\r\n> Valid operand types: string.\r\n\r\n- Left operand is the tested word.\r\n- Right operand is the SUFFIX term.\r\n\r\n```json\r\n[\"SUFFIX\", \"establishment\", \"ment\"]\r\n```\r\n\r\n```py\r\nillogical.evaluate([\"SUFFIX\", \"establishment\", \"ment\"], context)\r\n# True\r\n\r\nillogical.evaluate([\"SUFFIX\", \"establish\", \"ment\"], context)\r\n# False\r\n```\r\n\r\n#### Overlap\r\n\r\nExpression format: `[\"OVERLAP\", `[Left Operand](#operand-types), [Right Operand](#operand-types)`]`.\r\n\r\n> Valid operand types: list; set; tuple of (string, int, float, bool, None).\r\n\r\n```json\r\n[\"OVERLAP\", [1, 2], [1, 2, 3, 4, 5]]\r\n[\"OVERLAP\", [\"circle\", \"square\", \"triangle\"], [\"square\"]]\r\n```\r\n\r\n```py\r\nillogical.evaluate([\"OVERLAP\", [1, 2, 6], [1, 2, 3, 4, 5]], context)\r\n# True\r\n\r\nillogical.evaluate([\"OVERLAP\", [\"circle\", \"square\", \"triangle\"], [\"square\", \"oval\"]], context)\r\n# True\r\n```\r\n\r\n#### None\r\n\r\nExpression format: `[\"NONE\", `[Reference Operand](#reference)`]`.\r\n\r\n```json\r\n[\"NONE\", \"$RefA\"]\r\n```\r\n\r\n```py\r\nillogical.evaluate([\"NONE\", \"RefA\"], {})\r\n# True\r\n\r\nillogical.evaluate([\"NONE\", \"RefA\"], {\"RefA\": 10})\r\n# False\r\n```\r\n\r\n#### Present\r\n\r\nEvaluates as FALSE when the operand is UNDEFINED or NULL.\r\n\r\nExpression format: `[\"PRESENT\", `[Reference Operand](#reference)`]`.\r\n\r\n```json\r\n[\"PRESENT\", \"$RefA\"]\r\n```\r\n\r\n```py\r\nillogical.evaluate([\"PRESENT\", \"RefA\"], {})\r\n# False\r\n\r\nillogical.evaluate([\"PRESENT\", \"RefA\"], {\"RefA\": 10})\r\n# True\r\n\r\nillogical.evaluate([\"PRESENT\", \"RefA\"], {\"RefA\": False})\r\n# True\r\n\r\nillogical.evaluate([\"PRESENT\", \"RefA\"], {\"RefA\": \"val\"})\r\n# True\r\n```\r\n\r\n### Logical Expressions\r\n\r\n#### And\r\n\r\nThe logical AND operator returns the bool value TRUE if both operands are TRUE and returns FALSE otherwise.\r\n\r\nExpression format: `[\"AND\", Left Operand 1, Right Operand 2, ... , Right Operand N]`.\r\n\r\n> Valid operand types: [Comparison Expression](#comparison-expressions) or [Nested Logical Expression](#logical-expressions).\r\n\r\n```json\r\n[\"AND\", [\"==\", 5, 5], [\"==\", 10, 10]]\r\n```\r\n\r\n```py\r\nillogical.evaluate([\"AND\", [\"==\", 5, 5], [\"==\", 10, 10]], context)\r\n# True\r\n```\r\n\r\n#### Or\r\n\r\nThe logical OR operator returns the bool value TRUE if either or both operands is TRUE and returns FALSE otherwise.\r\n\r\nExpression format: `[\"OR\", Left Operand 1, Right Operand 2, ... , Right Operand N]`.\r\n\r\n> Valid operand types: [Comparison Expression](#comparison-expressions) or [Nested Logical Expression](#logical-expressions).\r\n\r\n```json\r\n[\"OR\", [\"==\", 5, 5], [\"==\", 10, 5]]\r\n```\r\n\r\n```py\r\nillogical.evaluate([\"OR\", [\"==\", 5, 5], [\"==\", 10, 5]], context)\r\n# True\r\n```\r\n\r\n#### Nor\r\n\r\nThe logical NOR operator returns the bool value TRUE if both operands are FALSE and returns FALSE otherwise.\r\n\r\nExpression format: `[\"NOR\", Left Operand 1, Right Operand 2, ... , Right Operand N]`\r\n\r\n> Valid operand types: [Comparison Expression](#comparison-expressions) or [Nested Logical Expression](#logical-expressions).\r\n\r\n```json\r\n[\"NOR\", [\"==\", 5, 1], [\"==\", 10, 5]]\r\n```\r\n\r\n```py\r\nillogical.evaluate([\"NOR\", [\"==\", 5, 1], [\"==\", 10, 5]], context)\r\n# True\r\n```\r\n\r\n#### Xor\r\n\r\nThe logical NOR operator returns the bool value TRUE if both operands are FALSE and returns FALSE otherwise.\r\n\r\nExpression format: `[\"XOR\", Left Operand 1, Right Operand 2, ... , Right Operand N]`\r\n\r\n> Valid operand types: [Comparison Expression](#comparison-expressions) or [Nested Logical Expression](#logical-expressions).\r\n\r\n```json\r\n[\"XOR\", [\"==\", 5, 5], [\"==\", 10, 5]]\r\n```\r\n\r\n```py\r\nillogical.evaluate([\"XOR\", [\"==\", 5, 5], [\"==\", 10, 5]], context)\r\n# True\r\n```\r\n\r\n```json\r\n[\"XOR\", [\"==\", 5, 5], [\"==\", 10, 10]]\r\n```\r\n\r\n```py\r\nillogical.evaluate([\"XOR\", [\"==\", 5, 5], [\"==\", 10, 10]], context)\r\n# False\r\n```\r\n\r\n#### Not\r\n\r\nThe logical NOT operator returns the bool value TRUE if the operand is FALSE, TRUE otherwise.\r\n\r\nExpression format: `[\"NOT\", Operand]`\r\n\r\n> Valid operand types: [Comparison Expression](#comparison-expressions) or [Nested Logical Expression](#logical-expressions).\r\n\r\n```json\r\n[\"NOT\", [\"==\", 5, 5]]\r\n```\r\n\r\n```py\r\nillogical.evaluate([\"NOT\", [\"==\", 5, 5]], context)\r\n# True\r\n```\r\n\r\n## Engine Options\r\n\r\n### Reference Serialize Options\r\n\r\n**Usage**\r\n\r\n```py\r\n\r\nfrom illogical.illogical import Illogical\r\nfrom illogical.parser.parse import Options\r\n\r\nillogical = Illogical(Options(reference_from=reference_from, reference_to=reference_to))\r\n```\r\n\r\n#### From\r\n\r\nA function used to determine if the operand is a reference type, otherwise evaluated as a static value.\r\n\r\n```py\r\nCallable[[str], str]\r\n```\r\n\r\n**Return value:**\r\n\r\n- `True` = reference type\r\n- `False` = value type\r\n\r\n**Default reference predicate:**\r\n\r\n> The `$` symbol at the begging of the operand is used to predicate the reference type., E.g. `$State`, `$Country`.\r\n\r\n#### To\r\n\r\nA function used to transform the operand into the reference annotation stripped form. I.e. remove any annotation used to detect the reference type. E.g. \"$Reference\" => \"Reference\".\r\n\r\n```py\r\nCallable[[str], str]\r\n```\r\n\r\n> **Default reference transform:**\r\n> It removes the `$` symbol at the begging of the operand name.\r\n\r\n### Collection Serialize Options\r\n\r\n**Usage**\r\n\r\n```py\r\nfrom illogical.illogical import Illogical\r\nfrom illogical.parser.parse import Options\r\n\r\nescape_character = \"\\\\\"\r\n\r\nillogical = Illogical(Options(escape_character=escape_character))\r\n```\r\n\r\n#### Escape Character\r\n\r\nCharter used to escape fist value within a collection, if the value contains operator value.\r\n\r\n**Example**\r\n- `[\"==\", 1, 1]` # interpreted as EQ expression\r\n- `[\"\\==\", 1, 1]` # interpreted as a collection\r\n\r\n> **Default escape character:**\r\n> `\\`\r\n\r\n### Simplify Options\r\n\r\nOptions applied while an expression is being simplified.\r\n\r\n**Usage**\r\n\r\n```py\r\nfrom illogical.illogical import Illogical\r\nfrom illogical.parser.parse import Options\r\n\r\nignored_paths = [\"ignored\"]\r\nignored_path_rx = [r\"^prefix\"]\r\n\r\nillogical = Illogical(Options(ignored_paths=ignored_paths, ignored_path_rx=ignored_path_rx))\r\n```\r\n\r\n#### Ignored Paths\r\n\r\nReference paths which should be ignored while simplification is applied. Must be an exact match.\r\n\r\n#### Ignored Paths RegEx\r\n\r\nReference paths which should be ignored while simplification is applied. Matching regular expression patterns.\r\n\r\n### Operator Mapping\r\n\r\nMapping of the operators. The key is unique operator key, and the value is the key used to represent the given operator in the raw expression.\r\n\r\n**Usage**\r\n\r\n```py\r\nfrom illogical.illogical import Illogical\r\nfrom illogical.parser.parse import Options, DEFAULT_OPERATOR_MAPPING, EQ\r\n\r\noperator_mapping = DEFAULT_OPERATOR_MAPPING.copy()\r\noperator_mapping[EQ] = \"IS\"\r\n\r\nillogical = Illogical(Options(operator_mapping=operator_mapping))\r\n```\r\n\r\n**Default operator mapping:**\r\n\r\n```py\r\nDEFAULT_OPERATOR_MAPPING = {\r\n    # Logical\r\n    AND:     \"AND\",\r\n    OR:      \"OR\",\r\n    NOR:     \"NOR\",\r\n    XOR:     \"XOR\",\r\n    NOT:     \"NOT\",\r\n    # Comparison\r\n    EQ:      \"==\",\r\n    NE:      \"!=\",\r\n    GT:      \">\",\r\n    GE:      \">=\",\r\n    LT:      \"<\",\r\n    LE:      \"<=\",\r\n    NONE:     \"NONE\",\r\n    PRESENT: \"PRESENT\",\r\n    IN:      \"IN\",\r\n    NIN:     \"NOT IN\",\r\n    OVERLAP: \"OVERLAP\",\r\n    PREFIX:  \"PREFIX\",\r\n    SUFFIX:  \"SUFFIX\",\r\n}\r\n```\r\n\r\n---\r\n\r\n## Contributing\r\n\r\nSee [contributing.md](https://github.com/spaceavocado/pyillogical/blob/master/contributing.md).\r\n\r\n## License\r\n\r\nIllogical is released under the MIT license. See [license.md](https://github.com/spaceavocado/pyillogical/blob/master/LICENSE.md).\r\n",
    "bugtrack_url": null,
    "license": "Copyright (c) 2023, David Horak (info@davidhorak.com)  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.",
    "summary": "A micro conditional engine used to parse raw logical and comparison expressions, evaluate the expression in the given data context, and provide access to a text form of the given expressions.",
    "version": "1.0.2",
    "project_urls": {
        "repository": "https://github.com/spaceavocado/pyillogical"
    },
    "split_keywords": [
        "expression",
        " logical",
        " expression-evaluator"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "755a89094db49a90dda326bdcd52cb745e29f927eec962b84cc66bf0094b2c2b",
                "md5": "c31aa8daf1bdc779b2c95f2d7ced1908",
                "sha256": "a813480be2a39d031a8ce1993a976bf05ba05d02b421296fc5990f1ca381ba49"
            },
            "downloads": -1,
            "filename": "illogical-1.0.2-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "c31aa8daf1bdc779b2c95f2d7ced1908",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.7",
            "size": 10033,
            "upload_time": "2024-08-13T23:18:29",
            "upload_time_iso_8601": "2024-08-13T23:18:29.676270Z",
            "url": "https://files.pythonhosted.org/packages/75/5a/89094db49a90dda326bdcd52cb745e29f927eec962b84cc66bf0094b2c2b/illogical-1.0.2-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "247bc5234220069c1f90ad7b983d5ce0b1e36d91c79471e0c28ee921112c14fe",
                "md5": "b42ae7d58bc18cdbf11e745896c09f87",
                "sha256": "7ad0d56e83c43fc17922ae4379a828be49a2c66c6942243fb5ec6931ce762fb7"
            },
            "downloads": -1,
            "filename": "illogical-1.0.2.tar.gz",
            "has_sig": false,
            "md5_digest": "b42ae7d58bc18cdbf11e745896c09f87",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.7",
            "size": 18033,
            "upload_time": "2024-08-13T23:18:31",
            "upload_time_iso_8601": "2024-08-13T23:18:31.751929Z",
            "url": "https://files.pythonhosted.org/packages/24/7b/c5234220069c1f90ad7b983d5ce0b1e36d91c79471e0c28ee921112c14fe/illogical-1.0.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-08-13 23:18:31",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "spaceavocado",
    "github_project": "pyillogical",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "requirements": [],
    "lcname": "illogical"
}
        
Elapsed time: 4.68269s