import type { RuleExpr, RuleLayer } from "@umt/contracts"; // 规则组层的编辑模型:一层 = 顶层 且/或 + 若干条目;条目是条件(可取反)或一个括号组(组内同一种 且/或)。 // 覆盖 MP 规则串的常见写法;更深的嵌套只能在规则串里改(cardsOf 返回 null)。 export type Op = "and" | "or"; export type AtomItem = { kind: "atom"; id: string; neg: boolean }; export type GroupItem = { kind: "group"; op: Op; neg: boolean; items: AtomItem[] }; export type LayerItem = AtomItem | GroupItem; export type LayerCard = { name?: string; op: Op; items: LayerItem[] }; /** 规范化:原子大写,层内 ! > & > |,只加必要括号(与后端 /rules/serialize 同口径) */ export function exprToString(e: RuleExpr, parent: "top" | "and" | "or" | "not" = "top"): string { switch (e.op) { case "atom": return e.id.toUpperCase(); case "not": return `!${exprToString(e.arg, "not")}`; case "and": { const s = e.args.map((a) => exprToString(a, "and")).join(" & "); return parent === "not" ? `(${s})` : s; } case "or": { const s = e.args.map((a) => exprToString(a, "or")).join(" | "); return parent === "and" || parent === "not" ? `(${s})` : s; } } } export function layersToString(layers: RuleLayer[]): string { return layers.map((l) => exprToString(l.expr)).join(" > "); } function atomOf(e: RuleExpr): AtomItem | null { if (e.op === "atom") return { kind: "atom", id: e.id.toUpperCase(), neg: false }; if (e.op === "not" && e.arg.op === "atom") return { kind: "atom", id: e.arg.id.toUpperCase(), neg: true }; return null; } function groupOf(e: RuleExpr): GroupItem | null { let neg = false; let inner = e; if (inner.op === "not") { neg = true; inner = inner.arg; } if (inner.op !== "and" && inner.op !== "or") return null; const items: AtomItem[] = []; for (const a of inner.args) { const at = atomOf(a); if (!at) return null; items.push(at); } return { kind: "group", op: inner.op, neg, items }; } /** 表达式 → 卡片;表达不了(嵌套超过两层)返回 null */ export function cardOf(layer: RuleLayer): LayerCard | null { const e = layer.expr; const single = atomOf(e); if (single) return { name: layer.name, op: "and", items: [single] }; if (e.op === "not") { const g = groupOf(e); return g ? { name: layer.name, op: "and", items: [g] } : null; } if (e.op !== "and" && e.op !== "or") return null; const items: LayerItem[] = []; for (const a of e.args) { const it = atomOf(a) ?? groupOf(a); if (!it) return null; items.push(it); } return { name: layer.name, op: e.op, items }; } export function cardsOf(layers: RuleLayer[]): LayerCard[] | null { const out: LayerCard[] = []; for (const l of layers) { const c = cardOf(l); if (!c) return null; out.push(c); } return out; } function atomExpr(a: AtomItem): RuleExpr { const e: RuleExpr = { op: "atom", id: a.id }; return a.neg ? { op: "not", arg: e } : e; } function itemExpr(it: LayerItem): RuleExpr | null { if (it.kind === "atom") return atomExpr(it); if (it.items.length === 0) return null; const inner: RuleExpr = it.items.length === 1 ? atomExpr(it.items[0] as AtomItem) : { op: it.op, args: it.items.map(atomExpr) }; return it.neg ? { op: "not", arg: inner } : inner; } /** 卡片 → 层;空层返回 null 的 expr(调用方据此提示) */ export function cardToLayer(c: LayerCard): RuleLayer | null { const args = c.items.map(itemExpr).filter((x): x is RuleExpr => !!x); if (args.length === 0) return null; const expr: RuleExpr = args.length === 1 ? (args[0] as RuleExpr) : { op: c.op, args }; return c.name ? { name: c.name, expr } : { expr }; } /** 表达式里引用的全部条件 id */ export function atomsIn(e: RuleExpr, out = new Set()): Set { if (e.op === "atom") out.add(e.id.toUpperCase()); else if (e.op === "not") atomsIn(e.arg, out); else for (const a of e.args) atomsIn(a, out); return out; } export const OP_LABEL: Record = { and: "且", or: "或" };