Skip to content

bead.dsl

Domain-Specific Language for constraint expressions used in template slot filling and list partitioning.

Parser

parser

Constraint DSL parser.

This module provides the parser for constraint expressions using the Lark parsing library. The parser converts constraint strings into AST nodes.

ASTBuilder

Bases: Transformer

Transformer that converts Lark parse tree to AST nodes.

string_literal(items: list[Token]) -> ast.Literal

Transform string literal.

number_literal(items: list[Token]) -> ast.Literal

Transform number literal.

true_literal(items: list[Token]) -> ast.Literal

Transform true literal.

false_literal(items: list[Token]) -> ast.Literal

Transform false literal.

variable(items: list[Token]) -> ast.Variable

Transform variable reference.

binary_op(items: list[Token | ast.ASTNode]) -> ast.BinaryOp

Transform binary operation.

binary_op_not_in(items: list[Token | ast.ASTNode]) -> ast.BinaryOp

Transform 'not in' binary operation.

unary_op(items: list[Token | ast.ASTNode]) -> ast.UnaryOp

Transform unary operation.

attribute_access(items: list[Token | ast.ASTNode]) -> ast.AttributeAccess

Transform attribute access.

subscript(items: list[Token | ast.ASTNode]) -> ast.Subscript

Transform subscript access.

function_call(items: list[Token | ast.ASTNode | list[ast.ASTNode] | None]) -> ast.FunctionCall

Transform function call.

list_literal(items: list[Token | ast.ASTNode | list[ast.ASTNode] | None]) -> ast.ListLiteral

Transform list literal.

arguments(items: list[Token | ast.ASTNode]) -> list[ast.ASTNode]

Transform function arguments, returning flat list.

list_elements(items: list[Token | ast.ASTNode]) -> list[ast.ASTNode]

Transform list elements, returning flat list.

atom(items: list[Token | ast.ASTNode]) -> ast.ASTNode

Transform atom (handles parenthesized expressions).

parse(expression: str) -> ast.ASTNode

Parse constraint expression into AST.

Parameters:

Name Type Description Default
expression str

Constraint expression to parse.

required

Returns:

Type Description
ASTNode

Root node of the abstract syntax tree.

Raises:

Type Description
ParseError

If the expression cannot be parsed.

Examples:

>>> node = parse("pos == 'VERB'")
>>> isinstance(node, ast.BinaryOp)
True
>>> node.operator
'=='

Evaluator

evaluator

Constraint evaluator for DSL.

This module provides the Evaluator class that executes AST nodes against an evaluation context to produce boolean results, and the DSLEvaluator class that provides a high-level interface for evaluating constraint expressions.

Evaluator

Evaluator for constraint AST nodes.

The evaluator walks the AST and computes values based on the evaluation context. It supports: - All AST node types - Operator evaluation - Function calls - Attribute access - Caching for performance

Parameters:

Name Type Description Default
use_cache bool

Whether to cache evaluation results.

True

Examples:

>>> from bead.dsl.context import EvaluationContext
>>> from bead.dsl.parser import parse
>>> ctx = EvaluationContext()
>>> ctx.set_variable("x", 10)
>>> evaluator = Evaluator()
>>> node = parse("x > 5")
>>> evaluator.evaluate(node, ctx)
True

evaluate(node: ast.ASTNode, context: EvaluationContext) -> Any

Evaluate an AST node in the given context.

Parameters:

Name Type Description Default
node ASTNode

AST node to evaluate.

required
context EvaluationContext

Evaluation context with variables and functions.

required

Returns:

Type Description
Any

Result of evaluation.

Raises:

Type Description
EvaluationError

If evaluation fails (undefined variable, type error, etc.).

clear_cache() -> None

Clear evaluation cache.

Examples:

>>> evaluator = Evaluator()
>>> evaluator.clear_cache()

DSLEvaluator

High-level evaluator for DSL constraint expressions.

This class provides a simplified interface for evaluating constraint expressions. It handles:

  • Parsing expression strings to AST
  • Building evaluation contexts from dictionaries
  • Caching compiled ASTs
  • Registering standard library functions
  • Property extraction for list partitioning

