mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 02:54:26 +00:00
134 lines
3.2 KiB
Python
134 lines
3.2 KiB
Python
"""Quote/paren-aware string splitters for the fallback expression evaluator.
|
|
|
|
Split out from ``utils.templating.ansible`` to keep that module under the
|
|
per-file line budget.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
def split_list_items(s: str) -> list[str]:
|
|
"""Split list-literal inner content: ``"A, 'b'"`` -> ``["A", "'b'"]``."""
|
|
items: list[str] = []
|
|
buf: list[str] = []
|
|
q: str | None = None
|
|
|
|
for ch in s:
|
|
if q:
|
|
buf.append(ch)
|
|
if ch == q:
|
|
q = None
|
|
continue
|
|
|
|
if ch in ("'", '"'):
|
|
buf.append(ch)
|
|
q = ch
|
|
continue
|
|
|
|
if ch == ",":
|
|
token = "".join(buf).strip()
|
|
if token:
|
|
items.append(token)
|
|
buf = []
|
|
continue
|
|
|
|
buf.append(ch)
|
|
|
|
token = "".join(buf).strip()
|
|
if token:
|
|
items.append(token)
|
|
|
|
return items
|
|
|
|
|
|
def find_top_level_op(s: str, op: str) -> int:
|
|
"""Index of the first ``op`` at paren-depth 0 and outside quotes, else -1.
|
|
|
|
``op`` is matched literally; callers pad word operators with spaces
|
|
(e.g. ``" if "``) so they don't match inside identifiers.
|
|
"""
|
|
depth = 0
|
|
quote: str | None = None
|
|
span = len(op)
|
|
i = 0
|
|
n = len(s)
|
|
while i <= n - span:
|
|
ch = s[i]
|
|
if quote:
|
|
if ch == quote:
|
|
quote = None
|
|
i += 1
|
|
continue
|
|
if ch in ("'", '"'):
|
|
quote = ch
|
|
i += 1
|
|
continue
|
|
if ch in "([{":
|
|
depth += 1
|
|
i += 1
|
|
continue
|
|
if ch in ")]}":
|
|
depth -= 1
|
|
i += 1
|
|
continue
|
|
if depth == 0 and s[i : i + span] == op:
|
|
return i
|
|
i += 1
|
|
return -1
|
|
|
|
|
|
def split_top_level(s: str, sep: str) -> list[str]:
|
|
"""Split ``s`` on single-char ``sep`` at paren-depth 0 outside quotes."""
|
|
parts: list[str] = []
|
|
buf: list[str] = []
|
|
depth = 0
|
|
quote: str | None = None
|
|
for ch in s:
|
|
if quote:
|
|
buf.append(ch)
|
|
if ch == quote:
|
|
quote = None
|
|
continue
|
|
if ch in ("'", '"'):
|
|
quote = ch
|
|
buf.append(ch)
|
|
continue
|
|
if ch in "([{":
|
|
depth += 1
|
|
buf.append(ch)
|
|
continue
|
|
if ch in ")]}":
|
|
depth -= 1
|
|
buf.append(ch)
|
|
continue
|
|
if depth == 0 and ch == sep:
|
|
parts.append("".join(buf))
|
|
buf = []
|
|
continue
|
|
buf.append(ch)
|
|
parts.append("".join(buf))
|
|
return parts
|
|
|
|
|
|
def is_paren_wrapped(s: str) -> bool:
|
|
"""True iff the outer parentheses wrap the whole expression."""
|
|
if not (s.startswith("(") and s.endswith(")")):
|
|
return False
|
|
depth = 0
|
|
quote: str | None = None
|
|
for i, ch in enumerate(s):
|
|
if quote:
|
|
if ch == quote:
|
|
quote = None
|
|
continue
|
|
if ch in ("'", '"'):
|
|
quote = ch
|
|
continue
|
|
if ch == "(":
|
|
depth += 1
|
|
elif ch == ")":
|
|
depth -= 1
|
|
if depth == 0 and i != len(s) - 1:
|
|
return False
|
|
return True
|