52 lines
3.3 KiB
JavaScript
52 lines
3.3 KiB
JavaScript
// tools/secretary-armor.mjs
|
|
// Детерминированный разбор/спасение ответов флэша (принцип В1).
|
|
|
|
export const normLens = (l) => {
|
|
const t = String(l || '').trim().replace(/^л/i, 'Л');
|
|
return /^Л\d$/.test(t) ? t : (/^\d$/.test(t) ? 'Л' + t : t || 'Л?');
|
|
};
|
|
|
|
const ОПОРЫ = ['внутр', 'внеш', 'воля', 'догадка'];
|
|
const ОП_SYN = {
|
|
внутренний: 'внутр', код: 'внутр', файл: 'внутр', проект: 'внутр', обмен: 'внутр', реплика: 'внутр',
|
|
ответ: 'внутр', цитата: 'внутр', текст: 'внутр', строка: 'внутр', внешний: 'внеш', url: 'внеш',
|
|
веб: 'внеш', док: 'внеш', документация: 'внеш', владелец: 'воля', юзер: 'воля', пользователь: 'воля',
|
|
запрос: 'воля', директива: 'воля', гипотеза: 'догадка', предположение: 'догадка', домысел: 'догадка',
|
|
незавершённый: 'догадка', неизвестно: 'догадка',
|
|
};
|
|
/** Паспорт-опора -> ровно одно слово словаря; неизвестное -> честно «догадка». */
|
|
export function one(raw) {
|
|
const w = String(raw || '').trim().toLowerCase().split(/[\s,;:|]+/)[0].replace(/[.,;:]+$/, '');
|
|
if (ОПОРЫ.includes(w)) return w;
|
|
return ОП_SYN[w] || 'догадка';
|
|
}
|
|
|
|
/** Спасение по-объектно: лезем В массив […] и парсим КАЖДЫЙ объект; битый пропускаем. */
|
|
export function salvageObjects(text) {
|
|
const lb = text.indexOf('['), rb = text.lastIndexOf(']');
|
|
const body = (lb >= 0 && rb > lb) ? text.slice(lb + 1, rb) : text;
|
|
const out = [];
|
|
let depth = 0, start = -1, inStr = false, esc = false;
|
|
for (let i = 0; i < body.length; i++) {
|
|
const c = body[i];
|
|
if (inStr) { if (esc) esc = false; else if (c === '\\') esc = true; else if (c === '"') inStr = false; continue; }
|
|
if (c === '"') inStr = true;
|
|
else if (c === '{') { if (depth === 0) start = i; depth++; }
|
|
else if (c === '}') { depth--; if (depth === 0 && start >= 0) { const chunk = body.slice(start, i + 1).replace(/,(\s*[}\]])/g, '$1'); try { out.push(JSON.parse(chunk)); } catch { /* битый — пропустить */ } start = -1; } }
|
|
}
|
|
return out;
|
|
}
|
|
|
|
/** Толерантный парсер: ```-обёртка, висячая запятая, голый массив/объект; слом -> спасение по-объектно. */
|
|
export function parseLoose(text, expectKey) {
|
|
let s = String(text || '').trim().replace(/^```(?:json)?\s*\n?/i, '').replace(/\n?```$/, '').trim();
|
|
const obj = s.indexOf('{'), arr = s.indexOf('[');
|
|
if (obj >= 0 || arr >= 0) { const start = arr < 0 ? obj : obj < 0 ? arr : Math.min(obj, arr); s = s.slice(start); }
|
|
s = s.replace(/,(\s*[}\]])/g, '$1');
|
|
let p; try { p = JSON.parse(s); }
|
|
catch { const salv = salvageObjects(s); return { [expectKey]: salv.length ? salv : [] }; }
|
|
if (Array.isArray(p)) return { [expectKey]: p };
|
|
if (p && typeof p === 'object') { if (!Array.isArray(p[expectKey])) p[expectKey] = p[expectKey] ? [p[expectKey]] : []; return p; }
|
|
return { [expectKey]: [] };
|
|
}
|