diff --git a/src/dictionary-parser.mjs b/src/dictionary-parser.mjs index b263a66a9f50a00b2b61dab811e570ef0f15108e..71f208be7e3067bf7380944dce6eaf3a31d02cd8 100644 --- a/src/dictionary-parser.mjs +++ b/src/dictionary-parser.mjs @@ -1,25 +1,59 @@ +import * as tree from './dictionary-tree.mjs'; + export function parse(dictionaryString) { const allLines = dictionaryString.split('\n'); const parsedLines = parseAllLines(allLines); - return combineUnspecifiedLines2IdSecPwdLines(parsedLines); + return combineUnspecified2IdSecPwd(parsedLines); } -function parseAllLines(lines){ + +function parseAllLines(lines) { return lines; } -function parseLine(theLine,allLines){ + +function parseLine(theLine, allLines) { return theLine; } -function combineUnspecifiedLines2IdSecPwdLines(lines){ - const unspecifiedLines = []; + +function combineUnspecified2IdSecPwd(lines) { const idSecPwdLines = []; - lines.forEach(line=>line.includes('@@@@') ? idSecPwdLines.push(line) : unspecifiedLines.push(line)); - if(unspecifiedLines.length) idSecPwdLines.push(`${mergeLines(unspecifiedLines)}@@@@${mergeLines(unspecifiedLines)}`) + const unspecifiedLines = []; + lines.forEach(line => line.includes('@@@@') ? deepUnspecifiedExtractor(line, unspecifiedLines, idSecPwdLines) : unspecifiedLines.push(line)); + if (unspecifiedLines.length) idSecPwdLines.push(`${mergeLines(unspecifiedLines)}@@@@${mergeLines(unspecifiedLines)}`) return mergeLines(idSecPwdLines); } -function mergeLines(lines){ - console.log('lines:',lines); - if(!lines.length) return ''; - lines = lines.filter(l=>l.length); - if(lines.length===1) return lines[0]; + +function deepUnspecifiedExtractor(line, unspecifiedLines, idSecPwdLines) { + const hybridTree = tree.buildTreeStruct(line); + + /* + buildTree (à créer dans un module à part) + parcours en profondeur jusqu'à trouver des branches unspecified. + extraction de celles-ci pour les ajouter (avec leur chaine de parents) aux unspecifiedlines + ajout de l'arbre restant aux idSecPwdLines + */ + idSecPwdLines.push(line); +} + +function mergeLines(lines) { + //console.log('lines:',lines); + if (!lines.length) return ''; + lines = lines.filter(l => l.length); + if (lines.length === 1) return lines[0]; return `(${lines.join('|')})`; } + +/* +const json1 = '(a(b@@@@c|d)|(e|f)|g|h@@@@i)' +const json2 = { + 'a':'a'{ + 'b@@@@c':'b@@@@c', + 'd':'d' + }, + { + 'e':'e', + 'f':'f' + }, + 'g':'g', + 'h@@@@i':'h@@@@i' +} +*/ diff --git a/src/dictionary-parser.test.mjs b/src/dictionary-parser.test.mjs index 1bf239ef0605f2bcaf7f626edd13c336c484f81b..fdd9aaee09f526f0924d380685199b2abed6ffa7 100644 --- a/src/dictionary-parser.test.mjs +++ b/src/dictionary-parser.test.mjs @@ -16,5 +16,5 @@ h@@@@(i|j) `), '(a@@@@(b|c)|h@@@@(i|j)|((d|e)|(f|g))@@@@((d|e)|(f|g)))'); }); test.skip('no @@@@ cases combined with @@@@ case on same line recombine to allways finish with @@@@ case', t => { - t.is(app.parse('(a|b|c@@@@d)'), '(c@@@@d|(a|b)@@@@(a|b))'); + t.is(app.parse('(a(b@@@@c|d)|(e|f)|g|h@@@@i)'), '(g@@@@h|(a|b|c)@@@@(a|b|c))'); }); diff --git a/src/dictionary-tree.mjs b/src/dictionary-tree.mjs index 9e1667e6a157480a0f4276d60ccc45611a734915..c07d603057fd3d526f2466e013ca30ffca0d2286 100644 --- a/src/dictionary-tree.mjs +++ b/src/dictionary-tree.mjs @@ -2,10 +2,10 @@ export function build(dictionaryString) { } export function buildTreeStruct(monoLineString) { - console.log('srcTree', monoLineString); + //console.log('srcTree', monoLineString); const stringAsArray = monoLineString.split(''); const rawTree = leftParser(stringAsArray); - console.log('rawTree', JSON.stringify(rawTree)); + //console.log('rawTree', JSON.stringify(rawTree)); const outOfScope = stringAsArray.length; if (outOfScope) throw `fail to build tree from : "${monoLineString}" parsed: ${JSON.stringify(rawTree)} unparsed/failed: ${stringAsArray.join('')}`; //return rawTree;//trivialDedup(flattenTree(rawTree)) @@ -15,70 +15,82 @@ export function buildTreeStruct(monoLineString) { /** * leftParser stuff */ -function flushNoEmptyString(data){ - if (data.str.length) data.tree[data.tree.length-1].step.push(data.str); +function flushNoEmptyString(data) { + if (data.str.length) data.tree[data.tree.length - 1].step.push(data.str); data.str = ''; } -function appendToString(chr,data){ + +function appendToString(chr, data) { data.str += chr; } -function nextAlt(data){ + +function nextAlt(data) { data.tree.push({step: []}); } -function goIntoNewAlt(data){ - data.tree[data.tree.length-1].step.push(leftParser(data.input)); + +function goIntoNewAlt(data) { + data.tree[data.tree.length - 1].step.push(leftParser(data.input)); +} + +function returnTrue() { + return true; +} + +function doTheses() { } -function returnTrue(){return true;} -function doTheses(){} + const leftParserFunc = { - ')':(chr,data)=>returnTrue(flushNoEmptyString(data)), - '|':(chr,data)=>doTheses(flushNoEmptyString(data), nextAlt(data)), - '(':(chr,data)=>doTheses(flushNoEmptyString(data), goIntoNewAlt(data)), - 'default':(chr,data)=>appendToString(chr,data) + ')': (chr, data) => returnTrue(flushNoEmptyString(data)), + '|': (chr, data) => doTheses(flushNoEmptyString(data), nextAlt(data)), + '(': (chr, data) => doTheses(flushNoEmptyString(data), goIntoNewAlt(data)), + 'default': (chr, data) => appendToString(chr, data) } + function leftParser(inputChrArray) { const data = { - input:inputChrArray, - str:'', - tree:[{step: []}], + input: inputChrArray, + str: '', + tree: [{step: []}], }; while (data.input.length) { const chr = data.input.shift(); - if(typeof leftParserFunc[chr]!=='undefined') { - const out = leftParserFunc[chr](chr,data); - if(out) return data.tree; - } else leftParserFunc['default'](chr,data); + if (typeof leftParserFunc[chr] !== 'undefined') { + const out = leftParserFunc[chr](chr, data); + if (out) return data.tree; + } else leftParserFunc['default'](chr, data); } flushNoEmptyString(data); return data.tree; } + // end leftParser -function concatStrings(array){ +function concatStrings(array) { const data = { - input:array, - str:'', - tree:[{step: []}], + input: array, + str: '', + tree: [{step: []}], }; - array.forEach(e=>{ - if(typeof e === 'string') appendToString(e,data); - else{ + array.forEach(e => { + if (typeof e === 'string') appendToString(e, data); + else { flushNoEmptyString(data) - data.tree[data.tree.length-1].step.push(e); + data.tree[data.tree.length - 1].step.push(e); } }); flushNoEmptyString(data) - return data.tree[data.tree.length-1].step; + return data.tree[data.tree.length - 1].step; } + function flattenTree(tree) { if (typeof tree === 'string') return tree; - if (Array.isArray(tree) && tree.length===0) return ''; - if (Array.isArray(tree) && tree.length===1) return flattenTree(tree[0]); + if (Array.isArray(tree) && tree.length === 0) return ''; + if (Array.isArray(tree) && tree.length === 1) return flattenTree(tree[0]); if (isStep(tree)) { tree.step = tree.step.map(flattenTree); if (!Array.isArray(tree.step)) return flattenTree(tree.step); if (tree.step.length === 1) return flattenTree(tree.step); if (tree.step.length === 0) return ''; - if(!tree.step.reduce((acc,cur)=>Array.isArray(cur)||acc,false)) return concatStrings(tree.step); + if (!tree.step.reduce((acc, cur) => Array.isArray(cur) || acc, false)) return concatStrings(tree.step); return tree; } @@ -93,7 +105,7 @@ function flattenTree(tree) { //console.log('tree:', JSON.stringify(tree)); ///if(isStep(tree[0])) tree[0].step = flattenTree(tree[0].step); tree = trivialDedup(tree.map(flattenTree)); - tree = [].concat(...tree.map(e=>Array.isArray(e) ? e : [e])); + tree = [].concat(...tree.map(e => Array.isArray(e) ? e : [e])); return tree; } @@ -101,13 +113,21 @@ function flattenTree(tree) { function isStep(element) { return typeof element === 'object' && typeof element.step !== 'undefined'; } + function trivialDedup(tree) { if (typeof tree === 'string') return tree; - if (isStep(tree)) return {step:tree.step.map(subTree=>trivialDedup(subTree))}; - if (Array.isArray(tree)){ + if (isStep(tree)) return {step: tree.step.map(subTree => trivialDedup(subTree))}; + if (Array.isArray(tree)) { const dedupKeys = {}; tree.forEach(v => dedupKeys[JSON.stringify(v)] = v); // eslint-disable-line no-return-assign - return Object.keys(dedupKeys).map(json=>trivialDedup(JSON.parse(json))); + return Object.keys(dedupKeys).map(json => trivialDedup(JSON.parse(json))); } throw `unknown case : ${tree}`; } + +export function serialize(treeStruct) { + if (typeof treeStruct === 'string') return treeStruct; + if (Array.isArray(treeStruct)) return `(${treeStruct.map(serialize).join('|')})`; + if (isStep(treeStruct)) return treeStruct.step.map(serialize).join(''); + throw new Error(`Error: how to serialize ${JSON.stringify(treeStruct)} RAW: ${treeStruct}`); +} diff --git a/src/dictionary-tree.test.mjs b/src/dictionary-tree.test.mjs index 73cf5c5e1e889f8f56b65602ceca40d65305a0f4..620877b19351df2175733cd2a6af79b9a51c47c6 100644 --- a/src/dictionary-tree.test.mjs +++ b/src/dictionary-tree.test.mjs @@ -2,27 +2,19 @@ import test from 'ava'; import * as app from './dictionary-tree.mjs'; const buildTreeStruct = str => JSON.stringify(app.buildTreeStruct(str)); -const expected= str => JSON.stringify(str); +const expected = str => JSON.stringify(str); -test('simple string still simple string',t=>t.is(buildTreeStruct('abc'),expected('abc'))); -test('(a|b) alt as array',t=>t.is(buildTreeStruct('(a|b)'),expected(['a','b']))); +test('simple string still simple string', t => t.is(buildTreeStruct('abc'), expected('abc'))); +test('(a|b) alt as array', t => t.is(buildTreeStruct('(a|b)'), expected(['a', 'b']))); //test('a) throw',t=>t.throws(()=>buildTreeStruct('a)'))); //test('(a throw',t=>t.throws(()=>buildTreeStruct('(a'))); //test('a|b throw',t=>t.throws(()=>buildTreeStruct('a|b'))); -test('(|b) empty choice is choice', t=>t.is(buildTreeStruct('(|b)'),expected(['','b']))); -test('(b|b) trivial dedup', t=>t.is(buildTreeStruct('(|b||b|)'),expected(['','b']))); -test('a(b|c) ordered part in step obj',t=>t.is(buildTreeStruct('a(b|c)'),expected({step:['a',['b','c']]}))); -test('a(b) flat merge when no alt',t=>t.is(buildTreeStruct('a(b)'),expected('ab'))); - -test('build tree with (|) pattern', t => t.is(buildTreeStruct('(a(|b@@@@c|d|)|(e|f)|g|h@@@@i)'), - expected([ - {step:['a',[ - '', - 'b@@@@c', - 'd' - ]]}, - 'e', - 'f', - 'g', - 'h@@@@i' - ]))); +test('(|b) empty choice is choice', t => t.is(buildTreeStruct('(|b)'), expected(['', 'b']))); +test('(b|b) trivial dedup', t => t.is(buildTreeStruct('(|b||b|)'), expected(['', 'b']))); +test('a(b|c) ordered part in step obj', t => t.is(buildTreeStruct('a(b|c)'), expected({step: ['a', ['b', 'c']]}))); +test('a(b) flat merge when no alt', t => t.is(buildTreeStruct('a(b)'), expected('ab'))); +test('build complexe tree with (|) pattern', t => t.is(buildTreeStruct('(a(|b@@@@c|d|)|(e|f)|g|h@@@@i)'), + expected([{step: ['a', ['', 'b@@@@c', 'd']]}, 'e', 'f', 'g', 'h@@@@i']) +)); +test('serialize tree to a(b|c)', t => t.is(app.serialize({step: ['a', ['b', 'c']]}), 'a(b|c)')); +test('serialize incorrect tree throw', t => t.throws(() => app.serialize({plop: ['a']})));