Skip to main content

Xpattern: Pattern Matching with XObject.

GitHub Tests PyPI version Downloads

xpattern is inspired by hask, pipetools, pampy, and with xpattern your code will be more readable and graceful!

Let's play now

Each pattern matchline evaluated in the order they appear.

from xpattern import caseof
from xpattern import m


~(caseof(v)
    | m(pattern_1) >> action_1
    | m(pattern_2) >> action_2
)

caseof is lazy, so you need to add ~ operator to run it!

m is a matcher, if you don't like circular brackets style, you can alse use square brackets style

from xpattern import caseof
from xpattern import m


~(caseof(v)
    | m[pattern_1] >> action_1
    | m[pattern_2] >> action_2
)

Write a Fibonacci

The operator _ means "match everything"

from xpattern import _
from xpattern import caseof
from xpattern import m


def fibonacci(n):
    return ~(caseof(n)
        | m(1) >> 1
        | m(2) >> 1
        | _ >> (lambda x: fibonacci(x - 1) + fibonacci(x - 2))
    )

Write a Lisp calculator

from functools import reduce

from xpattern import _
from xpattern import REST
from xpattern import caseof
from xpattern import m


def lisp(exp):
    return ~(caseof(exp)
        | m(int)            >> (lambda x: x)
        | m(callable)       >> (lambda x: x)
        | m(callable, REST) >> (lambda f, rest: f(*map(lisp, rest)))
        | m(tuple)          >> (lambda t: list(map(lisp, t)))
    )

plus = lambda a, b: a + b
minus = lambda a, b: a - b
lisp((plus, 1, 2))                  # => 3
lisp((plus, 1 (minus, 4, 2)))       # => 3
lisp((reduce, plus, (range, 10)))   # => 45

You can match so many things!

~(caseof(x)
    | m(3)         >> "this matches the number 3"
    | m(int)       >> "matches any integer"
    | m(str, int)  >> (lambda a, b: "a tuple (a, b) you can use in a function")
    | m(1, 2, _)   >> "any list of 3 elements that begins with [1, 2]"
    | m({"x", _})  >> "any dict with a key 'x' and any value associated"
    | _            >> "anything else"
)

Match dataclass

from dataclasses import dataclass

from xpattern import _
from xpattern import caseof
from xpattern import m


@dataclass
class Point:
    x: int
    y: int

@dataclass
class Point2:
    x: int
    y: int

@dataclass
class Line:
    p1: Point
    p2: Point

@dataclass
class Rect:
    l1: Line
    l2: Line

~(caseof(Rect(Point(1, 2), Point(3, 4)))
    | m(Rect(Point(_, str), Point(_, 4))) >> "first"
    | m(Rect(Point(_, int), Point2(_, 4))) >> "second"
    | m(Rect(Point(_, int), Point(_, 4))) >> (lambda x, y, z: (x, y, z))
)  # => (1, 2, 3)

Match [HEAD, TAIL]

from xpattern import _
from xpattern import HEAD
from xpattern import TAIL
from xpattern import caseof
from xpattern import m


x = [1, 2, 3]

~(caseof(x)
    | m(1, TAIL) >> (lambda t: t)             # => [2, 3]
)

~(caseof(x)
    | m(HEAD, TAIL) >> (lambda h, t: (h, t))  # => [1, [2, 3]]
)

Chain match

from xpattern import _
from xpattern import caseof
from xpattern import m


pet = {"type": "dog", "details": {"age": 3}}
~(caseof(pet)
    | m({_: {"age": _}}) >> ~(caseof(X)
        | m(int, int) >> (lambda x, y: x + y)
        | m(str, int) >> (lambda x, y: y)
    )
)  # => 3

With Bitwise Operators

from xpattern import _
from xpattern import caseof
from xpattern import m


~(caseof(1)
    | (m[2] | m[1]) >> 1
    | _ >> "nothing"
) # => 1

~(caseof(11)
    | (m[lambda x: x > 1] & m[lambda x: x < 10]) >> "1 < x < 10"
    | (m[lambda x: x > 1] & m[lambda x: x < 15]) >> "1 < x < 15"
) # => "1 < x < 15"

~(caseof(6)
    | ~m[lambda x: x > 5]  >> "x <=5"
    | ~m[lambda x: x > 10] >> "x <= 10"
) # => "x <= 10"

More pattern cases

Your can visit repo pampy get more pattern cases, xpattern is Syntactic Sugar of pampy

Why name Xpattern, what's X mean?

X means XObject !!!