The DSLEvaluator is the primary interface for constraint evaluation in the bead package. It wraps the lower-level Evaluator class.

Attributes:

Name Type Description
evaluator Evaluator

The underlying AST evaluator instance.

compiled_cache dict[str, ASTNode]

Cache mapping expression strings to their compiled AST nodes.

Examples:

>>> from bead.resources.items import LexicalItem
>>> evaluator = DSLEvaluator()
>>> item = LexicalItem(lemma="walk", pos="VERB")
>>> evaluator.evaluate(
...     "self.pos == 'VERB'",
...     {"self": item}
... )
True
>>> evaluator.evaluate(
...     "self.lemma in motion_verbs",
...     {"self": item, "motion_verbs": {"walk", "run", "jump"}}
... )
True

evaluate(expression: str, context: dict[str, ContextValue | LexicalItem | FilledTemplate | Item]) -> bool | str | int | float | list[Any]

Evaluate DSL expression with given context.

Parameters:

Name Type Description Default
expression str

DSL expression to evaluate.

required
context dict[str, ContextValue | LexicalItem | FilledTemplate | Item]

Variables available during evaluation. Can include: - ContextValue: primitive values, lists, sets - LexicalItem: lexical items for single-slot constraints - FilledTemplate: filled templates for multi-slot constraints - Item: items for list partitioning

required

Returns:

Type Description
bool | str | int | float | list[Any]

Result of evaluation.

Raises:

Type Description
EvaluationError

If evaluation fails (parse error, undefined variable, etc.).

Examples:

>>> evaluator = DSLEvaluator()
>>> evaluator.evaluate("x > 5", {"x": 10})
True
>>> evaluator.evaluate(
...     "subject.lemma == verb.lemma",
...     {"subject": item1, "verb": item2}
... )
False

extract_property_value(obj: Any, property_expression: str, context: dict[str, ContextValue] | None = None) -> Any

Extract property value using DSL expression.

This method is used by ListPartitioner to extract property values from items using DSL expressions. The property_expression is evaluated with the object available as 'item' in the context.

Parameters:

Name Type Description Default
obj Any

Object to extract property from (typically a LexicalItem or Item).

required
property_expression str

DSL expression that accesses object properties (e.g., "item.lemma", "item.features.number", "len(item.lemma)").

required
context dict[str, ContextValue] | None

Additional context variables (e.g., constants, helper data).

None

Returns:

Type Description
Any

Extracted property value.

Raises:

Type Description
EvaluationError

If property extraction fails.

Examples:

>>> evaluator = DSLEvaluator()
>>> item = LexicalItem(lemma="walk", pos="VERB")
>>> evaluator.extract_property_value(item, "item.lemma")
'walk'
>>> evaluator.extract_property_value(item, "len(item.lemma)")
4

clear_cache() -> None

Clear compiled AST cache.

This should be called if you want to free memory or if expression strings might have changed meaning.

Examples:

>>> evaluator = DSLEvaluator()
>>> evaluator.clear_cache()

Standard Library

stdlib

Standard library functions for constraint DSL.

This module provides built-in functions that can be used in constraint expressions. Functions are organized by category and registered with the evaluation context.

len_(s: str | list[str | int | float | bool | None] | dict[str, str | int | float | bool | None] | tuple[str | int | float | bool | None, ...]) -> int

Return length of string or collection.

Parameters:

Name Type Description Default
s str | list | dict | tuple

String or collection to measure.

required

Returns:

Type Description
int

Length of string or collection.

Examples:

>>> len_("hello")
5
>>> len_([1, 2, 3])
3

lower(s: str) -> str

Convert string to lowercase.

Parameters:

Name Type Description Default
s str

String to convert.

required

Returns:

Type Description
str

Lowercase string.

Examples:

>>> lower("HELLO")
'hello'

upper(s: str) -> str

Convert string to uppercase.

Parameters:

Name Type Description Default
s str

String to convert.

required

Returns:

Type Description
str

Uppercase string.

Examples:

>>> upper("hello")
'HELLO'

startswith(s: str, prefix: str) -> bool

Check if string starts with prefix.

