"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 bufferLines_1 = require("./bufferLines"); const EOW = '$'; const BACK = '<'; const EOL = '\n'; const LF = '\r'; const REF = '#'; const EOR = ';'; const ESCAPE = '\\'; const specialCharacters = new Set([EOW, BACK, EOL, REF, EOR, ESCAPE, LF] .concat('0123456789'.split('')) .concat('`~!@#$%^&*()_-+=[]{};:\'"<>,./?\\|'.split(''))); const specialCharacterMap = new Map([ ['\n', '\\n'], ['\r', '\\r'], ['\\', '\\\\'], ]); const characterMap = new Map([...specialCharacterMap].map((a) => [a[1], a[0]])); exports.DATA = '__DATA__'; function generateHeader(base, comment) { const header = ['#!/usr/bin/env cspell-trie reader', 'TrieXv3', 'base=' + base] .concat(comment ? comment.split('\n').map((a) => '# ' + a) : []) .concat(['# Data:', exports.DATA]); return gensequence_1.genSequence(header).map((a) => a + '\n'); } /** * Serialize a TrieNode. */ 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 cache = new Map(); let count = 0; const backBuffer = { last: '', count: 0 }; function ref(n) { return '#' + n.toString(radix) + ';'; } function escape(s) { return specialCharacters.has(s) ? ESCAPE + (specialCharacterMap.get(s) || s) : s; } function* flush() { while (backBuffer.count) { const n = Math.min(9, backBuffer.count); yield n > 1 ? backBuffer.last + n : backBuffer.last; backBuffer.last = BACK; backBuffer.count -= n; } } function* emit(s) { switch (s) { case EOW: yield* flush(); backBuffer.last = EOW; backBuffer.count = 0; break; case BACK: backBuffer.count++; break; default: yield* flush(); yield s; } } function* walk(node, depth) { const r = cache.get(node); if (r !== undefined) { yield* emit(ref(r)); return; } if (node.c) { cache.set(node, count++); const c = [...node.c].sort((a, b) => (a[0] < b[0] ? -1 : 1)); for (const [s, n] of c) { yield* emit(escape(s)); yield* walk(n, depth + 1); yield* emit(BACK); if (depth === 0) yield* emit(EOL); } } // Output EOW after children so it can be optimized on read if (node.f) { yield* emit(EOW); } } function* serialize(node) { yield* walk(node, 0); yield* flush(); } return generateHeader(radix, comment).concat(bufferLines_1.bufferLines(bufferLines_1.bufferLines(serialize(root), 120, '\n'), 10, '')); } 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 = /^TrieXv3\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().replace(/\r|\n/g, ''); if (!line || comment.test(line)) { continue; } if (line === exports.DATA) { break; } headerRows.push(line); } parseHeaderRows(headerRows); } readHeader(iter); const root = {}; const n = gensequence_1.genSequence(iter) .concatMap((a) => a.split('')) .reduce(parseStream(radix), { nodes: [root], root, stack: [{ node: root, s: '' }], parser: undefined }); return n.root; } exports.importTrie = importTrie; function parseStream(radix) { const eow = Object.freeze({ f: 1 }); function parseReference(acc, _) { let ref = ''; function parser(acc, s) { if (s === EOR) { const { root, nodes, stack } = acc; const r = parseInt(ref, radix); const top = stack[stack.length - 1]; const p = stack[stack.length - 2].node; p.c.set(top.s, nodes[r]); return { root, nodes, stack, parser: undefined }; } ref = ref + s; return acc; } const { nodes } = acc; nodes.pop(); return { ...acc, nodes, parser }; } function parseEscapeCharacter(acc, _) { let prev = ''; const parser = function (acc, s) { if (prev) { s = characterMap.get(prev + s) || s; return parseCharacter({ ...acc, parser: undefined }, s); } if (s === ESCAPE) { prev = s; return acc; } return parseCharacter({ ...acc, parser: undefined }, s); }; return { ...acc, parser }; } function parseCharacter(acc, s) { var _a; const parser = undefined; const { root, nodes, stack } = acc; const top = stack[stack.length - 1]; const node = top.node; node.c = (_a = node.c) !== null && _a !== void 0 ? _a : new Map(); const n = { f: undefined, c: undefined, n: nodes.length }; node.c.set(s, n); stack.push({ node: n, s }); nodes.push(n); return { root, nodes, stack, parser }; } function parseEOW(acc, _) { const parser = parseBack; const { root, nodes, stack } = acc; const top = stack[stack.length - 1]; const node = top.node; node.f = TrieNode_1.FLAG_WORD; if (!node.c) { top.node = eow; const p = stack[stack.length - 2].node; p.c.set(top.s, eow); nodes.pop(); } stack.pop(); return { root, nodes, stack, parser }; } const charactersBack = new Set((BACK + '23456789').split('')); function parseBack(acc, s) { if (!charactersBack.has(s)) { return parserMain({ ...acc, parser: undefined }, s); } let n = s === BACK ? 1 : parseInt(s, 10) - 1; const { stack } = acc; while (n-- > 0) { stack.pop(); } return { ...acc, parser: parseBack }; } function parseIgnore(acc, _) { return acc; } const parsers = new Map([ [EOW, parseEOW], [BACK, parseBack], [REF, parseReference], [ESCAPE, parseEscapeCharacter], [EOL, parseIgnore], [LF, parseIgnore], ]); function parserMain(acc, s) { var _a, _b; const parser = (_b = (_a = acc.parser) !== null && _a !== void 0 ? _a : parsers.get(s)) !== null && _b !== void 0 ? _b : parseCharacter; return parser(acc, s); } return parserMain; } //# sourceMappingURL=importExportV3.js.map