feat(836-1): 기계 덮음 꼴 · 계산기 짝 규칙 · 서버 저장 정리 — SK 수식 문서 칸 기계덮음

- 자리 = SK 수식.문서.기계덮음 {기계 입력 글: {값 · 조종원 줄 · 잡품 줄}} · 사본 줄 그대로
- 계산기(파이썬 · TS 짝)가 기계 식 부를 때 그 기계 덮음을 얹음 · 덮음 없음 = 문서 그대로
- 서버 저장 · 미리 셈 = 몸 검사 + 원본과 같은 칸 · 안 부르는 기계 뺌
- 거울 · 손셈(굴착기 1.0㎥ 108,958) · 저장 길 지킴 시험 test_836_1

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-15 13:30:57 +09:00
co-authored by Claude Opus 5.5
parent 6f3daf7f3b
commit c261fee9b7
7 changed files with 1085 additions and 9 deletions
@@ -16,6 +16,7 @@ import math
from decimal import ROUND_DOWN, ROUND_HALF_UP, ROUND_UP, Decimal, InvalidOperation
import master_doc as md
import master_doc_cover as mcv
COSTS, BUNDLE = md.COSTS, md.BUNDLE
EPS = 1e-7 # B09_Estimation_Calc_Base.EPS 와 같음
@@ -79,8 +80,9 @@ def number(v):
# ── 셈 한 벌 ──────────────────────────────────────────────────────────
class _Run:
def __init__(self, doc: dict, links, given: dict | None, stack: tuple, memo: dict):
def __init__(self, doc: dict, links, given: dict | None, stack: tuple, memo: dict, cover=None):
self.doc, self.links, self.stack, self.memo = doc, links, stack, memo
self.cover = cover # 기계 덮음(836-1) — 맨 위 문서 칸 · 부름에 얹음
self.given = given or {}
self.vars = {md.var_id(v): v for v in doc.get("변수") or []}
self.lines = {str(x.get("기호") or ""): x for x in doc.get("식") or []}
@@ -296,7 +298,10 @@ class _Run:
given = payload.get("입력") or {}
memo_key = (key, tuple(sorted((k, plain_text(v)) for k, v in given.items())))
if memo_key not in self.memo:
self.memo[memo_key] = calc(doc, self.links, given, (*self.stack, key), self.memo)
doc = mcv.cover_doc(doc, given, self.cover)
self.memo[memo_key] = calc(
doc, self.links, given, (*self.stack, key), self.memo, self.cover
)
got = self.memo[memo_key]
if not got["셈됨"]: # 값 없음(결과 빈 입력 · 표 줄 없음) = 빈 값 · 그 밖(표지) = 멈춤
why = got.get("까닭") or " · ".join(m["글"] for m in got["표지"])
@@ -505,14 +510,19 @@ def rules_of(doc: dict) -> dict:
def calc(
doc: dict, links, given: dict | None = None, stack: tuple = (), memo: dict | None = None
doc: dict,
links,
given: dict | None = None,
stack: tuple = (),
memo: dict | None = None,
cover: dict | None = None,
) -> dict:
"""수식 문서 셈 → {값, 빈값, 줄, 노무비 · 재료비 · 경비 · 계 | 결과,
묶음, 머리, 단계?, 표지, 셈됨, 끝수}.
given = {변수 열쇠: 값}(단가산출 부름이 넘긴 입력) · 예외 없음."""
given = {변수 열쇠: 값}(단가산출 부름이 넘긴 입력) · cover = 기계 덮음(없음 = 문서 칸) · 예외 없음."""
memo = {} if memo is None else memo
stack = stack or ((str(doc["키"]),) if doc.get("키") else ()) # 제 키 — 저를 부르면 순환
run = _Run(doc, links, given, stack, memo)
run = _Run(doc, links, given, stack, memo, mcv.cover_of(doc) if cover is None else cover)
for one in md.check(doc):
if one["갈래"] in ("기호 꼴", "연결 꼴", "금액 줄", "끝수", "겹침"):
run.marks.append(one)