XObject is a Syntactic Sugar of lambda function

from xpattern import X
from xpattern import caseof
from xpattern import m


~(caseof(1)
    | m(1) >> X + 1       # => 2
)

~(caseof("apple")
    | m(str) >> X.upper()  # => "APPLE"
)

~(caseof([1, 2, 3])
    | m(1, 2, 3) >> X[2]   # => 3
)

~(caseof([1, 2, 3])
    | m(1, 2, 3) >> X + [4, 5, 6]              # => [1, 2, 3, 4, 5, 6]
)

~(caseof(9)
    | m(int) >> X + X ** (X << 2) % 2 / 3 - X  # => 0.333333333
)

~(caseof(1)
    | m(int) >> X._in_([1, 2, 3])   # => True
)

~(caseof(lambda x, y: x + y)
    | m(callable) >> X(1, 2)        # => 3
)
Operation Syntax
Addition X + 1
Call X(a, b, c)
Concatenation X + [1, 2, 3]
Containment Test X._in_( [1, 2, 3] )
Contains X._contains_(1)
Division X / 2 or X // 2
Bitwise And X & 2
Bitwise Exclusive Or X ^ 2
Bitwise Inversion ~X
Bitwise Or X | 2
Exponentiation X ** 2
Identity X._is_(2)
Indexing X[k]
Left Shift X << 2
Modulo X % 2
Multiplication X * 2
Matrix Multiplication X @ matrix
Negation (Arithmetic) -X
Negation (Logical) X._not()
Positive +X
Right Shift X >> 2
Subtraction X - 2
Ordering X < 2 or X <= 2 or X > 2 or X >= 2
Equality X == 2
Difference X != 2

match pattern with XObject

from xpattern import X
from xpattern import caseof
from xpattern import m


~(caseof((1, 2, 3))
    | m(X[2] == 3) >> X[2] + 4  # => 7
)

# same as
~(caseof((1, 2, 3))
    | m(lambda x: x[2] == 3) >> (lambda x: x[2] + 4)  # => 7
)

xfunction

XObject only can represent as function like X(1, 2), but can not be as args in function func(X, X + 2)

Now you can try to use xfunction realizing it!

from xpattern import X
from xpattern import _
from xpattern import caseof
from xpattern import m
from xpattern import xfunction


@xfunction
def add(a, b):
    return a + b

# in actions
~(caseof(1)
    | m(int) >> add(X, X)                             # => 2
)

# recursion xfunction
~(caseof(2)
    | m(int) >> add(add(X + 1, 4), b=add(X * 9, 7))   # => 32
)


@xfunction
def greater_than_4(x):
    return x > 4


# in patterns
~(caseof(1)
    | m(greater_than_4(X + 5)) >> "greater than 4"
    | _ >> "equal or lesser than 4"
)  # => "greater than 4"

~(caseof(1)
    | m(greater_than_4(X)) >> "greater than 4"
    | _ >> "equal or lesser than 4"
)  # => "equal or lesser than 4"

Metadata

Release files for xpattern 0.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for xpattern 0.5.0
File Size Uploaded Provenance Actions
xpattern-0.5.0.tar.gz 10.5 kB Without provenance Details for xpattern-0.5.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for xpattern 0.5.0
File Interpreter ABI Platform Actions
xpattern-0.5.0-py3-none-any.whl Python 3 none any Details for xpattern-0.5.0-py3-none-any.whl

Total release size: 18.4 kB

Release files / xpattern-0.5.0.tar.gz

Download URL xpattern-0.5.0.tar.gz
Size 10.5 kB
Tags Source
SHA-256 checksum
How to use checksums
6d059c913b0b33674a4928846e871cc40d2c5713f1977fd09811d4de558e5127
BLAKE2b-256 checksum
How to use checksums
02a3e40c381388234a043615396c849ef85d5b5dbe408b63017129ecf0608e2a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/1.0.3 CPython/3.7.5 Linux/4.15.0-101-generic

Release files / xpattern-0.5.0-py3-none-any.whl

Download URL xpattern-0.5.0-py3-none-any.whl
Size 7.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ca7eb9958d7d1b8e3fbe31adc574352f326c833816e1d32715038eb95d792eeb
BLAKE2b-256 checksum
How to use checksums
7eafd7ccce15fb1c4b19674ac43d0616f4034ddee6a22248c7be6f820bf7f0be
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/1.0.3 CPython/3.7.5 Linux/4.15.0-101-generic

Security for xpattern 0.5.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page