fix(est): parse marine quantity formulas

This commit is contained in:
2026-11-06 22:12:04 +09:00
parent 064bc6df4c
commit 8f9251d627
4 changed files with 103 additions and 7 deletions
+25 -6
View File
@@ -16,9 +16,19 @@ const EstText = (() => {
String(text)
.replace(/\x0e[^\x0e]*\x0e|\x13[^\x13]*\x13|\x15[^\x15]*\x15|\([가-힣][^()]*\)/g, "")
.replace(/[;;]/g, ":");
const mathBraces = (text) =>
String(text).replace(/\{([^{}]*)\}/g, (_, content, offset, source) =>
(!source.slice(0, offset).trimEnd() ||
/[=+×÷*/(]$/.test(source.slice(0, offset).trimEnd())) &&
/\d/.test(content) &&
/[+×÷*/]/.test(content) &&
!/[가-힣]/.test(content)
? `(${content})`
: "",
);
function expression(text) {
return clean(
String(text)
mathBraces(text)
.replace(///g, "/")
.replace(
/\x0e[^\x0e]*\x0e|\x13[^\x13]*\x13|\x15[^\x15]*\x15|\{[^}]*\}|〔[^〕]*〕|[[^]]*]|\[[가-힣][^\]]*\]|\([가-힣][^()]*\)|\(\s*\d+(?:\.\d+)?\s*[˚°]\s*\)|<[^>]*>/g,
@@ -26,13 +36,14 @@ const EstText = (() => {
),
)
.replace(
/(?<![A-Za-z0-9_가-힣.])(\d[\d,.]*|\.\d+)\s*(?:공\/?㎥|공|단|면|겹|경간|개월|월|년|인|㎥|㎢|㎠|㎡|hr|sec|min|set|kwh|kw|km|ton|kg|㎏|㎞|ℓ|회|개소|개|대|분|초|본|장|매|병|조|식|일|시간|원|포|동|주|톤|배|번|줄|μ|₩|₩|\\|EA|㎜|㎝|mm|cm|m3|m2|m|g|L|a)(?:\/(?:개소|경간|개|대|본|인|분|포|매|주|톤|공|회|조|span|set|ton|\$|hr|일|㎥|㎡|m3|m2|m|a|EA))?/gi,
/(?<![A-Za-z0-9_가-힣.])(\d[\d,.]*|\.\d+)\s*(?:공\/?㎥|공|단|면|겹|경간|개월|월|년|인|㎥|㎢|㎠|㎡|hr|sec|min|set|kwh|kw|km|knot|ton|kg|㎏|㎞|ℓ|구멍|구역|지점|마일|척|열|회|개소|개|대|분|초|본|장|매|병|조|식|일|시간|원|포|동|주|톤|배|번|줄|μ|₩|₩|\\|EA|㎜|㎝|mm|cm|m3|m2|m|g|L|a)(?:\/(?:개소|경간|구멍|지점|개|대|본|인|분|포|매|주|톤|공|회|조|span|set|ton|\$|hr|일|㎥|㎡|m3|m2|m|a|EA))?/gi,
"$1",
)
.replace(/(?<=\d)\s*\/\s*(?:일|㎥|㎡|hr)(?=\s|$|[×*+)])/gi, "")
.replace(/[】]〕]\s*$/, "")
.replace(/(?<=\))\s*(?:개|인|본|면|단|회|㎥|㎡)(?:\/(?:㎡|㎥|hr))?/g, "")
.replace(/^\s*[-*•※]?\s*[가-힣㎡㎥][가-힣㎡㎥\s]*\s+(?=[\d.(])/, "")
.replace(/^\s*[-*•※⊙◎]?\s*[가-힣㎡㎥][가-힣㎡㎥\s]*\s+(?=[\d.(])/, "")
.replace(/^\s*[⊙◎]\s*/, "")
.replace(/[①-⑳]/g, "")
.replace(/=\s*$/, "");
}
@@ -254,7 +265,14 @@ const EstText = (() => {
let componentDeclaration;
try {
if (selectionError) throw Error(selectionError);
if ((matched || /^\s*[A-Za-z][A-Za-z0-9]*\s*=/.test(text)) && /─/.test(text)) {
if (!matched)
for (const m of text.matchAll(/\(([A-Za-z][A-Za-z0-9]*)\)\s*[::]\s*([-+]?\d[\d,.]*)/g))
variables[m[1]] = numeric(m[2]);
if (
(matched || /^\s*[A-Za-z][A-Za-z0-9]*\s*=/.test(text)) &&
/─/.test(text) &&
!(!matched && /⇒.*─/.test(text))
) {
const above = clean(rows[index - 1]?.LineData),
below = clean(rows[index + 1]?.LineData);
const columns = (value) =>
@@ -321,15 +339,16 @@ const EstText = (() => {
? []
: [
...assignmentText.matchAll(
/(?:^\s*| {2,}|[,,]\s*|(?<=[가-힣])\s+|(?<=hr)\s+|[::]\s*|['"]\s*)([A-Za-z][A-Za-z0-9]*)\s*=/g,
/(?:^\s*| {2,}|[,,]\s*|(?<=[가-힣])\s+|(?<=hr)\s+|[::]\s*|['"]\s*)([A-Za-z\u0370-\u03ff][A-Za-z0-9\u0370-\u03ff]*)\s*=/g,
),
].filter((match) => !/[+*/÷×−-]\s*$/.test(assignmentText.slice(0, match.index)));
for (let j = 0; j < assignments.length; j++) {
const assignment = assignments[j],
end = assignments[j + 1]?.index ?? text.length;
let body = text.slice(assignment.index + assignment[0].length, end);
if (!matched && /⇒.*─/.test(body)) body = body.split("⇒")[0];
if (!matched && /^\s*\*.*\*\s*$/.test(text)) body = body.replace(/\*\s*$/, "");
body = body.replace(/\{[^}]*\}/g, "");
body = mathBraces(body);
if (/['"]\s*[A-Za-z][A-Za-z0-9]*\s*=/.test(assignment[0]))
body = body.replace(/['"]\s*$/, "");
if (!matched) body = body.split("{")[0];