1717import re
1818import unicodedata
1919
20- from .expr import expr_lookbehind , expr_max_length , expr_neg_lookbehind , expr_to_literal , expr_to_regex , is_plain_string
20+ from .expr import expr_lookbehind , expr_max_length , expr_neg_lookbehind , expr_to_literal , expr_to_regex
2121
2222
2323class ExecutionError (ValueError ):
2424 """The map uses a construct this engine does not implement yet."""
2525
2626
27- def _compile_parallel (subs : list [dict ]) -> tuple [re .Pattern [str ], dict [str , str ], dict [ str , str ] ]:
27+ def _compile_parallel (subs : list [dict ]) -> tuple [re .Pattern [str ], dict [str , str ]]:
2828 """Compile one parallel group: longest-pattern-first alternation
2929 with a named group per sub; lookaround guards for before:/after:.
3030 Plain-string patterns additionally feed the casing maps."""
3131 indexed = []
32- anchor_results : dict [str , str ] = {}
33- n = len (subs )
3432 for i , sub in enumerate (subs ):
3533 pat = expr_to_regex (sub ["pattern" ])
3634 full = pat
@@ -48,27 +46,12 @@ def _compile_parallel(subs: list[dict]) -> tuple[re.Pattern[str], dict[str, str]
4846 key += expr_max_length (sub [guard ])
4947 key += sub .get ("priority" , 0 )
5048 indexed .append ((key , full , f"s{ i } " ))
51- if is_plain_string (sub ["pattern" ]) and not any (sub .get (g ) for g in ("before" , "after" , "not_before" , "not_after" )):
52- src = expr_to_literal (sub ["pattern" ])
53- if src .upper () != src :
54- anchor_results [f"a{ i } " ] = expr_to_literal (sub ["result" ])
55- indexed .append ((len (src ), re .escape (src .upper ()), f"a{ i } " ))
5649 indexed .sort (key = lambda t : - t [0 ])
5750 combined = "|" .join (f"(?P<{ name } >{ full } )" for _ , full , name in indexed )
5851 pattern = re .compile (combined ) if indexed else re .compile (r"(?!)" )
5952
6053 results = {f"s{ i } " : expr_to_literal (sub ["result" ]) for i , sub in enumerate (subs )}
61- results .update (anchor_results )
62- casing_map : dict [str , str ] = {}
63- upper_dst : dict [str , str ] = {}
64- for sub in subs :
65- if is_plain_string (sub ["pattern" ]) and not any (sub .get (g ) for g in ("before" , "after" , "not_before" , "not_after" )):
66- src = expr_to_literal (sub ["pattern" ])
67- dst = expr_to_literal (sub ["result" ])
68- casing_map [src ] = dst
69- if src .upper () != src :
70- upper_dst [src .upper ()] = dst
71- return pattern , {"casing" : casing_map , "upper" : upper_dst , "results" : results }, {}
54+ return pattern , results
7255
7356
7457_CASE_FNS = {
@@ -89,7 +72,6 @@ def __init__(self, tree: dict, loader=None, on_unsupported: str = "raise") -> No
8972 self .on_unsupported = on_unsupported
9073 self .skipped_unsupported : list [str ] = []
9174 self ._compiled : re .Pattern [str ] | None = None
92- self ._compiled_map : dict [str , str ] = {}
9375 self ._group_results : dict [str , str ] = {}
9476 self ._compiled_source : int | None = None
9577
@@ -103,51 +85,15 @@ def _run_stage(self, stage: dict, text: str) -> str:
10385 text = self ._run_op (child , text )
10486 return text
10587
106- def _group_repl (self , m : re .Match [str ], text : str ) -> str :
107- name = m .lastgroup if m .lastgroup else ""
108- if name in self ._group_results :
109- result = self ._group_results [name ]
110- tok = m .group (0 )
111- if result != result .upper () and tok == tok .upper () and tok != tok .lower ():
112- ws , we = m .start (), m .end ()
113- while ws > 0 and text [ws - 1 ].isalpha ():
114- ws -= 1
115- while we < len (text ) and text [we ].isalpha ():
116- we += 1
117- if text [ws :we ].isupper ():
118- return result .upper ()
119- return result
120- return self ._parallel_repl (m , text )
121-
122- def _parallel_repl (self , m : re .Match [str ], text : str ) -> str :
123- tok = m .group (0 )
124- dst = self ._compiled_map .get (tok ) or self ._upper_dst .get (tok )
125- if dst is None :
126- return tok
127- # interscript-ruby casing convention: inside an ALL-CAPS source
128- # word, a fully-uppercase source token uppercases its result
129- # (Я -> Ya normally, YA inside БЯГА).
130- if dst != dst .upper () and tok == tok .upper () and tok != tok .lower ():
131- ws , we = m .start (), m .end ()
132- while ws > 0 and text [ws - 1 ].isalpha ():
133- ws -= 1
134- while we < len (text ) and text [we ].isalpha ():
135- we += 1
136- if text [ws :we ].isupper ():
137- return dst .upper ()
138- return dst
139-
14088 def _run_op (self , op : dict , text : str ) -> str :
14189 kind = op .get ("kind" )
14290 if kind == "parallel" :
14391 if self ._compiled is None or self ._compiled_source != id (op ):
144- pattern , maps , _ = _compile_parallel (op ["subs" ])
92+ pattern , results = _compile_parallel (op ["subs" ])
14593 self ._compiled = pattern
146- self ._compiled_map = maps ["casing" ]
147- self ._upper_dst = maps ["upper" ]
148- self ._group_results = maps ["results" ]
94+ self ._group_results = results
14995 self ._compiled_source = id (op )
150- return self ._compiled .sub (lambda m : self ._group_repl ( m , text ) , text )
96+ return self ._compiled .sub (lambda m : self ._group_results [ m . lastgroup ] , text )
15197 if kind == "subst" :
15298 flags = re .IGNORECASE if op .get ("ignore_case" ) else 0
15399 pattern = re .compile (op ["pattern" ], flags )
0 commit comments