Parameters:

Name Type Description Default
s str

String to check.

required
prefix str

Prefix to look for.

required

Returns:

Type Description
bool

True if string starts with prefix.

Examples:

>>> startswith("hello", "hel")
True
>>> startswith("hello", "bye")
False

endswith(s: str, suffix: str) -> bool

Check if string ends with suffix.

Parameters:

Name Type Description Default
s str

String to check.

required
suffix str

Suffix to look for.

required

Returns:

Type Description
bool

True if string ends with suffix.

Examples:

>>> endswith("hello", "lo")
True
>>> endswith("hello", "hi")
False

contains(s: str, substring: str) -> bool

Check if string contains substring.

Parameters:

Name Type Description Default
s str

String to check.

required
substring str

Substring to look for.

required

Returns:

Type Description
bool

True if string contains substring.

Examples:

>>> contains("hello", "ell")
True
>>> contains("hello", "bye")
False

replace(s: str, old: str, new: str) -> str

Replace occurrences of substring.

Parameters:

Name Type Description Default
s str

String to modify.

required
old str

Substring to replace.

required
new str

Replacement substring.

required

Returns:

Type Description
str

String with replacements.

Examples:

>>> replace("hello world", "world", "there")
'hello there'

split(s: str, sep: str = ' ') -> list[str]

Split string by separator.

Parameters:

Name Type Description Default
s str

String to split.

required
sep str

Separator string. Defaults to space.

' '

Returns:

Type Description
list[str]

List of substrings.

Examples:

>>> split("a,b,c", ",")
['a', 'b', 'c']

count(collection: str | list[DslScalar], item: DslScalar) -> int

Count occurrences of item in collection.

Parameters:

Name Type Description Default
collection str | list[DslScalar]

Collection to search.

required
item DslScalar

Item to count.

required

Returns:

Type Description
int

Number of occurrences.

Examples:

>>> count([1, 2, 2, 3], 2)
2
>>> count("hello", "l")
2

sum_(collection: list[int | float]) -> int | float

Sum numeric collection.

Parameters:

Name Type Description Default
collection list[int | float]

Collection of numbers.

required

Returns:

Type Description
int | float

Sum of all numbers.

Examples:

>>> sum_([1, 2, 3])
6
>>> sum_([1.5, 2.5])
4.0

min_(collection: list[DslScalar]) -> DslScalar

Return minimum value from collection.

Parameters:

Name Type Description Default
collection list[DslScalar]

Collection to search.

required

Returns:

Type Description
DslScalar

Minimum value.

Examples:

>>> min_([3, 1, 2])
1

max_(collection: list[T]) -> T

Return maximum value from collection.

Parameters:

Name Type Description Default
collection list[T]

Collection to search.

required

Returns:

Type Description
T

Maximum value.

Examples:

>>> max_([3, 1, 2])
3

any_(collection: list[T]) -> bool

Check if any element is truthy.

Parameters:

Name Type Description Default
collection list[T]

Collection to check.

required

Returns:

Type Description
bool

True if any element is truthy.

Examples:

>>> any_([False, True, False])
True
>>> any_([False, False])
False

all_(collection: list[T]) -> bool

Check if all elements are truthy.

Parameters:

Name Type Description Default
collection list[T]

Collection to check.

required

Returns:

Type Description
bool

True if all elements are truthy.

Examples:

>>> all_([True, True, True])
True
>>> all_([True, False, True])
False

is_str(value: DslScalar) -> bool

Check if value is a string.

Parameters:

Name Type Description Default
value DslScalar

Value to check.

required

Returns:

Type Description
bool

True if value is a string.

Examples:

>>> is_str("hello")
True
>>> is_str(42)
False

is_int(value: DslScalar) -> bool

Check if value is an integer.

Parameters:

Name Type Description Default
value DslScalar

Value to check.

required

Returns:

Type Description
bool

True if value is an integer.

Examples:

>>> is_int(42)
True
>>> is_int(42.0)
False

is_float(value: DslScalar) -> bool

Check if value is a float.

Parameters:

Name Type Description Default
value DslScalar

Value to check.

required

Returns:

Type Description
bool

