UNPKG

@lahmatiy/jison

Version:

A parser generator with Bison's API

178 lines (142 loc) 5.23 kB
exports.packTable = function packTable(table) { function encodeNum(num) { return num < 32 ? e64[num] : e64[(num & 0b11111) | 0b100000] + e64[num >> 5]; } function packSubtable(table) { // reduce consequence repetitions table = table.replace(/(.+?)\1+/g, (m, p) => { const t = encodeNum(m.length / p.length); return t.length + 2 + p.length < m.length ? '<' + t + p + '>' : m; }); // reduce repetitions by back ref const rx = /([^<>]{6,})(.*?)\1/; let match = null; let round = 0; while (round < 16 && (match = table.match(rx))) { const idx = String.fromCharCode(32 + round); // 0x20-0x2f const pattern = match[1]; const parts = table.split(pattern); table = ''; for (let i = 0; i < parts.length; i++) { if (i === 1) { table += '[' + idx + pattern + ']'; } else if (i !== 0) { table += idx; } table += parts[i]; } round++; } return table; } const e64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~|='; const indexTable = []; const indexRowMap = new Map(); const stateTables = [ '', '', '', '' ]; for (let i = 0; i < table.length; i++) { const row = table[i]; const maxIndex = Math.max(...Object.keys(row).map(Number)); let encodedRow = ''; for (let j = 0; j <= maxIndex; j += 3) { let chunk = 0; for (let k = 0; k < 3; k++) { const index = j + k; if (index in row) { const [state, ref] = Array.isArray(row[index]) ? row[index] : [0, row[index]]; if (state !== 3) { chunk |= (state + 1) << (k * 2); stateTables[state] += encodeNum(ref); } else { stateTables[state] += encodeNum(i) + encodeNum(index); } } } encodedRow += e64[chunk]; } if (!indexRowMap.has(encodedRow)) { indexRowMap.set(encodedRow, indexRowMap.size); } indexTable.push(encodeNum(indexRowMap.get(encodedRow))); } return [ indexTable.join(''), [...indexRowMap.keys()].join(';'), ...stateTables ].map(packSubtable).join('='); }; exports.unpackTable = function unpackTable(tables) { function unpackSubtable(table) { // restore repetitions by ref const refs = table.match(/[\x20-\x2f]/g); const last = refs ? Math.max(...refs.map(ref => ref.charCodeAt() - 32)) : -1; for (let i = last; i >= 0; i--) { const idx = (32 + i).toString(16); let pattern; table = table .replace(new RegExp('\\[\\x' + idx + '(.+?)\\]'), (_, repl) => (pattern = repl)) .replace(new RegExp('\\x' + idx, 'g'), pattern); } // restore repetitions table = table.replace(/<([^>]+?)>/g, (_, p) => { let n = d64[p[0]]; let offset = 1; if (n >> 5 === 1) { n = (n & 0b11111) | (d64[p[1]] << 5); offset = 2; } return p.slice(offset).repeat(n); }); return table; } function decodeNums(encoded) { const nums = []; for (let j = 0; j < encoded.length; j++) { const b1 = d64[encoded[j]]; if (b1 & 0b100000) { const b2 = d64[encoded[++j]]; nums.push((b1 & 0b11111) | (b2 << 5)); } else { nums.push(b1); } } return nums; } const e64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~|='; const d64 = e64.split('').reduce((map, ch, idx) => (map[ch] = idx, map), Object.create(null)); const [indexTable, indexRows, ...stateTables] = tables.split('=').map(unpackSubtable); const decodedIndexRows = indexRows.split(';').map(row => { const states = []; for (let i = 0; i < row.length; i++) { for (let j = 0, chunk = d64[row[i]]; j < 3; j++) { states.push((chunk >> (j * 2)) & 0b11); } } return states; }); const decodedStateTables = stateTables.map(decodeNums); const result = decodeNums(indexTable).map(rowIdx => { const rowIndexes = decodedIndexRows[rowIdx]; const row = Object.create(null); for (let i = 0; i < rowIndexes.length; i++) { const state = rowIndexes[i]; if (state !== 0) { const ref = decodedStateTables[state - 1].shift(); row[i] = state === 1 ? ref : [state - 1, ref]; } } return row; }); for (let i = 0; i < decodedStateTables[3].length; i += 2) { result[decodedStateTables[3][i]][decodedStateTables[3][i + 1]] = [3]; } return result; };