grammar-inference-engine/bex/marking.py

54 lines
1.7 KiB
Python

"""Marking — Convert k-OA to SOA over Σ^(k) (Definition 4.4, arXiv 1004.2372)."""
from .soa import SOA
from .grammar import Symbol, Epsilon, Empty, Plus, Star, Optional, Concat, Alt
def mark_koa(G):
"""
Mark a k-OA G as a SOA over Σ^(k).
Process nodes in arbitrary order. For the i-th occurrence of label a,
replace by a^(i) (represented as Symbol('a_i')).
Returns a SOA H over Σ^(k) such that L(G) = strip(L(H)).
"""
H = SOA()
H.src = G.src
H.sink = G.sink
H._succ = {n: set(succ) for n, succ in G._succ.items()}
H._pred = {n: set(pred) for n, pred in G._pred.items()}
H._label = {}
H._next = G._next
counts = {}
for n in G._succ:
lab = G._label.get(n)
if lab is not None and not isinstance(lab, (Empty, Epsilon)) and n not in (G.src, G.sink):
sym = strip_k(lab)
key = sym.value if isinstance(sym, Symbol) else str(sym)
counts[key] = counts.get(key, 0) + 1
H._label[n] = Symbol(f"{key}_{counts[key]}")
elif n in (G.src, G.sink):
H._label[n] = None
else:
H._label[n] = lab
return H
def strip_k(node):
"""Remove k-ORE markers from AST: Symbol('a_1') → Symbol('a'), Symbol('b^(2)') → Symbol('b')."""
if isinstance(node, Symbol):
import re
value = node.value
value = re.sub(r'_\d+$', '', value)
value = re.sub(r'\^\(\d+\)$', '', value)
return Symbol(value)
if isinstance(node, (Epsilon, Empty)):
return node
if isinstance(node, (Plus, Optional, Star)):
return type(node)(strip_k(node.child))
if isinstance(node, (Concat, Alt)):
return type(node)([strip_k(child) for child in node.parts])
return node