Skip to content

Expression Serialization

Grammar-based serialization converts expressions to/from compact string representations — useful for URL query strings, API parameters, and storing filters as text.

Grammar Reference

Python Operator Grammar Syntax Example
& (AND) ; age>18;status=="active"
\| (OR) , status=="active",status=="pending"
~ (NOT) ! !(age<18)
== == age==18
!= != status!="inactive"
< < age<65
<= <= age<=65
> > age>18
>= >= age>=18
.is_in() =in= status=in=("active","pending")
.matches() ~regex email~regex(".*@example\\.com")
.is_null() ==null deleted_at==null
.is_not_null() !=null created_at!=null
TRUE true() true()
FALSE false() false()

Precedence: ! (NOT) > ; (AND) > , (OR)

Basic Usage

from therismos import F, Serializer, Eq, AllExpr, Gt

serializer = Serializer()

expr = Eq(F("age"), 18)
text = serializer.serialize(expr)
# "age==18"

expr = AllExpr(Eq(F("age"), 18), Gt(F("score"), 75))
text = serializer.serialize(expr)
# "(age==18;score>75)"

expr = serializer.deserialize("age==18")
# Eq(field=Field(name='age', type_=None), value=18)

URL Encoding

serializer = Serializer(url_encode=True)
expr = Eq(F("name"), "Alice Smith")
text = serializer.serialize(expr)   # URL-encoded string
expr = serializer.deserialize(text) # automatically decoded

Type Annotations

age = F("age", int)

# Without annotations (default)
serializer = Serializer()
text = serializer.serialize(Eq(age, 18))
# "age==18"

# With all annotations
serializer = Serializer(include_all_types=True)
text = serializer.serialize(Eq(age, 18))
# "age{int}==18"

Custom Types

import uuid
from therismos import Serializer

serializer = Serializer()
serializer.register_custom_type(uuid.UUID, 'uuid.UUID')

expr = serializer.deserialize('user_id{uuid.UUID}=="550e8400-e29b-41d4-a716-446655440000"')
assert isinstance(expr.value, uuid.UUID)

Implicit Field Types

Define type mappings for field names to avoid repeating annotations:

from decimal import Decimal

implicit_field_types = {
    "user_id": uuid.UUID,
    "price": Decimal,
}

serializer = Serializer(implicit_field_types=implicit_field_types)
serializer.register_custom_type(uuid.UUID, 'uuid.UUID')
serializer.register_custom_type(Decimal, 'Decimal')

expr = serializer.deserialize('user_id=="550e8400-e29b-41d4-a716-446655440000"')
assert expr.field.type_ is uuid.UUID

# Programmatic registration
serializer.register_field_type("account_id", uuid.UUID)

# Explicit annotations always override implicit mappings
expr = serializer.deserialize('price{int}=="100"')
assert expr.field.type_ is int  # not Decimal

Value Reference

Value type Syntax
String "Alice" (double-quoted, JSON escapes)
Integer 25
Float 3.14
Boolean true / false
Null null
Identifier active (unquoted, interpreted as string)

Roundtrip Example

from therismos import F, Serializer, optimize

expr = (F("age") >= 21) & (F("status").is_in("active", "pending"))
optimized, _ = optimize(expr)

serializer = Serializer(url_encode=True)
query_param = serializer.serialize(optimized)

received_expr = serializer.deserialize(query_param)