- 걷음: 43784a4c0 의 옛 수식 나무 위 고침(서버 · 화면 · css) = 그 앞 판으로 되돌림
- 남김: master_equation_sign.py · ui_template_equation_text.ts · ui_template_equation_edit.ts(다시 쓸 조각 · 지금 안 부름) · text 는 `입력글` 을 제 타입에
- 지움: test_801_1_equation_inline.py — 걷은 옛 나무 고침을 재는 시험
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
98 lines
4.2 KiB
TypeScript
98 lines
4.2 KiB
TypeScript
/* =============================================================================
|
||
* ui_template_equation_text.ts
|
||
* 부품 「수식」 글 꼴(801-1 · 요구 662) — 수식 나무 ↔ 키보드 한 줄 식의 한 곳(화면 · DOM 없음 · 시험 `test_801_1_equation_inline`).
|
||
* textOf = 나무 → 키보드 식(변수 = 열쇠 · 표 찾기 · 요소 · 로직 부르기 = 서버 `입력글`(별칭 꼴) › 원문 식 · 만약 = 세 갈래 모두) · `swap` 마디는 그 글로
|
||
* leavesOf = 보이는 잎(수 · 글 · 변수 · 블록) 차례 — 값 식 그림의 숫자 차례와 같음(탄 갈래만 · 반올림 감싸개는 첫 인자만)
|
||
* 보이기 규칙 = `taken`(만약 = 탄 갈래만) · `HIDDEN`(반올림 = 자리 감싸개 숨김) — 셈 · 식은 그대로(그림만)
|
||
* ========================================================================== */
|
||
|
||
import type { EqNode as Base } from "./ui_template_equation";
|
||
|
||
/** 서버 마디 + 키보드 글(`입력글` · 별칭 꼴 · 없으면 식) */
|
||
type EqNode = Base & { 입력글?: string };
|
||
|
||
/** 그림에서 감싸개를 숨기는 함수(첫 인자만 보임) */
|
||
export const HIDDEN = new Set(["반올림"]);
|
||
|
||
/** 만약 — 탄 갈래 마디(모르면 null = 세 갈래 모두 보임) */
|
||
export const taken = (n: EqNode): EqNode | null =>
|
||
n.걸림 === 1 ? (n.참 ?? null) : n.걸림 === 2 ? (n.거짓 ?? null) : null;
|
||
|
||
const isOp = (n: EqNode | undefined): boolean => n?.꼴 === "연산";
|
||
const wrap = (text: string, yes: boolean): string => (yes ? `(${text})` : text);
|
||
|
||
/** 나무 → 키보드 한 줄 식 — 엔진 묶음 그대로(분수 · 퍼센트가 연산 오른쪽이면 괄호) */
|
||
export function textOf(n: EqNode | undefined, swap?: Map<EqNode, string>): string {
|
||
if (!n) return "";
|
||
const own = swap?.get(n);
|
||
if (own !== undefined) return own;
|
||
const t = (x: EqNode | string | undefined): string =>
|
||
typeof x === "string" ? x : textOf(x, swap);
|
||
switch (n.꼴) {
|
||
case "수": {
|
||
const raw = typeof n.값 === "number" || typeof n.값 === "string" ? String(n.값) : "";
|
||
return /^-?\d+(\.\d+)?(e-?\d+)?$/i.test(raw) ? raw : (n.글 ?? "").replace(/,/g, "");
|
||
}
|
||
case "글":
|
||
return n.따옴 ? `'${n.글 ?? ""}'` : (n.글 ?? "");
|
||
case "변수":
|
||
return n.갈래 === "입력" || n.갈래 === "중간"
|
||
? (n.열쇠 ?? "")
|
||
: n.입력글 || n.식 || n.열쇠 || "";
|
||
case "블록":
|
||
return n.입력글 || n.식 || n.열쇠 || "";
|
||
case "연산": {
|
||
const right = n.우?.꼴 === "분수" || n.우?.꼴 === "퍼센트";
|
||
return `${t(n.좌)} ${n.기호 ?? ""} ${wrap(t(n.우), right)}`;
|
||
}
|
||
case "분수": {
|
||
const top = n.위 as EqNode;
|
||
const low = n.아래 as EqNode;
|
||
const head = wrap(t(top), isOp(top) && !["×", "÷"].includes(top.기호 ?? ""));
|
||
if (n.사슬 && low?.꼴 === "연산")
|
||
return `${head} / ${wrap(t(low.좌), isOp(low.좌))} / ${wrap(t(low.우), isOp(low.우))}`;
|
||
return `${head} / ${wrap(t(low), isOp(low) || low?.꼴 === "분수")}`;
|
||
}
|
||
case "괄호":
|
||
return `(${t(n.안)})`;
|
||
case "퍼센트":
|
||
return `${t(n.안)} / 100`;
|
||
case "음":
|
||
return `-${t(n.안)}`;
|
||
case "조건":
|
||
return `만약(${t(n.조건)}, ${t(n.참)}, ${t(n.거짓)})`;
|
||
case "함수":
|
||
return `${n.이름 ?? ""}(${(n.인자 ?? []).map((a) => t(a)).join(", ")})`;
|
||
default:
|
||
return n.글 ?? "";
|
||
}
|
||
}
|
||
|
||
/** 보이는 잎 차례 — 값 식 그림(`renderEq`)의 수 · 글 차례와 같음 */
|
||
export function leavesOf(n: EqNode | undefined, out: EqNode[] = []): EqNode[] {
|
||
if (!n) return out;
|
||
const go = (x: EqNode | string | undefined): void => {
|
||
if (x && typeof x !== "string") leavesOf(x, out);
|
||
};
|
||
switch (n.꼴) {
|
||
case "수":
|
||
case "글":
|
||
case "변수":
|
||
case "블록":
|
||
out.push(n);
|
||
break;
|
||
case "조건": {
|
||
const pick = taken(n);
|
||
if (pick) go(pick);
|
||
else [n.조건, n.참, n.거짓].forEach(go);
|
||
break;
|
||
}
|
||
case "함수":
|
||
(HIDDEN.has(n.이름 ?? "") ? (n.인자 ?? []).slice(0, 1) : (n.인자 ?? [])).forEach(go);
|
||
break;
|
||
default:
|
||
[n.좌, n.우, n.위, n.아래, n.안].forEach(go);
|
||
}
|
||
return out;
|
||
}
|