import jsyaml from '../../../libs/js-yaml.mjs'; const STATE_TAG_RE = /<\s*state\b[^>]*>([\s\S]*?)<\s*\/\s*state\s*>/gi; export function extractStateBlocks(text) { const s = String(text ?? ''); if (!s || s.toLowerCase().indexOf(' 块内容 -> ops[] * 单行支持运算符,多行只支持覆盖 set(YAML) */ export function parseStateBlock(content) { const results = []; const lines = String(content ?? '').split(/\r?\n/); let pendingPath = null; let pendingLines = []; const flushPending = () => { if (!pendingPath) return; // 没有任何缩进行:视为 set 空字符串 if (!pendingLines.length) { results.push({ path: pendingPath, op: 'set', value: '' }); pendingPath = null; pendingLines = []; return; } try { // 去除公共缩进 const nonEmpty = pendingLines.filter(l => l.trim()); const minIndent = nonEmpty.length ? Math.min(...nonEmpty.map(l => l.search(/\S/))) : 0; const yamlText = pendingLines .map(l => (l.trim() ? l.slice(minIndent) : '')) .join('\n'); const obj = jsyaml.load(yamlText); results.push({ path: pendingPath, op: 'set', value: obj }); } catch (e) { results.push({ path: pendingPath, op: 'set', value: null, warning: `YAML 解析失败: ${e.message}` }); } finally { pendingPath = null; pendingLines = []; } }; for (const raw of lines) { const trimmed = raw.trim(); if (!trimmed || trimmed.startsWith('#')) continue; const indent = raw.search(/\S/); if (indent === 0) { flushPending(); const colonIdx = findTopLevelColon(trimmed); if (colonIdx === -1) continue; const path = trimmed.slice(0, colonIdx).trim(); const rhs = trimmed.slice(colonIdx + 1).trim(); if (!path) continue; if (!rhs) { pendingPath = path; pendingLines = []; } else { results.push({ path, ...parseInlineValue(rhs) }); } } else if (pendingPath) { pendingLines.push(raw); } } flushPending(); return results; } function findTopLevelColon(line) { let inQuote = false; let q = ''; let esc = false; for (let i = 0; i < line.length; i++) { const ch = line[i]; if (esc) { esc = false; continue; } if (ch === '\\') { esc = true; continue; } if (!inQuote && (ch === '"' || ch === "'")) { inQuote = true; q = ch; continue; } if (inQuote && ch === q) { inQuote = false; q = ''; continue; } if (!inQuote && ch === ':') return i; } return -1; } function unescapeString(s) { return String(s ?? '') .replace(/\\n/g, '\n') .replace(/\\t/g, '\t') .replace(/\\r/g, '\r') .replace(/\\"/g, '"') .replace(/\\'/g, "'") .replace(/\\\\/g, '\\'); } /** * 单行内联值解析 */ export function parseInlineValue(raw) { const t = String(raw ?? '').trim(); if (t === 'null') return { op: 'del' }; // (负数) 用于强制 set -5,而不是 inc -5 const parenNum = t.match(/^\((-?\d+(?:\.\d+)?)\)$/); if (parenNum) return { op: 'set', value: Number(parenNum[1]) }; // +10 / -20 if (/^\+\d/.test(t) || /^-\d/.test(t)) { const n = Number(t); if (Number.isFinite(n)) return { op: 'inc', delta: n }; } // +"str" / +'str' const pushD = t.match(/^\+"((?:[^"\\]|\\.)*)"\s*$/); if (pushD) return { op: 'push', value: unescapeString(pushD[1]) }; const pushS = t.match(/^\+'((?:[^'\\]|\\.)*)'\s*$/); if (pushS) return { op: 'push', value: unescapeString(pushS[1]) }; // +[...] if (t.startsWith('+[')) { try { const arr = JSON.parse(t.slice(1)); if (Array.isArray(arr)) return { op: 'push', value: arr }; return { op: 'set', value: t, warning: '+[] 不是数组,作为字符串' }; } catch { return { op: 'set', value: t, warning: '+[] JSON 解析失败,作为字符串' }; } } // -"str" / -'str' const popD = t.match(/^-"((?:[^"\\]|\\.)*)"\s*$/); if (popD) return { op: 'pop', value: unescapeString(popD[1]) }; const popS = t.match(/^-'((?:[^'\\]|\\.)*)'\s*$/); if (popS) return { op: 'pop', value: unescapeString(popS[1]) }; // -[...] if (t.startsWith('-[')) { try { const arr = JSON.parse(t.slice(1)); if (Array.isArray(arr)) return { op: 'pop', value: arr }; return { op: 'set', value: t, warning: '-[] 不是数组,作为字符串' }; } catch { return { op: 'set', value: t, warning: '-[] JSON 解析失败,作为字符串' }; } } // 裸数字 set if (/^-?\d+(?:\.\d+)?$/.test(t)) return { op: 'set', value: Number(t) }; // "str" / 'str' const strD = t.match(/^"((?:[^"\\]|\\.)*)"\s*$/); if (strD) return { op: 'set', value: unescapeString(strD[1]) }; const strS = t.match(/^'((?:[^'\\]|\\.)*)'\s*$/); if (strS) return { op: 'set', value: unescapeString(strS[1]) }; if (t === 'true') return { op: 'set', value: true }; if (t === 'false') return { op: 'set', value: false }; // JSON set if (t.startsWith('{') || t.startsWith('[')) { try { return { op: 'set', value: JSON.parse(t) }; } catch { return { op: 'set', value: t, warning: 'JSON 解析失败,作为字符串' }; } } // 兜底 set 原文本 return { op: 'set', value: t }; }