Source code for cfpq_data.grammars.utils.change_terminals
"""Change terminals of a context-free grammar in different formats."""
import logging
import re
from typing import Dict
from pyformlang.cfg import CFG
from pyformlang.regular_expression import Regex
from pyformlang.rsa import RecursiveAutomaton as RSA
from cfpq_data.grammars.readwrite.cfg import cfg_to_text, cfg_from_text
from cfpq_data.grammars.readwrite.cnf import cnf_from_text
from cfpq_data.grammars.readwrite.regex import regex_to_text, regex_from_text
from cfpq_data.grammars.readwrite.rsa import rsa_to_text, rsa_from_text
__all__ = [
"change_terminals_in_cfg",
"change_terminals_in_cnf",
"change_terminals_in_regex",
"change_terminals_in_rsa",
]
[docs]
def change_terminals_in_cfg(cfg: CFG, mapping: Dict[str, str]) -> CFG:
"""Change terminals of a context-free grammar [1]_.
Parameters
----------
cfg : CFG
Context-free grammar.
mapping: Dict[str, str]
Terminals mapping.
Examples
--------
>>> from cfpq_data import *
>>> cfg = cfg_from_text("S -> a S b S")
>>> new_cfg = change_terminals_in_cfg(cfg, {"a": "b", "b": "c"})
>>> cfg_to_text(new_cfg)
'S -> b S c S'
Returns
-------
cfg : CFG
Context-free grammar with changed terminals.
References
----------
.. [1] https://en.wikipedia.org/wiki/Context-free_grammar#Formal_definitions
"""
regex = re.compile("|".join(map(re.escape, mapping.keys())))
text = regex.sub(lambda match: mapping[match.group(0)], cfg_to_text(cfg))
new_cfg = cfg_from_text(text, start_symbol=cfg.start_symbol)
logging.info(f"Change terminals in {cfg=} with {mapping=} to {new_cfg=}")
return new_cfg
[docs]
def change_terminals_in_cnf(cnf: CFG, mapping: Dict[str, str]) -> CFG:
"""Change terminals of a context-free grammar [1]_ in Chomsky normal form.
Parameters
----------
cnf : CFG
Context-free grammar
in Chomsky normal form.
mapping: Dict[str, str]
Terminals mapping.
Examples
--------
>>> from cfpq_data import *
>>> cnf = cnf_from_text("S -> a b")
>>> new_cnf = change_terminals_in_cnf(cnf, {"a": "b", "b": "c"})
>>> cfg_to_text(new_cnf)
'S -> b#CNF##CNF# c#CNF##CNF#\\nb#CNF##CNF# -> b#CNF#\\nc#CNF##CNF# -> c#CNF#'
Returns
-------
cnf : CFG
Context-free grammar in Chomsky normal form with changed terminals.
References
----------
.. [1] https://en.wikipedia.org/wiki/Chomsky_normal_form
"""
regex = re.compile("|".join(map(re.escape, mapping.keys())))
text = regex.sub(lambda match: mapping[match.group(0)], cfg_to_text(cnf))
new_cnf = cnf_from_text(text, start_symbol=cnf.start_symbol)
logging.info(f"Change terminals in {cnf=} with {mapping=} to {new_cnf=}")
return new_cnf
[docs]
def change_terminals_in_regex(regex: Regex, mapping: Dict[str, str]) -> Regex:
"""Change terminals of a regular expression [1]_.
Parameters
----------
regex : Regex
Regular expression.
mapping: Dict[str, str]
Terminals mapping.
Examples
--------
>>> from cfpq_data import *
>>> regex = regex_from_text("a (bc|d*)")
>>> new_regex = change_terminals_in_regex(regex, {"a": "b", "b": "c"})
>>> regex_to_text(new_regex)
'(b (cc|(d)*))'
Returns
-------
regex : Regex
Regular expression with changed terminals.
References
----------
.. [1] https://en.wikipedia.org/wiki/Regular_expression#Formal_definition
"""
expr = re.compile("|".join(map(re.escape, mapping.keys())))
text = expr.sub(lambda match: mapping[match.group(0)], regex_to_text(regex))
new_regex = regex_from_text(text)
logging.info(f"Change terminals in {regex=} with {mapping=} to {new_regex=}")
return new_regex
[docs]
def change_terminals_in_rsa(rsa: RSA, mapping: Dict[str, str]) -> RSA:
"""Change terminals of a Recursive State Automaton [1]_.
Parameters
----------
rsa : RSA
Recursive State Automaton.
mapping: Dict
Terminals mapping.
Examples
--------
>>> from cfpq_data import *
>>> rsa = rsa_from_text("S -> a*")
>>> new_rsa = change_terminals_in_rsa(rsa, {"a": "b"})
>>> rsa_to_text(new_rsa)
'S -> (b)*'
Returns
-------
rsa : RSA
Recursive State Automaton with changed terminals.
References
----------
.. [1] Alur R., Etessami K., Yannakakis M. (2001) Analysis of Recursive State Machines.
In: Berry G., Comon H., Finkel A. (eds) Computer Aided Verification. CAV 2001.
Lecture Notes in Computer Science, vol 2102.
Springer, Berlin, Heidelberg. https://doi.org/10.1007/3-540-44585-4_18
"""
regex = re.compile("|".join(map(re.escape, mapping.keys())))
text = regex.sub(lambda match: mapping[match.group(0)], rsa_to_text(rsa))
new_rsa = rsa_from_text(text, start_symbol=rsa.initial_label)
logging.info(f"Change terminals in {rsa=} with {mapping=} to {new_rsa=}")
return rsa_from_text(text)