/* ============================================================================= * line_stack_why.ts * 결정된 식 근거 한 줄(요구 695 · 696) — 탄 길 위 만약마다 「조건 → 탄 갈래」 * 옵션 비교 = 「지붕슬래브 「아님」 아님 → 17」 · 수치 비교 = 「H = 9 > 7 → …」 · 그 밖 = 조건 글 + 아님 * 판정 · 결정은 `common_util_formula_doc_view` (decide · whyOf) — 여기는 글만 * ========================================================================== */ import { textOf, type Node } from "../common_util/common_util_formula_doc"; import { whyOf, type Values } from "../common_util/common_util_formula_doc_view"; import { lt } from "./line_stack_text"; export interface WhyCtx { /** 옵션 기호 → 화면 이름(변수 이름) */ label: (id: string) => string; /** 식 글 → 화면 글(표 값 별칭) */ shown: (text: string) => string; /** 값 → 글 */ fmt: (v: unknown) => string; } const OP: Record = { "<": "<", "<=": "≤", ">": ">", ">=": "≥", "==": "=", "!=": "≠", }; function condText(c: Node, yes: boolean, vals: Values, x: WhyCtx): string { const no = lt("If_Not"); if (c[0] === "bin" && (c[1] === "==" || c[1] === "!=")) { const [v, s] = c[2][0] === "var" && c[3][0] === "str" ? [c[2], c[3]] : c[3][0] === "var" && c[2][0] === "str" ? [c[3], c[2]] : [null, null]; if (v && s && typeof vals[v[1]] === "string") return `${x.label(v[1])} 「${s[1]}」${(c[1] === "==" ? !yes : yes) ? no : ""}`; } if (c[0] === "bin" && OP[c[1]]) { const side = (n: Node): string => n[0] === "var" ? `${x.shown(n[1])} = ${x.fmt(vals[n[1]])}` : x.shown(textOf(n)); return `${side(c[2])} ${OP[c[1]]} ${side(c[3])}${yes ? "" : no}`; } return `${x.shown(textOf(c))}${yes ? "" : no}`; } /** 근거 한 줄 — 만약 없으면 빈 글 */ export const whyText = (n: Node, vals: Values, x: WhyCtx): string => whyOf(n, vals) .map((w) => `${condText(w.cond, w.yes, vals, x)} → ${x.shown(textOf(w.to))}`) .join(" · ");