TOMK / src /python /tom_world /grammar.py
c0mbatduckzz's picture
Publish WQK 0.6 formal specification, use cases and verified release (part 2)
767a051 verified
Raw History Blame Contribute Delete
5.75 kB
"""Finite, budgeted deterministic grammar expansion."""
from __future__ import annotations
from dataclasses import dataclass
from typing import Any, Mapping
from .canonical import attach_hash
from .store import WorldStore
@dataclass(slots=True)
class _BitCursor:
bits: list[int]
policy: str
index: int = 0
def take(self) -> tuple[int, int]:
if not self.bits:
raise ValueError("branched grammar production has no branch bits")
source_index = self.index
if self.policy == "strict" and self.index >= len(self.bits):
raise ValueError("branched grammar exhausted strict branch_bits")
bit = self.bits[self.index % len(self.bits)]
self.index += 1
return bit, source_index
def _stack_depth(symbols: list[str]) -> int:
depth = 0
maximum = 0
for symbol in symbols:
if symbol == "[":
depth += 1
maximum = max(maximum, depth)
elif symbol == "]":
depth -= 1
if depth < 0:
raise ValueError("grammar emitted an unmatched closing bracket")
if depth != 0:
raise ValueError("grammar emitted unbalanced brackets")
return maximum
class GrammarEngine:
def __init__(self, store: WorldStore, *, commit: str | None = None) -> None:
self.store = store
self.commit = commit or store.head
if self.commit is None:
raise ValueError("grammar engine requires a committed world")
def expand(
self,
grammar_id: str,
*,
depth: int | None = None,
branch_bits: list[int] | None = None,
) -> dict[str, Any]:
record = self.store.read_record(grammar_id, commit=self.commit)
if record["record_type"] != "grammar":
raise TypeError(f"{grammar_id} is not a grammar record")
payload = record["payload"]
budgets = payload["budgets"]
maximum_depth = int(budgets["max_depth"])
requested_depth = maximum_depth if depth is None else depth
if isinstance(requested_depth, bool) or not isinstance(requested_depth, int) or requested_depth < 0:
raise ValueError("grammar depth must be a nonnegative integer")
if requested_depth > maximum_depth:
raise ValueError(f"grammar depth budget exceeded: {requested_depth} > {maximum_depth}")
bits = list(payload.get("branch_bits", [])) if branch_bits is None else list(branch_bits)
if any(bit not in (0, 1) for bit in bits):
raise ValueError("branch_bits must contain only 0 and 1")
cursor = _BitCursor(bits, str(payload.get("branch_policy", "cycle")))
productions: Mapping[str, Any] = payload["productions"]
current = list(payload["axiom"])
max_symbols = int(budgets["max_symbols"])
max_stack = int(budgets["max_stack"])
if len(current) > max_symbols:
raise ValueError("grammar axiom exceeds max_symbols")
if _stack_depth(current) > max_stack:
raise ValueError("grammar axiom exceeds max_stack")
generations: list[dict[str, Any]] = [{
"depth": 0,
"symbols": current,
"symbol_count": len(current),
"stack_depth": _stack_depth(current),
"branch_decisions": [],
}]
for generation in range(1, requested_depth + 1):
next_symbols: list[str] = []
decisions: list[dict[str, Any]] = []
for input_index, symbol in enumerate(current):
production = productions.get(symbol)
if production is None:
replacement = [symbol]
elif isinstance(production, list):
replacement = list(production)
elif isinstance(production, Mapping):
bit, bit_index = cursor.take()
replacement = list(production["one" if bit else "zero"])
decisions.append({
"input_index": input_index,
"symbol": symbol,
"branch_bit": bit,
"branch_bit_index": bit_index,
})
else: # Record validation normally makes this unreachable.
raise ValueError(f"invalid production for {symbol}")
next_symbols.extend(replacement)
if len(next_symbols) > max_symbols:
raise ValueError(
f"grammar symbol budget exceeded at depth {generation}: "
f"{len(next_symbols)} > {max_symbols}"
)
stack = _stack_depth(next_symbols)
if stack > max_stack:
raise ValueError(
f"grammar stack budget exceeded at depth {generation}: {stack} > {max_stack}"
)
current = next_symbols
generations.append({
"depth": generation,
"symbols": current,
"symbol_count": len(current),
"stack_depth": stack,
"branch_decisions": decisions,
})
return attach_hash({
"schema": "TOM-GRAMMAR-EXPANSION-CERTIFICATE-0.1",
"commit": self.commit,
"grammar_id": grammar_id,
"grammar_hash": record["content_hash"],
"requested_depth": requested_depth,
"branch_bits": bits,
"branch_policy": cursor.policy,
"bits_consumed": cursor.index,
"generations": generations,
"terminal_symbols": current,
"terminal_symbol_count": len(current),
"status": "complete",
})