Files
Aislo/M02_MasterTemplete/M02_San_Est.py
T
eomsangdonandClaude Opus 5.5 74446990f8 refactor(812-1): 옛 엑셀 엔진 걷기 1 — San_Est 글 추정 사슬 · San_Eval EST 셈 · 엑셀 검산 · San_Emit.est · San_Book.est_blocks · load
- 남김: San_Est.tokens · num · NUMRE(NativeEst 가 씀) · San_Eval.run_stmate · stmate_block(STC · STmate) · San_Emit.stmate
- 시험 고침: 802_4b1 · 797_1 = 세 줄 → 묶음 한 줄(원파일 길 꼴) · 797_2 = 블록 번은 엑셀 둘 꼴에서

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 07:37:28 +09:00

92 lines
3.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""EST 식 글 토큰(PLAN 793-1 · 812-1 · eval 없음) — 원파일 산근 변환(`M02_Doc_NativeEst`)이 쓰는 수 · 토큰 읽기만.
옛 엑셀 글 추정 사슬(줄 꼴 · 역할 · 값 역색인 · `parse_block`)은 812-1 엑셀 읽기기(`M02_San_Xlsx`)로 바뀌어 걷음.
"""
from __future__ import annotations
import re
from M02_MasterTemplete import M02_San_Expr as ex
NUMRE = r"\d{1,3}(?:,\d{3})+(?:\.\d*)?|\d+(?:\.\d*)?|\.\d+"
EPS = ex.EPS
OPS = {"×": "*", "x": "*", "X": "*", "*": "*", "÷": "/", "/": "/", "+": "+", "-": "-"}
OPS |= {"-": "-", "−": "-"}
_UNIT = re.compile(r"\s*(?:[A-Za-z가-힣㎡㎥㎏ℓ㎝㎜°˚’'\"Øø]+(?:\s*/\s*[A-Za-z가-힣㎡㎥㎏ℓ]+)?)")
def num(s: str) -> float:
return float(s.replace(",", "").rstrip(".") or 0)
def tokens(seg: str) -> list[tuple] | None:
"""식 조각 → Expr 토큰 · 숫자 식이 아니면(기호 · 수 둘 잇달아 · 연산 없음) None."""
s = seg.replace("(", "(").replace(")", ")").replace("x", "x")
prev = None
while prev != s: # 수 없는 괄호 글 「(품할증)」
prev = s
s = re.sub(r"\([^()\d]*\)", " ", s)
out: list[tuple] = []
i, want, depth = 0, True, 0
while i < len(s):
ch = s[i]
if ch.isspace():
i += 1
continue
m = re.match(NUMRE, s[i:])
if m and (i == 0 or not (s[i - 1].isalpha() and s[i - 1].isascii())):
if not want:
return None
out.append(("수", num(m.group(0))))
i += len(m.group(0))
want = False
while i < len(s): # 단위 글 「인」 「㎡」 「본/일」
m2 = _UNIT.match(s[i:])
if m2 and m2.group(0).strip() and not re.fullmatch(r"\s*[xX]", m2.group(0)):
i += len(m2.group(0))
continue
break
continue
if ch == "%":
if want:
return None
out.append(("%",))
i += 1
continue
if ch in OPS:
if ch in "xX" and want:
return None
if ch == "/" and not re.match(r"\s*[\d(.]", s[i + 1 :]):
return None
if ch in "--−" and want:
out.append(("연산", "-"))
i += 1
continue
if want:
return None
out.append(("연산", OPS[ch]))
i += 1
want = True
continue
if ch == "(":
if not want:
return None
depth += 1
out.append(("(",))
i += 1
continue
if ch == ")":
depth -= 1
if depth < 0 or want:
return None
out.append((")",))
i += 1
continue
if ch.isalpha() or ch == "_":
return None
i += 1 # 장식(₩ 등)
if depth != 0 or want or not any(t[0] in ("연산", "%") for t in out):
return None
return out