Files
fanlusky a7779d2b05 refactor(engine): 将 engine 从 submodule 内嵌为仓内一等组件
`vendor/MetasequoiaImeEngine` submodule 改为 `engine/` 目录,导入的是
product-lock.json 原本钉住的提交 c810d201,逐文件核对过 blob,因此这是一次
搬迁而不是引擎升级。引擎自己的嵌套 submodule(googlepinyinime-rev、utfcpp、
miniaudio)按各自 gitlink 一并展开,来源与提交见 engine/UPSTREAM.md。

Windows 专门化裁掉了不属于本产品的部分:whisper.cpp 与本地推理 provider、
macOS 示例宿主、整个 dictionary/ 建库流水线、上游 CI 与 release-please 配置。
词库仍按 product-lock.json 在构建时取发布产物。

引擎不再进 product-lock.json:没有 gitlink 就没有可比对的东西,锁里写一个上游
commit 等于宣称这棵树仍等于上游,而第一个 Windows 专门化改动落地时这句话就假了。
verify-contracts / verify-checkout 随之移除,verify-published 保留。

CI 与工具链:引擎源码并入根 scripts/format.sh(删掉引擎自己的那份,避免两套排除
列表分叉);引擎 voice.yml 里的 silero_vad.onnx 摘要校验移到本仓 lock job;
engine-bump-triage 工作流与其测试删除;gitsubmodule 的 dependabot 频率由 daily
改为 monthly。

README、README.en、AGENTS.md、CONTRIBUTING、docs/product-release.md 及各子目录
文档同步说明新的位置与边界。
2026-09-12 03:40:48 +08:00

94 lines
3.7 KiB
Python

#!/usr/bin/env python3
"""Generate the C++ punctuation contract from policy.json."""
import argparse
import json
from pathlib import Path
ROOT = Path(__file__).resolve().parent
SPEC = json.loads((ROOT / "policy.json").read_text(encoding="utf-8"))
simple = SPEC["simple"]
alternating = SPEC["alternating"]
nested = SPEC["nested"]
inputs = [entry["input"] for entry in simple + alternating] + [nested["openingInput"], nested["closingInput"]]
if (SPEC["contractVersion"] != 1 or any(len(value) != 1 or not 32 <= ord(value) < 127 for value in inputs) or
len(set(inputs)) != len(inputs)):
raise SystemExit("Invalid punctuation contract")
def char_literal(value):
return "'" + value.replace("\\", "\\\\").replace("'", "\\'") + "'"
lines = [
"// Generated by contracts/punctuation/generate.py; edit policy.json instead.",
"#pragma once",
"#include <array>",
"#include <string_view>",
"",
"namespace metasequoia::punctuation_contract",
"{",
f"inline constexpr int version = {SPEC['contractVersion']};",
"struct Mapping",
"{",
" char input;",
" const char *output;",
"};",
"struct AlternatingMapping",
"{",
" char input;",
" const char *opening;",
" const char *closing;",
"};",
f"inline constexpr std::array<Mapping, {len(simple)}> simple = {{{{",
]
lines += [f" {{{char_literal(entry['input'])}, {json.dumps(entry['output'], ensure_ascii=False)}}}," for entry in simple]
lines += [
"}};",
f"inline constexpr std::array<AlternatingMapping, {len(alternating)}> alternating = {{{{",
]
lines += [
f" {{{char_literal(entry['input'])}, {json.dumps(entry['opening'], ensure_ascii=False)}, {json.dumps(entry['closing'], ensure_ascii=False)}}},"
for entry in alternating
]
lines += [
"}};",
f"inline constexpr char nested_opening_input = {char_literal(nested['openingInput'])};",
f"inline constexpr char nested_closing_input = {char_literal(nested['closingInput'])};",
f"inline constexpr char nested_opening[] = {json.dumps(nested['opening'], ensure_ascii=False)};",
f"inline constexpr char nested_opening_inner[] = {json.dumps(nested['nestedOpening'], ensure_ascii=False)};",
f"inline constexpr char nested_closing[] = {json.dumps(nested['closing'], ensure_ascii=False)};",
f"inline constexpr char nested_closing_inner[] = {json.dumps(nested['nestedClosing'], ensure_ascii=False)};",
"constexpr bool is_supported(char input)",
"{",
" for (const auto &mapping : simple)",
" if (mapping.input == input) return true;",
" for (const auto &mapping : alternating)",
" if (mapping.input == input) return true;",
" return input == nested_opening_input || input == nested_closing_input;",
"}",
"constexpr const char *simple_output(char input)",
"{",
" for (const auto &mapping : simple)",
" if (mapping.input == input) return mapping.output;",
" return nullptr;",
"}",
"constexpr const AlternatingMapping *alternating_mapping(char input)",
"{",
" for (const auto &mapping : alternating)",
" if (mapping.input == input) return &mapping;",
" return nullptr;",
"}",
"} // namespace metasequoia::punctuation_contract",
"",
]
expected = "\n".join(lines)
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--check", action="store_true")
args = parser.parse_args()
target = ROOT / "policy.h"
if args.check:
if not target.exists() or target.read_text(encoding="utf-8") != expected:
raise SystemExit("Punctuation contract header is stale; run contracts/punctuation/generate.py")
else:
target.write_text(expected, encoding="utf-8")