True if value is a float.

Examples:

>>> is_float(42.0)
True
>>> is_float(42)
False

is_bool(value: DslScalar) -> bool

Check if value is a boolean.

Parameters:

Name Type Description Default
value DslScalar

Value to check.

required

Returns:

Type Description
bool

True if value is a boolean.

Examples:

>>> is_bool(True)
True
>>> is_bool(1)
False

is_list(value: DslScalar | list[DslScalar]) -> bool

Check if value is a list.

Parameters:

Name Type Description Default
value DslScalar | list[DslScalar]

Value to check.

required

Returns:

Type Description
bool

True if value is a list.

Examples:

>>> is_list([1, 2, 3])
True
>>> is_list((1, 2, 3))
False

str_(value: DslScalar) -> str

Convert value to string.

Parameters:

Name Type Description Default
value DslScalar

Value to convert.

required

Returns:

Type Description
str

String representation of value.

Examples:

>>> str_(42)
'42'
>>> str_(True)
'True'

abs_(value: int | float) -> int | float

Return absolute value.

Parameters:

Name Type Description Default
value int | float

Numeric value.

required

Returns:

Type Description
int | float

Absolute value.

Examples:

>>> abs_(-5)
5
>>> abs_(5)
5

round_(value: float, ndigits: int = 0) -> float

Round numeric value.

Parameters:

Name Type Description Default
value float

Value to round.

required
ndigits int

Number of decimal places.

0

Returns:

Type Description
float

Rounded value.

Examples:

>>> round_(3.14159, 2)
3.14

floor(value: float) -> int

Return floor of value.

Parameters:

Name Type Description Default
value float

Numeric value.

required

Returns:

Type Description
int

Floor value.

Examples:

>>> floor(3.7)
3
>>> floor(-3.7)
-4

ceil(value: float) -> int

Return ceiling of value.

Parameters:

Name Type Description Default
value float

Numeric value.

required

Returns:

Type Description
int

Ceiling value.

Examples:

>>> ceil(3.2)
4
>>> ceil(-3.2)
-3

not_(value: DslScalar | list[DslScalar]) -> bool

Return logical negation of value.

Parameters:

Name Type Description Default
value DslScalar | list[DslScalar]

Value to negate.

required

Returns:

Type Description
bool

Logical negation.

Examples:

>>> not_(True)
False
>>> not_(False)
True
>>> not_(0)
True

sigmoid(x: float) -> float

Sigmoid activation function.

Converts unbounded value to probability in (0, 1).

Parameters:

Name Type Description Default
x float

Input value.

required

Returns:

Type Description
float

Sigmoid output in (0, 1).

Examples:

>>> sigmoid(0.0)
0.5
>>> round(sigmoid(5.0), 3)
0.993
>>> round(sigmoid(-5.0), 3)
0.007

softmax(values: list[float]) -> list[float]

Softmax function over list of values.

Converts list of scores to probability distribution.

Parameters:

Name Type Description Default
values list[float]

Input scores.

required

Returns:

Type Description
list[float]

Probability distribution (sums to 1.0).

Examples:

>>> probs = softmax([1.0, 2.0, 3.0])
>>> [round(p, 2) for p in probs]
[0.09, 0.24, 0.67]

sample_categorical(probs: list[float], seed: int | None = None) -> int

Sample from categorical distribution.

Parameters:

Name Type Description Default
probs list[float]

Probability distribution.

required
seed int | None

Random seed.

None

Returns:

Type Description
int

Sampled index (0-based).

Examples:

>>> sample_categorical([0.2, 0.5, 0.3], seed=42)
1

add_noise(value: float, noise_type: str, strength: float, seed: int | None = None) -> float

Add noise to a value.

Parameters:

Name Type Description Default
value float

Original value.

required
noise_type str

Type of noise ("gaussian", "uniform").

required
strength float

Noise strength (stddev for gaussian, range for uniform).

required
seed int | None

Random seed.

None

Returns:

Type Description
float

Value with noise added.

Examples:

>>> result = add_noise(5.0, "gaussian", 0.1, seed=42)
>>> isinstance(result, float)
True
>>> result = add_noise(5.0, "uniform", 0.1, seed=42)
>>> isinstance(result, float)
True

