import argparse
import json
import re
from pathlib import Path
def extract_conditions(js_text: str) -> dict | None:
mch = re.search(r"const conditions\s*=\s*(\{.*?\});", js_text, re.DOTALL)
if not mch:
return None
obj = mch.group(1)
obj = re.sub(r"'([^']*)'", r'"\1"', obj)
obj = re.sub(r",\s*([}\]])", r"\1", obj)
obj = re.sub(r"(\b\w+\b)\s*:", r'"\1":', obj)
json_out = json.loads(obj)
return {
key: {k: v for k, v in val.items() if k not in ("i18n", "subConditions")}
for key, val in json_out.items()
}
def scan_yomitan_repo(repo_path: Path) -> dict[str, dict]:
if not repo_path.exists():
raise FileNotFoundError(f"Repo not found @ {repo_path.resolve()}")
lang_root = repo_path / "ext" / "js" / "language"
if not lang_root.exists():
raise FileNotFoundError(f"Language folder not found @ {lang_root}")
results: dict[str, dict] = {}
seen: dict[str, dict] = {}
seen_lang: dict[str, str] = {}
for js_file in lang_root.rglob("*.js"):
text = js_file.read_text(encoding="utf-8")
conditions = extract_conditions(text)
if conditions is None:
continue
lang = js_file.parent.name
for key, condition in conditions.items():
if key in seen and seen[key] != condition:
print(
f"WARN: condition ident '{key}' conflict:\n [{seen_lang[key]}] {seen[key]}\n [{lang}] {condition}"
)
else:
seen[key] = condition
seen_lang[key] = lang
if lang in results:
results[lang].update(conditions)
else:
results[lang] = conditions
return results
def build_rules_rs(res: dict[str, list[str]], out_path: Path) -> None:
idt = " " * 4
with out_path.open("w", encoding="utf-8") as f:
w = f.write
w("//! This file was generated and should not be edited directly.\n")
w("//! The source code can be found at scripts/scan_yomitan.py\n\n")
w("use crate::lang::Lang;\n\n")
w("pub fn is_valid_rule(lang: Lang, rule: &str) -> bool {\n")
w(f"{idt}match lang {{\n")
for lang, conditions in res.items():
if not conditions:
continue
variants = " | ".join(f'"{c}"' for c in conditions)
w(f"{idt * 2}Lang::{lang.title()} => matches!(rule, {variants}),\n")
w(f"{idt * 2}_ => false,\n")
w(f"{idt}}}\n")
w("}\n")
print(f"Wrote rust code @ {out_path}")
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument(
"repo_path",
type=Path,
nargs="?",
default="../yomitan/", help="Path to local yomitan repo",
)
parser.add_argument(
"--out", type=Path, default=None, help="Optional JSON output path"
)
args = parser.parse_args()
results = scan_yomitan_repo(args.repo_path)
if args.out:
res = {}
for k, v in results.items():
if k not in res:
res[k] = []
for k1 in v.keys():
res[k].append(k1)
print(res)
with args.out.open("w", encoding="utf-8") as f:
json.dump(res, f, indent=4, ensure_ascii=False)
print(f"Wrote results to {args.out}")
rules_rs = Path("src/dict/rules.rs")
build_rules_rs(res, rules_rs)
if __name__ == "__main__":
main()