/* -*- coding: utf-8 -*- */ /** * 단가산출 편집기 글 도우미 (scr_edit_lang.js) — 코드 줄 만들기(F6/F7 · 중기비 Q·A·T·N) · 계표시 · * 금액결과 삭제 · 문법 강조 · 문법설명 · 순수 계산 * 근거: REA 30 TWM_EDIT_SAN(cBox_Trans 5택 · ComOp / * · ComQty Q A T N · Chk0Not 「단가가 0원인 비목은 * 입력안함」 · Act_InsSum1~5 · Act_InsCon1~5 · Memo_Use 문법 (1)~(10) · CBoxSystax · CBoxEnable) * · result_text_restoration_rules(소계~총합계 · 제외금액 10종 · 참조줄 '=' 까지 남김) · 지식DB 26 §3 */ const StmateSmtLang = (() => { const esc = (s) => String(s == null ? "" : s).replace( /[&<>"]/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[c], ); const num = (v) => Number(v) || 0; // 계표시 — 레벨 1~5 · 제외금액 const MARK_NAMES = ["소계", "계", "합계", "총계", "총합계"]; const marker = (level, excluded) => `(${excluded ? "-" : ""}${"=".repeat(Math.max(1, Math.min(5, level)))})`; // 일반코드 입력 꼴(cBox_Trans) 0 코드 · 1 명칭 코드 · 2 명칭 규격 코드 · 3 명칭 규격 코드 단위 · 4 코드번호만 const TRANS = [ "코드 * 수량(0) =", "명칭 코드 * 수량(0) =", "명칭 규격 코드 * 수량(0) =", "명칭 규격 코드 * 수량(0) 단위 =", "코드번호(만 입력)", ]; const COMP_LABEL = { L: "노 무 비", M: "재 료 비", S: "경 비" }; /** * 코드 줄 만들기 — row = COSTN 행 · opts { trans, op('/'|'*'), qtyVar('Q'|'A'|'T'|'N'), skipZero, qty } * 중기(X)·일위대가(B)·산근(D)는 성분마다 한 줄(「'노 무 비' &X00006L& / Q =」) · 0원 성분은 skipZero 면 뺌 */ function codeLines(code, row = {}, opts = {}) { const c = String(code || "").trim(); const q = opts.qty === undefined || opts.qty === "" ? "0" : String(opts.qty); if (/^[XBD]/.test(c)) { const op = opts.op === "*" ? "*" : "/"; const v = /^[QATN]$/.test(opts.qtyVar || "") ? opts.qtyVar : "Q"; return ["L", "M", "S"] .filter((k) => !opts.skipZero || num(row[`${k}AMT1`]) !== 0) .map((k) => `'${COMP_LABEL[k]}' &${c}${k}& ${op} ${v} =`); } const nm = String(row.NAME || "").trim(), sz = String(row.SIZE || "").trim(), un = String(row.UNIT || "").trim(); const t = Number(opts.trans) || 0; if (t === 4) return [`&${c}&`]; const label = t === 1 ? nm : t >= 2 ? `${nm} ${sz}`.trim() : ""; const unit = t === 3 && un ? `'${un}'` : ""; return [`${label ? `'${label}' ` : ""}&${c}& * ${q}${unit} =`]; } /** * 금액결과 삭제(Act_Del_ResultAmt) — 「소계」…「총합계(제외금액)」 글을 계표시로 · 참조 줄은 마지막 '=' 까지 * 남기고 뒤는 따옴표 주석 · '->' · 금액변수(A..)만 · 그 밖의 '=' 없는 글 줄은 따옴표로 감쌈 */ function stripResults(text) { const marks = {}; MARK_NAMES.forEach((n, i) => { marks[n] = marker(i + 1, false); marks[`${n}(제외금액)`] = marker(i + 1, true); }); return String(text || "") .split(/\r?\n/) .map((ln) => { const t = ln.trim(); if (marks[t]) return ln.replace(t, marks[t]); const amps = (ln.match(/&/g) || []).length; const eq = ln.lastIndexOf("="); if (amps === 2 && eq > ln.lastIndexOf("&")) { // ':= A01' 금액변수 · '->' 제외 · 첫 따옴표부터의 단위 주석만 남김 const tail = ln.slice(eq + 1); const q = tail.indexOf("'"); const lbl = /^\s*A/.test(tail) ? tail.trim().slice(0, 3) : ""; const arrow = tail.includes("->") ? `${lbl ? " " : ""}->` : ""; return ln.slice(0, eq + 1) + lbl + arrow + (q >= 0 && !arrow ? tail.slice(q) : ""); } if ( t && !t.includes("=") && !t.includes("'") && !/^\(-?=+\)/.test(t) && !t.startsWith(";;") && !t.startsWith("//") ) return ln.replace(t, `'${t}'`); return ln; }) .join("\r\n"); } // 문법 강조 낱말 — 원본 TWM_EDIT_SAN Syn1.KeyWords(함수·상수) · 대소문자 가리지 않음 const SYN_KEYWORDS = "ABS COS CUT EXP FACT IIF INT LA LN LOG MA MOD PI POWER ROUND ROUNDDOWN SIN SQRT STMATE TAN"; const KEYS = new Set(SYN_KEYWORDS.split(" ")); // 문법 강조 — 주석문 · Text(따옴표) · 숫자 · 함수 낱말 · 변수·단가코드 · 계표시 function highlight(line) { const s = String(line); const out = []; let i = 0; while (i < s.length) { const rest = s.slice(i); let m; if (rest.startsWith("//")) { out.push(`${esc(rest)}`); break; } if (rest[0] === "'") { const j = s.indexOf("'", i + 1); const seg = j < 0 ? rest : s.slice(i, j + 1); out.push(`${esc(seg)}`); i += seg.length; continue; } if ((m = rest.match(/^&[^&\s]*&/)) || (m = rest.match(/^~[^~]*~/))) { out.push(`${esc(m[0])}`); i += m[0].length; continue; } if ((m = rest.match(/^\(-?=+\)(?::\s*T\d{2})?/))) { out.push(`${esc(m[0])}`); i += m[0].length; continue; } if ((m = rest.match(/^\d+\.?\d*/))) { out.push(`${esc(m[0])}`); i += m[0].length; continue; } if ((m = rest.match(/^[A-Za-z_][A-Za-z_0-9]*/))) { const cls = KEYS.has(m[0].toUpperCase()) ? "syn-key" : "syn-var"; out.push(`${esc(m[0])}`); i += m[0].length; continue; } out.push(esc(rest[0])); i += 1; } return out.join(""); } // 문법설명(CBoxSystax) — TWM_EDIT_SAN Memo_Use 요지 const GRAMMAR_HELP = [ "(1) Text — 계산과 무관한 글은 양쪽을 ' 로 막음", "(2) 변수 — 「K = 1.0」 · 첫 글자 영문 · 대소문자 구분 없음 · 영문 뒤 숫자 가능(Q1, Q2)", "(3) 계산식 — 「Q = qo*K*E/Cm」 소수 그대로 · 끝에 '=' 를 붙이면 단가산출서 소수 자리(ZCD)로 반올림", " 자리 함수 round(A/B,4) · int(B/A) · A/B=[?:4R] 반올림 · [?:2U] 올림 · [?:0C] 절사", "(4) 단가코드 — 「&L00084&」 앞뒤에 & · 짧게 &L84&", "(5) 그림코드 — 「IMG(I00001)」 · 한 줄에 하나", "(6) 단가코드+단가코드 불가 — 「0.208 * &L84& =」 「0.208 * &L87& =」 처럼 줄을 나눔", "(7) 계표시 — (=)소계 (==)계 (===)합계 (====)총계 (=====)총합계 · (-=) 제외금액(셈은 하되 계에서 뺌)", "(8) 주석문 — 「//」 뒤는 계산·인쇄에서 빠짐(한 줄 안)", "(9) 손료계산 — [Q=0.208] * &L84& :=A01 금액변수 누적 · :=A01 -> 누적하되 제외 · (=):T01 누계번지", " &PA01L:S& * 10'%' = · &RA01L:S& * 0.1 = · &PC_S:M& 마지막 계표시 이후 · &PS_:M& 처음부터", "(10) 결과금액 조정 — 합계 × M·L·S 배율 · 결과적용 비목(각 해당 · 재료 · 노무 · 경비)", "수량변수 — ~Q00001.QTY1~ 기본 수량변수 · ~Q00001.별명~ 확장 수량변수(수량산출서)", "함수 — ∏ INT SIN COS TAN SQRT √ ROUND ROUNDDOWN ROUNDUP CUT LOG LN ABS EXP MOD POWER FACT IIF(조건, 참, 거짓)", ]; return { MARK_NAMES, TRANS, marker, codeLines, stripResults, highlight, GRAMMAR_HELP, SYN_KEYWORDS, esc, }; })(); if (typeof window !== "undefined") window.STMATE_SMT_LANG = StmateSmtLang; if (typeof module !== "undefined" && module.exports) module.exports = StmateSmtLang;