model_output(item: Item, key: str, default: DslScalar = None) -> DslScalar | list[float]

Extract model output from item.

Parameters:

Name Type Description Default
item Item

Item with model outputs.

required
key str

Key to extract (e.g., "lm_score", "embedding").

required
default DslScalar

Default value if key not found.

None

Returns:

Type Description
DslScalar | list[float]

Extracted value or default.

Examples:

>>> # Would work with actual Item object
>>> # model_output(item, "lm_score", default=0.0)
>>> # -12.4

distance(emb1: list[float], emb2: list[float], metric: str = 'cosine') -> float

Compute distance between embeddings.

Parameters:

Name Type Description Default
emb1 list[float]

First embedding.

required
emb2 list[float]

Second embedding.

required
metric str

Distance metric ("cosine", "euclidean", "manhattan").

'cosine'

Returns:

Type Description
float

Distance value.

Examples:

>>> distance([1.0, 0.0], [0.0, 1.0], "cosine")
1.0
>>> round(distance([1.0, 0.0], [0.0, 1.0], "euclidean"), 3)
1.414
>>> distance([1.0, 0.0], [0.0, 1.0], "manhattan")
2.0

preference_prob(score1: float, score2: float, temperature: float = 1.0) -> float

Compute preference probability using sigmoid.

P(choose option 1) = sigmoid((score1 - score2) / temperature)

Parameters:

Name Type Description Default
score1 float

Score for option 1.

required
score2 float

Score for option 2.

required
temperature float

Temperature for scaling.

1.0

Returns:

Type Description
float

Probability of choosing option 1.

Examples:

>>> round(preference_prob(10.0, 5.0, temperature=1.0), 3)
0.993
>>> round(preference_prob(10.0, 5.0, temperature=5.0), 2)
0.73

register_stdlib(context: EvaluationContext) -> None

Register all standard library functions in context.

Parameters:

Name Type Description Default
context EvaluationContext

Context to register functions in.

required

Examples:

>>> from bead.dsl.context import EvaluationContext
>>> ctx = EvaluationContext()
>>> register_stdlib(ctx)
>>> ctx.call_function("len", ["hello"])
5

Abstract Syntax Tree

ast

Abstract Syntax Tree node definitions for constraint DSL.

This module defines the AST nodes that represent parsed constraint expressions. Each node type corresponds to a construct in the constraint DSL grammar.

ASTNode

Bases: BeadBaseModel

Base class for all AST nodes.

All AST nodes inherit from BeadBaseModel to get: - Automatic validation - Serialization support - Metadata tracking

Literal

Bases: ASTNode

Literal value node (string, number, boolean).

Examples:

>>> node = Literal(value="hello")
>>> node.value
'hello'
>>> node = Literal(value=42)
>>> node.value
42

Variable

Bases: ASTNode

Variable reference node.

References a variable in the evaluation context (e.g., item attributes).

Examples:

>>> node = Variable(name="lemma")
>>> node.name
'lemma'

BinaryOp

Bases: ASTNode

Binary operation node.

Represents operations like: a == b, x > y, p and q

Attributes:

Name Type Description
operator str

The operator (==, !=, <, >, <=, >=, and, or, in, etc.)

left ASTNode

Left operand

right ASTNode

Right operand

Examples:

>>> left = Variable(name="pos")
>>> right = Literal(value="VERB")
>>> node = BinaryOp(operator="==", left=left, right=right)
>>> node.operator
'=='

UnaryOp

Bases: ASTNode

Unary operation node.

Represents operations like: not x, -y

Examples:

>>> operand = Variable(name="is_transitive")
>>> node = UnaryOp(operator="not", operand=operand)
>>> node.operator
'not'

FunctionCall

Bases: ASTNode

Function call node.

Represents function calls and method calls like: - len(x), startswith("pre") - obj.method(arg)

Examples:

>>> func = Variable(name="len")
>>> arg = Variable(name="lemma")
>>> node = FunctionCall(function=func, arguments=[arg])
>>> node.function.name
'len'

