Files

79 lines
2.5 KiB
Python
Raw Permalink Normal View History

"""
Safe predicate evaluator for XIC v2 control flow.
Supports simple expressions referencing fused symbol fields and helper functions.
Allowed: comparisons, boolean ops, dominant_contains('glyph://id').
"""
import ast
from typing import Any, Dict
ALLOWED_NODE_TYPES = (
ast.Expression, ast.BoolOp, ast.BinOp, ast.UnaryOp, ast.Compare,
ast.Name, ast.Load, ast.Constant, ast.Call, ast.Attribute,
ast.And, ast.Or, ast.Not,
ast.Gt, ast.Lt, ast.GtE, ast.LtE, ast.Eq, ast.NotEq, ast.In, ast.NotIn
)
def _validate_node(node: ast.AST):
"""Recursively validate AST node is safe for eval."""
if not isinstance(node, ALLOWED_NODE_TYPES):
raise ValueError(f"Unsafe predicate node: {type(node).__name__}")
for child in ast.iter_child_nodes(node):
_validate_node(child)
class DotDict:
"""Helper class that allows dict access via dot notation."""
def __init__(self, data: Dict[str, Any]):
self.__dict__.update(data)
def _build_context(fused: Dict[str, Any], dominant: list):
"""Build safe evaluation context with helpers and fused symbol fields."""
def dominant_contains(glyph_id: str) -> bool:
"""Check if a glyph is in the dominant list."""
return any(g == glyph_id for g, _ in dominant)
safe = {
"dominant_contains": dominant_contains,
"fused": DotDict(fused or {}),
}
return safe
def eval_predicate(
expr: str,
fused: Dict[str, Any] | None = None,
dominant: list | None = None
) -> bool:
"""
Evaluate predicate expression safely.
Example predicates:
"fused.global_resonance_score > 0.7"
"dominant_contains('glyph://entropy') and fused.global_resonance_score > 0.5"
Args:
expr: Predicate expression string
fused: Fused symbol dict with fields like global_resonance_score, glyph_ids
dominant: List of (glyph_id, weight) tuples for dominant glyphs
Returns:
Boolean result of predicate evaluation
"""
if dominant is None:
dominant = []
# Parse and validate AST
try:
expr_ast = ast.parse(expr, mode="eval")
except SyntaxError as e:
raise ValueError(f"Invalid predicate syntax: {e}")
_validate_node(expr_ast)
# Compile and evaluate
compiled = compile(expr_ast, "<predicate>", "eval")
safe_ctx = _build_context(fused or {}, dominant)
try:
return bool(eval(compiled, {}, safe_ctx))
except Exception as e:
raise ValueError(f"Predicate evaluation error: {e}")