feat(m01): /text 줄마다 요소조각 — 로직 부르기 이름 + 인자 글 (PLAN 5장)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V1MKKZKpUHTPKb513FneU8
This commit is contained in:
2026-09-24 11:13:20 +09:00
co-authored by Claude Sonnet 5
parent cebd49980a
commit 53c23d2739
2 changed files with 60 additions and 0 deletions
@@ -468,6 +468,7 @@ def lines(files: dict, key: str, given: dict) -> dict:
env[""] = {r["이름"]: r["금액"] for r in shown if r["이름"]}
for item, line in zip(items, shown):
조각 = _formula_pieces(item["수량"], env, master, name_of, alias_by_name)
요소 = _item_pieces(item, env, master)
for part, money in _shares(line):
split = part if len(line["비목"]) > 1 else None
one = {
@@ -476,6 +477,7 @@ def lines(files: dict, key: str, given: dict) -> dict:
"이름글": _line_names(item, line, env, master, split, name_of),
"금액": money,
"조각": 조각,
"요소조각": 요소,
}
why = _why(item, line)
made[part].append({**one, "까닭": why} if why else one)
@@ -491,6 +493,7 @@ def lines(files: dict, key: str, given: dict) -> dict:
else [item.get("비목")]
)
조각 = _formula_pieces(item["수량"], env, master, name_of, alias_by_name)
요소 = _item_pieces(item, env, master)
for part in parts:
if part not in mf.COST_ITEMS:
continue
@@ -502,6 +505,7 @@ def lines(files: dict, key: str, given: dict) -> dict:
"이름글": _line_names(item, {}, env, master, split, name_of),
"금액": None,
"조각": 조각,
"요소조각": 요소,
}
)
for extra in row.get("덧줄") or []:
@@ -529,6 +533,42 @@ def lines(files: dict, key: str, given: dict) -> dict:
return _paired({"ok": True, "groups": groups, "": , "별칭": marks})
def _arg_text(node, env: dict, master) -> str:
"""로직 부르기 인자 값을 사람이 읽는 글로 — 요소 키는 이름 · 입력 자리는 지금 값."""
try:
value = mf.evaluate(node, env, master, 0)
except _CALC_ERRORS: # 값 없는 입력 자리 — 이름만
return str(node[1]) if node[0] in ("name", "str") else ""
if isinstance(value, str):
for ref in (value, f"EQ:{value}"): # 기계 입력은 원문번호(0602-0025)
try:
return mc.plain(master.get(ref).get("이름")) or value
except mf.FormulaError:
pass
return value
return fmt(value)
def _item_pieces(item: dict, env: dict, master) -> list[dict]:
"""줄의 단가 자리(요소 · 로직 부르기) 조각 — PLAN 3-1 요소조각."""
ref = item.get("요소")
if item.get("종류") != "로직":
name = str(item.get("이름") or "")
if isinstance(ref, dict): # 재료 고르기 조건 — 품명만
return [{"kind": "요소", "": name}]
piece = _elem_piece(master, ref)
return [{**piece, "": name or piece[""]}]
try:
node = mf.parse(ref)
key = mf.fill(node[1], env)
name = str(master.logic(key).get("이름") or key)
except _CALC_ERRORS:
return [{"kind": "로직", "": str(item.get("이름") or "")}]
args = " · ".join(f"{k} {_arg_text(v, env, master)}" for k, v in node[2])
out = [{"kind": "로직", "": name, "참조": {"테이블": key[:2], "": key}}]
return out + ([{"kind": "", "": args}] if args else [])
def _paired(answer: dict) -> dict:
"""줄마다 짝 번호 — 두 칸(이름 꼴 · 값 꼴)이 같은 차례로 놓이게 1 부터."""
at = 0
@@ -536,6 +576,7 @@ def _paired(answer: dict) -> dict:
for one in group[""]:
at += 1
one[""] = at
one.setdefault("요소조각", [])
return answer
+19
View File
@@ -358,3 +358,22 @@ def test_이름글_번호_별칭은_별칭표로_되돌려진다() -> None:
raw_of = {m["이름"]: m["원문"] for m in got["별칭"]}
back = mt.mc._unmask(left, raw_of)
assert back.count("찾기(") == 9 and "촌락지대가" not in back
def test_요소조각_로직_부르기는_이름과_인자_글_날식_없음() -> None:
"""GC001093 트럭 줄 — 로직 조각(키 GC000268) + 인자 글(기계 이름 · 지역 지금 값)."""
files = store.cm.load(folder=REAL)
row = store.cm.master(REAL).logic("GC001093")
given = dict(store.cm.시험입력(row))
for got in (mt.lines(files, "GC001093", given), mt.lines(files, "GC001093", {})):
lines = _all_lines(got)
assert all("요소조각" in one for one in lines)
truck = [one for one in lines if one["이름"].startswith("트럭")]
assert truck
for one in truck:
head, args = one["요소조각"]
assert head["kind"] == "로직" and head["참조"][""] == "GC000268"
assert args["kind"] == "" and "덤프트럭" in args[""]
assert "GC000268" not in args[""] and "(" not in args[""].split("덤프트럭")[0]
labor = lines[0]["요소조각"]
assert labor[0]["kind"] == "요소" and labor[0][""] == "배관공(수도)"