"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.importTrie = exports.serializeTrie = exports.DATA = void 0; const TrieNode_1 = require("./TrieNode"); const gensequence_1 = require("gensequence"); const convertToTrieRefNodes_1 = require("./convertToTrieRefNodes"); const EOW = '*'; exports.DATA = EOW; function toReferences(node) { return gensequence_1.genSequence(convertToTrieRefNodes_1.convertToTrieRefNodes(node)); } const regExpEscapeChars = /([[\]\\,:{}*])/g; const regExTrailingComma = /,(\}|\n)/g; function escapeChar(char) { return char.replace(regExpEscapeChars, '\\$1'); // lgtm[js/incomplete-sanitization] } function trieToExportString(node, base) { function* walk(node) { if (node.f) { yield EOW; } if (node.r) { const refs = [...node.r].sort((a, b) => (a[0] < b[0] ? -1 : 1)); for (const n of refs) { const [c, r] = n; const ref = r ? r.toString(base) : ''; yield escapeChar(c) + ref + ','; } } } return gensequence_1.genSequence(walk(node)); } function generateHeader(base, comment) { const header = ['#!/usr/bin/env cspell-trie reader', 'TrieXv1', 'base=' + base] .concat(comment ? comment.split('\n').map((a) => '# ' + a) : []) .concat(['# Data:']); return gensequence_1.genSequence(header).map((a) => a + '\n'); } /** * Serialize a TrieNode. * Note: This is destructive. The node will no longer be usable. * Even though it is possible to preserve the trie, dealing with very large tries can consume a lot of memory. * Considering this is the last step before exporting, it was decided to let this be destructive. */ function serializeTrie(root, options = 16) { options = typeof options === 'number' ? { base: options } : options; const { base = 16, comment = '' } = options; const radix = base > 36 ? 36 : base < 10 ? 10 : base; const rows = toReferences(root).map((node) => { const row = [...trieToExportString(node, radix), '\n'].join('').replace(regExTrailingComma, '$1'); return row; }); return generateHeader(radix, comment).concat(rows); } exports.serializeTrie = serializeTrie; function* toIterableIterator(iter) { yield* iter; } function importTrie(linesX) { let radix = 16; const comment = /^\s*#/; const iter = toIterableIterator(linesX); function parseHeaderRows(headerRows) { const header = headerRows.slice(0, 2).join('\n'); const headerReg = /^TrieXv1\nbase=(\d+)$/; /* istanbul ignore if */ if (!headerReg.test(header)) throw new Error('Unknown file format'); radix = Number.parseInt(header.replace(headerReg, '$1'), 10); } function readHeader(iter) { const headerRows = []; // eslint-disable-next-line no-constant-condition while (true) { const next = iter.next(); if (next.done) { break; } const line = next.value.trim(); if (!line || comment.test(line)) { continue; } if (line === exports.DATA) { break; } headerRows.push(line); } parseHeaderRows(headerRows); } const regNotEscapedCommas = /(^|[^\\]),/g; const regUnescapeCommas = /__COMMA__/g; const regUnescape = /[\\](.)/g; const flagsWord = { f: TrieNode_1.FLAG_WORD }; function splitLine(line) { const pattern = '$1__COMMA__'; return line .replace(regNotEscapedCommas, pattern) .split(regUnescapeCommas) .map((a) => a.replace(regUnescape, '$1')); } function decodeLine(line, nodes) { const isWord = line[0] === EOW; line = isWord ? line.slice(1) : line; const flags = isWord ? flagsWord : {}; const children = splitLine(line) .filter((a) => !!a) .map((a) => [a[0], Number.parseInt(a.slice(1) || '0', radix)]) .map(([k, i]) => [k, nodes[i]]); const cNode = children.length ? { c: new TrieNode_1.ChildMap(children) } : {}; return { ...cNode, ...flags }; } readHeader(iter); const n = gensequence_1.genSequence([exports.DATA]) .concat(iter) .map((a) => a.replace(/\r?\n/, '')) .filter((a) => !!a) .reduce((acc, line) => { const { lines, nodes } = acc; const root = decodeLine(line, nodes); nodes[lines] = root; return { lines: lines + 1, root, nodes }; }, { lines: 0, nodes: [], root: {} }); return n.root; } exports.importTrie = importTrie; //# sourceMappingURL=importExportV1.js.map