ListLiteral

Bases: ASTNode

List literal node.

Represents list literals like: ["a", "b", "c"]

Examples:

>>> elements = [Literal(value="a"), Literal(value="b")]
>>> node = ListLiteral(elements=elements)
>>> len(node.elements)
2

AttributeAccess

Bases: ASTNode

Attribute access node.

Represents attribute access like: item.lemma, obj.property

Examples:

>>> obj = Variable(name="item")
>>> node = AttributeAccess(object=obj, attribute="lemma")
>>> node.attribute
'lemma'

Subscript

Bases: ASTNode

Subscript access node.

Represents subscript access like: item['key'], obj[0]

Examples:

>>> obj = Variable(name="item")
>>> key = Literal(value="key")
>>> node = Subscript(object=obj, index=key)
>>> node.index.value
'key'

Context

context

Evaluation context for constraint DSL.

This module provides the EvaluationContext class that manages variable bindings and function lookups during constraint evaluation.

EvaluationContext

Evaluation context for constraint expressions.

The context provides: - Variable bindings (e.g., item attributes) - Function registry (built-in and custom functions) - Parent context chain for scoping

Parameters:

Name Type Description Default
parent EvaluationContext | None

Parent context for variable/function lookup chain.

None

Examples:

>>> ctx = EvaluationContext()
>>> ctx.set_variable("x", 42)
>>> ctx.get_variable("x")
42
>>> ctx.set_function("double", lambda x: x * 2)
>>> ctx.call_function("double", [5])
10

set_variable(name: str, value: Any) -> None

Set a variable in the context.

Parameters:

Name Type Description Default
name str

Variable name.

required
value Any

Variable value.

required

get_variable(name: str) -> Any

Get a variable from the context (searches parent chain).

Parameters:

Name Type Description Default
name str

Variable name.

required

Returns:

Type Description
Any

Variable value.

Raises:

Type Description
EvaluationError

If variable is not defined in context or parent chain.

has_variable(name: str) -> bool

Check if variable exists in context or parent chain.

Parameters:

Name Type Description Default
name str

Variable name.

required

Returns:

Type Description
bool

True if variable exists, False otherwise.

set_function(name: str, func: Callable[..., Any]) -> None

Register a function in the context.

Parameters:

Name Type Description Default
name str

Function name.

required
func Callable[..., Any]

Function to register.

required

call_function(name: str, args: list[Any]) -> Any

Call a function with arguments.

Parameters:

Name Type Description Default
name str

Function name.

required
args list[Any]

Function arguments.

required

Returns:

Type Description
Any

Function return value.

Raises:

Type Description
EvaluationError

If function is not defined or call fails.

has_function(name: str) -> bool

Check if function exists in context or parent chain.

Parameters:

Name Type Description Default
name str

Function name.

required

Returns:

Type Description
bool

True if function exists, False otherwise.

create_child() -> EvaluationContext

Create a child context with this context as parent.

Returns:

Type Description
EvaluationContext

New child context.

Examples:

>>> parent = EvaluationContext()
>>> parent.set_variable("x", 10)
>>> child = parent.create_child()
>>> child.get_variable("x")
10
>>> child.set_variable("y", 20)
>>> child.get_variable("y")
20

Errors

errors

DSL-specific exceptions.

DSLError

Bases: Exception

Base exception for DSL errors.

ParseError

Bases: DSLError

Exception raised when parsing fails.

Parameters:

Name Type Description Default
message str

Error message describing what went wrong during parsing.

required
line int | None

Line number where the error occurred (1-indexed). None if unknown.

None
column int | None

Column number where the error occurred (1-indexed). None if unknown.

None
text str | None

The text that caused the error. None if unavailable.

None

Attributes:

Name Type Description
line int | None

Line number where error occurred.

column int | None

Column number where error occurred.

text str | None

Text that caused the error.

Examples:

>>> try:
...     raise ParseError("Unexpected token", line=5, column=12, text="@invalid")
... except ParseError as e:
...     print(e.line, e.column)
5 12

__str__() -> str

Return formatted error message.

EvaluationError

Bases: DSLError

Exception raised when evaluation fails.