// 원가 및 단가산출의 사칙연산·괄호·[항목명] 참조를 평가한다. const EstFormula = (() => { function evaluate(expression, values) { const tokens = expression.match(/\s+|\d+(?:\.\d+)?|\[[^\]]+\]|[()+*/-]/g) || []; if (tokens.join('') !== expression) throw Error('지원하지 않는 수식 문자'); const items = tokens.filter(t => !/^\s+$/.test(t)); let i = 0; function primary() { const token = items[i++]; if (token === '(') { const n = sum(); if (items[i++] !== ')') throw Error('괄호 오류'); return n; } if (token === '-') return -primary(); if (token === '+') return primary(); if (/^\d/.test(token || '')) return Number(token); if (token?.startsWith('[')) { const key=token.slice(1,-1); if (!Object.hasOwn(values,key)) throw Error(`참조 항목 없음: ${key}`); return values[key]; } throw Error('수식 오류'); } function product() { let n = primary(); while (items[i] === '*' || items[i] === '/') { const op=items[i++], v=primary(); if (op === '/' && v === 0) throw Error('0으로 나눌 수 없음'); n=op === '*' ? n*v : n/v; } return n; } function sum() { let n = product(); while (items[i] === '+' || items[i] === '-') { const op=items[i++], v=product(); n=op === '+' ? n+v : n-v; } return n; } const result=sum(); if (i !== items.length || !Number.isFinite(result)) throw Error('수식 오류'); return result; } return { evaluate }; })(); if (typeof module !== 'undefined') module.exports = EstFormula;