- 새 master_gate: 호표 줄 수량 0 이 만약 간 갈래(상수 0) 탓이면 옵션꺼짐 true (고르기 · 수 입력 문턱 · 중간값 거침) · 입력 0 · 표 칸 0 · 원문 0 = 칸 없음 - master_formula.run 한 곳에서 붙임 → M01 /calc · SK 셈 답 줄 저절로 실림 - master_sheet_line: /text · M02 sheet 줄에 같은 칸(KEYS) - M01 정본 자료 안 고침 · 금액 · 차례 · cid 그대로(돈 로직 1,440 전후 같음) - 지킴 시험 test_753_1_option_off(GC000211 폼타이 · 진짜 0 · 문턱 · 금액 · /text · SK) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
91 lines
3.9 KiB
Python
91 lines
3.9 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""옵션으로 빠진 줄 — **셈 때 파생**(정본에 칸 없음 · PLAN 753-1 · 요구 588-4).
|
|
|
|
엔진 `run` 호표 줄 덧칸 `옵션꺼짐` = True — 그 줄 수량 0 이 조건 `만약` 의 간 갈래(상수 0) 탓
|
|
(폼타이 12.7 · 9.5 · 7.9 셋 중 고른 규격 아닌 줄 · 폼타이 「아님」 이면 셋 다 ·
|
|
타설길이 문턱으로 고른 장비 아닌 줄). 화면이 숨김(753-2).
|
|
셈 · 금액 · 줄 차례 그대로 · 덧칸만.
|
|
|
|
가름(0 을 낸 마디를 거슬러 감):
|
|
- `만약` = 간 갈래가 상수 0 → 꺼짐(조건 = 고르기 · 수 입력 문턱 · 중간값 모두) ·
|
|
아니면 간 갈래 안으로.
|
|
- 곱 = 0 인 인수 중 하나라도 꺼짐 · 나눗셈 = 분자 · 더하기 · 빼기 = 두 쪽 다 0 이고 하나라도 꺼짐 ·
|
|
끝수 함수 = 첫 인수 · 최소 · 최대 = 0 인 인수 중 꺼짐 · 중간값 이름 = 그 식으로.
|
|
- 입력 값 · 표 값 · 요소 · 로직 부름 · 원문 0 = 진짜 0(안 꺼짐 · 구멍땜 개소 0 · 표 칸 0 등).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from decimal import Decimal
|
|
|
|
import master_formula as mf
|
|
|
|
KEY = "옵션꺼짐"
|
|
_ERRORS = (mf.FormulaError, ArithmeticError, KeyError, TypeError, ValueError, RecursionError)
|
|
_SOURCES = ("name", "find", "field", "ref", "logic") # 값이 입력 · 표에서 오는 마디
|
|
|
|
|
|
class _Trace:
|
|
def __init__(self, master, row: dict, env: dict):
|
|
self.master, self.env = master, env
|
|
self.steps = {s["이름"]: s["식"] for s in row.get("중간") or []}
|
|
self.memo: dict[str, bool] = {}
|
|
|
|
def zero(self, node) -> bool:
|
|
value = mf.evaluate(node, self.env, self.master)
|
|
return not isinstance(value, bool) and value == 0
|
|
|
|
def off(self, node, depth: int = 0) -> bool:
|
|
"""이 마디 값 0 이 옵션 갈래 탓인지."""
|
|
kind = node[0]
|
|
if depth > 40:
|
|
return False
|
|
if kind == "name":
|
|
name = node[1]
|
|
if name not in self.steps:
|
|
return False
|
|
if name not in self.memo:
|
|
self.memo[name] = False # 돌고 도는 이름 막음
|
|
self.memo[name] = self.off(mf.parse(self.steps[name]), depth + 1)
|
|
return self.memo[name]
|
|
if kind == "neg":
|
|
return self.off(node[1], depth + 1)
|
|
if kind == "bin":
|
|
sides = node[2:4]
|
|
if node[1] == "*":
|
|
return any(self.zero(x) and self.off(x, depth + 1) for x in sides)
|
|
if node[1] == "/":
|
|
return self.off(node[2], depth + 1)
|
|
if node[1] in ("+", "-"):
|
|
both = all(self.zero(x) for x in sides)
|
|
return both and any(self.off(x, depth + 1) for x in sides)
|
|
return False
|
|
if kind != "call" or not node[2]:
|
|
return False
|
|
items = node[2]
|
|
if node[1] == "만약":
|
|
taken = items[1] if mf.evaluate(items[0], self.env, self.master) else items[2]
|
|
fixed = not any(x[0] in _SOURCES for x in mf.walk(taken))
|
|
if fixed and self.zero(taken):
|
|
return True
|
|
return self.off(taken, depth + 1)
|
|
if node[1] in ("최소", "최대"):
|
|
return any(self.zero(x) and self.off(x, depth + 1) for x in items)
|
|
return self.off(items[0], depth + 1)
|
|
|
|
|
|
def tag(master, row: dict, lines: list[dict], env: dict) -> list[dict]:
|
|
"""엔진 `run` 호표 줄(덧줄 앞)에 `옵션꺼짐` — 수량 0 인 줄만 가름 · 못 가름 = 안 붙임."""
|
|
trace = None
|
|
for item, line in zip(row.get("호표") or [], lines):
|
|
qty = line.get("수량")
|
|
if not isinstance(qty, Decimal) or qty != 0:
|
|
continue
|
|
trace = trace or _Trace(master, row, env)
|
|
try:
|
|
if trace.off(mf.parse(item["수량"])):
|
|
line[KEY] = True
|
|
except _ERRORS:
|
|
continue
|
|
return lines
|