eval("let Formula = __webpack_require__(/*! ./Formula.js */ \"./src/search-mod/Formula.js\");\n\n/**\n * This class is used to translate the GUI query language into a corresponding\n * Optimade query.\n */\nclass OptimadeTranslator {\n\n constructor() {\n }\n\n /**\n * Translates the given GUI query into the corresponding optimade query\n * string.\n */\n translate(queryIn, typesIn, allowOtherElements) {\n // Make a copy so that this function becomes pure.\n let query = [...queryIn];\n let types = [...typesIn];\n\n // Add escaped quotes to all elements\n for (let i=0; i < query.length; ++i) {\n let q = query[i];\n let t = types[i];\n if (t === \"E\") {\n query[i] = `\\\"${q}\\\"`\n }\n }\n\n // Add escaped quotes to formulas\n for (let i=0; i < query.length; ++i) {\n let q = query[i];\n let t = types[i];\n if (t === \"F\") {\n const formula = new Formula(q)\n const fragments = []\n formula.formulaMap.forEach( (number, element)=> {\n const fragment = '\"' + element + (number === 1 ? '' : +number) + '\"';\n fragments.push(fragment)\n })\n query[i] = fragments.join(', ')\n }\n }\n\n // Construct the reduced expression\n [query, types] = this.simplify(query, types);\n\n // Convert into optimade syntax\n for (let i=0; i < query.length; ++i) {\n let q = query[i];\n let t = types[i];\n if (t === \"E\") {\n if (allowOtherElements) {\n q = \"elements HAS ALL \" + q\n } else {\n q = \"elements HAS ONLY \" + q\n }\n } else if (t === \"F\") {\n if (allowOtherElements) {\n q = \"formula HAS ALL \" + q\n } else {\n q = \"formula HAS ONLY \" + q\n }\n }\n query[i] = q;\n }\n\n let res = query.join(\"\")\n console.log(\"FINAL OPTIMADE QUERY:\")\n console.log(res)\n return res\n }\n\n /**\n * For combining elements/formulas connected by AND into a single list.\n */\n combineANDIn(query, types) {\n const newQuery = []\n const newTypes = []\n for (let i=0; i < query.length;) {\n const q0 = query[i-1];\n const t0 = types[i-1];\n const q1 = query[i];\n const t1 = types[i];\n const q2 = query[i+1];\n const t2 = types[i+1];\n const q3 = query[i+2];\n const t3 = types[i+2];\n\n if (((t1 === \"E\" && t3 === \"E\") || (t1 === \"F\" && t3 === \"F\")) && q2 === \"AND\" && q0 !== \"NOT\") {\n let active = true\n let type = t3\n let joined = [q1, q3]\n i = i + 3\n while(active) {\n const q4 = query[i];\n const t4 = types[i];\n const q5 = query[i+1];\n const t5 = types[i+1];\n if (q4 === \"AND\" && t5 === type) {\n joined.push(q5)\n i += 2\n } else {\n active = false\n newQuery.push(joined.join(\", \"))\n newTypes.push(type)\n }\n }\n } else {\n newQuery.push(q1)\n newTypes.push(t1)\n i += 1\n }\n }\n return [newQuery, newTypes]\n }\n\n /**\n * For finding parenthesis pairs\n */\n findPairs(query, types) {\n const pairs = new Map();\n\n // Find list of indices where parenthesis should be removed\n let removed = []\n for (let i=0; i < query.length; ++i) {\n const q1 = query[i];\n const t1 = types[i];\n if (q1 === \"(\") {\n let index = 0;\n for (let j=i+1; j < query.length; ++j) {\n const q2 = query[j];\n const t2 = types[j];\n if (q2 === \"(\") {\n index += 1;\n }\n if (q2 === \")\") {\n if (index != 0) {\n index -= 1;\n } else {\n pairs.set(i, j)\n pairs.set(j, i)\n break\n }\n }\n }\n }\n }\n return pairs;\n }\n\n /**\n * For removing unnecessary parenthesis\n */\n removeParenthesis(query, types) {\n // Find list of indices where parenthesis should be removed\n const pairs = this.findPairs(query, types)\n let removed = []\n for (let i=0; i < query.length; ++i) {\n const q1 = query[i];\n const t1 = types[i];\n if (q1 === \"(\") {\n let p = pairs.get(i)\n\n // Remove outer parentheses\n if (i === 0 && p == query.length-1) {\n removed.push(p)\n removed.push(i)\n // Remove parentheses that do not contain multiple operations\n } else {\n const contents = query.slice(i+1, p)\n const tp = types.slice(i+1, p)\n let noop = true\n for (let t of tp) {\n if (t === \"S\") {\n noop = false\n break\n }\n }\n if (noop) {\n removed.push(p)\n removed.push(i)\n }\n }\n }\n }\n\n // Find repeated quotes and reduce them to single one\n for (let i=0; i < query.length; ++i) {\n const q1 = query[i];\n const t1 = types[i];\n const q2 = query[i+1];\n const t2 = types[i+1];\n if (q1 === \"(\" && q1 === \"(\") {\n let p1 = pairs.get(i)\n let p2 = pairs.get(i+1)\n if (p2 == p1 - 1) {\n removed.push(i)\n removed.push(p1)\n }\n }\n }\n\n // Remove found parentheses\n removed = removed.sort((a, b) => b - a);\n for (let j=0; j < removed.length; ++j) {\n query.splice(removed[j], 1);\n types.splice(removed[j], 1);\n }\n return [query, types]\n }\n\n /**\n * Combines AND statements where either one or both of the statements has a\n * parenthesis. These statements are refactored using distributive laws of\n * set theory:\n * A AND (B OR C) == (A AND B) OR (A AND C)\n * in order to see if there are lists of elements that should be joined.\n */\n combineANDOut(query, types) {\n let newQuery = query\n let newTypes = types\n const pairs = this.findPairs(query, types)\n\n let combined = false\n for (let i=0; i < query.length; ++i) {\n\n // Look for AND surrounded by one or more parenthesis\n const q1 = query[i];\n const t1 = types[i];\n const q2 = query[i+1];\n const t2 = types[i+1];\n const q3 = query[i+2];\n const t3 = types[i+2];\n if (q2 === \"AND\" && (q1 === \")\" || q3 === \"(\" )) {\n combined = true\n\n // Get left hand and right hand side expressions\n let lhsQ\n let lhsT\n let rhsQ\n let rhsT\n let rhsEnd\n let lhsStart\n if (q1 === \")\") {\n lhsStart = pairs.get(i)\n lhsQ = query.slice(lhsStart, i+1)\n lhsT = types.slice(lhsStart, i+1)\n } else {\n if (query[i-1] === \"NOT\") {\n lhsStart = i-1\n } else {\n lhsStart = i\n }\n lhsQ = query.slice(lhsStart, i+1)\n lhsT = types.slice(lhsStart, i+1)\n lhsQ.unshift(\"(\")\n lhsT.unshift(\"P\")\n lhsQ.push(\")\")\n lhsT.push(\"P\")\n }\n if (q3 === \"(\") {\n rhsEnd = pairs.get(i+2)+1\n rhsQ = query.slice(i+2, rhsEnd)\n rhsT = types.slice(i+2, rhsEnd)\n } else {\n if (query[i+2] === \"NOT\") {\n rhsEnd = i+4\n } else {\n rhsEnd = i+3\n }\n rhsQ = query.slice(i+2, rhsEnd)\n rhsT = types.slice(i+2, rhsEnd)\n rhsQ.unshift(\"(\")\n rhsT.unshift(\"P\")\n rhsQ.push(\")\")\n rhsT.push(\"P\")\n }\n\n // Split the rhs into parts separated by OR\n const rhsParts = []\n let prevPos = 1\n for (let j=1; j < rhsQ.length - 1; ++j) {\n const sq1 = rhsQ[j];\n if (sq1 === \"OR\") {\n rhsParts.push([rhsQ.slice(prevPos, j), rhsT.slice(prevPos, j)])\n prevPos = j+1\n } else if (j === rhsQ.length - 2) {\n rhsParts.push([rhsQ.slice(prevPos, j+1), rhsT.slice(prevPos, j+1)])\n }\n }\n\n // Combine lhs with the different parts of rhs\n let comboQ = []\n let comboT = []\n for (let j=0; j < rhsParts.length; ++j) {\n let icomboQ = []\n let icomboT = []\n icomboQ = icomboQ.concat(rhsParts[j][0])\n icomboT = icomboT.concat(rhsParts[j][1])\n icomboQ.push(\"AND\")\n icomboT.push(\"S\")\n icomboQ = icomboQ.concat(lhsQ)\n icomboT = icomboT.concat(lhsT)\n if (j === 0) {\n if (rhsParts.length > 1) {\n comboQ.push(\"(\")\n comboT.push(\"P\")\n }\n comboQ = comboQ.concat(icomboQ)\n comboT = comboT.concat(icomboT)\n if (rhsParts.length > 1) {\n comboQ.push(\")\")\n comboT.push(\"P\")\n }\n } else {\n comboQ.push(\"OR\")\n comboT.push(\"S\")\n comboQ.push(\"(\")\n comboT.push(\"P\")\n comboQ = comboQ.concat(icomboQ)\n comboT = comboT.concat(icomboT)\n comboQ.push(\")\")\n comboT.push(\"P\")\n }\n }\n comboQ.unshift(\"(\")\n comboT.unshift(\"P\")\n comboQ.push(\")\")\n comboT.push(\"P\")\n newQuery = query.slice(0, lhsStart).concat(comboQ).concat(query.slice(rhsEnd, query.length))\n newTypes = types.slice(0, lhsStart).concat(comboT).concat(types.slice(rhsEnd, query.length))\n break\n }\n }\n return [newQuery, newTypes, combined]\n }\n\n /**\n * Recursive function for simplifying the query into a form that can be\n * correctly interpreted by Optimade.\n */\n simplify(query, types) {\n\n // Remove unnecessary parenthesis, combine all elements connected with\n // AND, and remove unnecessary parenthesis again.\n [query, types] = this.removeParenthesis(query, types);\n [query, types] = this.combineANDIn(query, types);\n [query, types] = this.removeParenthesis(query, types);\n\n // Combine parenthesis statements\n let combined\n [query, types, combined] = this.combineANDOut(query, types);\n\n // If a combination was done, run recursively until no combination is\n // done\n if (combined) {\n [query, types] = this.simplify(query, types)\n // After the last step perform on final cleanup\n } else {\n [query, types] = this.removeParenthesis(query, types);\n [query, types] = this.combineANDIn(query, types);\n [query, types] = this.removeParenthesis(query, types);\n }\n return [query, types]\n }\n}\n\n// EXPORTS\nmodule.exports = OptimadeTranslator;\n\n\n//# sourceURL=webpack:///./src/search-mod/OptimadeTranslator.js?");
eval("let Formula = __webpack_require__(/*! ./Formula.js */ \"./src/search-mod/Formula.js\");\n\n/**\n * This class is used to translate the GUI query language into a corresponding\n * Optimade query.\n */\nclass OptimadeTranslator {\n\n constructor() {\n }\n\n /**\n * Translates the given GUI query into the corresponding optimade query\n * string.\n */\n translate(queryIn, typesIn, allowOtherElements) {\n // Make a copy so that this function becomes pure.\n let query = [...queryIn];\n let types = [...typesIn];\n\n // Add escaped quotes to all elements\n for (let i=0; i < query.length; ++i) {\n let q = query[i];\n let t = types[i];\n if (t === \"E\") {\n query[i] = `\\\"${q}\\\"`\n }\n }\n\n // Add escaped quotes to formulas\n for (let i=0; i < query.length; ++i) {\n let q = query[i];\n let t = types[i];\n if (t === \"F\") {\n const formula = new Formula(q)\n const fragments = []\n formula.formulaMap.forEach( (number, element)=> {\n const fragment = '\"' + element + (number === 1 ? '' : +number) + '\"';\n fragments.push(fragment)\n })\n query[i] = fragments.join(', ')\n }\n }\n\n // Construct the reduced expression\n [query, types] = this.simplify(query, types);\n\n // Convert into optimade syntax\n for (let i=0; i < query.length; ++i) {\n let q = query[i];\n let t = types[i];\n if (t === \"E\") {\n if (allowOtherElements) {\n q = \"elements HAS ALL \" + q\n } else {\n q = \"elements HAS ONLY \" + q\n }\n } else if (t === \"F\") {\n if (allowOtherElements) {\n q = \"formula HAS ALL \" + q\n } else {\n q = \"formula HAS ONLY \" + q\n }\n }\n query[i] = q;\n }\n\n// Surround with parenthesis so that this part will not get incorrectly\n // mixed with the filters due to operator precedence \nlet res = \"(\" + query.join(\"\") + \")\"\n console.log(\"FINAL OPTIMADE QUERY:\")\n console.log(res)\n return res\n }\n\n /**\n * For combining elements/formulas connected by AND into a single list.\n */\n combineANDIn(query, types) {\n const newQuery = []\n const newTypes = []\n for (let i=0; i < query.length;) {\n const q0 = query[i-1];\n const t0 = types[i-1];\n const q1 = query[i];\n const t1 = types[i];\n const q2 = query[i+1];\n const t2 = types[i+1];\n const q3 = query[i+2];\n const t3 = types[i+2];\n\n if (((t1 === \"E\" && t3 === \"E\") || (t1 === \"F\" && t3 === \"F\")) && q2 === \"AND\" && q0 !== \"NOT\") {\n let active = true\n let type = t3\n let joined = [q1, q3]\n i = i + 3\n while(active) {\n const q4 = query[i];\n const t4 = types[i];\n const q5 = query[i+1];\n const t5 = types[i+1];\n if (q4 === \"AND\" && t5 === type) {\n joined.push(q5)\n i += 2\n } else {\n active = false\n newQuery.push(joined.join(\", \"))\n newTypes.push(type)\n }\n }\n } else {\n newQuery.push(q1)\n newTypes.push(t1)\n i += 1\n }\n }\n return [newQuery, newTypes]\n }\n\n /**\n * For finding parenthesis pairs\n */\n findPairs(query, types) {\n const pairs = new Map();\n\n // Find list of indices where parenthesis should be removed\n let removed = []\n for (let i=0; i < query.length; ++i) {\n const q1 = query[i];\n const t1 = types[i];\n if (q1 === \"(\") {\n let index = 0;\n for (let j=i+1; j < query.length; ++j) {\n const q2 = query[j];\n const t2 = types[j];\n if (q2 === \"(\") {\n index += 1;\n }\n if (q2 === \")\") {\n if (index != 0) {\n index -= 1;\n } else {\n pairs.set(i, j)\n pairs.set(j, i)\n break\n }\n }\n }\n }\n }\n return pairs;\n }\n\n /**\n * For removing unnecessary parenthesis\n */\n removeParenthesis(query, types) {\n // Find list of indices where parenthesis should be removed\n const pairs = this.findPairs(query, types)\n let removed = []\n for (let i=0; i < query.length; ++i) {\n const q1 = query[i];\n const t1 = types[i];\n if (q1 === \"(\") {\n let p = pairs.get(i)\n\n // Remove outer parentheses\n if (i === 0 && p == query.length-1) {\n removed.push(p)\n removed.push(i)\n // Remove parentheses that do not contain multiple operations\n } else {\n const contents = query.slice(i+1, p)\n const tp = types.slice(i+1, p)\n let noop = true\n for (let t of tp) {\n if (t === \"S\") {\n noop = false\n break\n }\n }\n if (noop) {\n removed.push(p)\n removed.push(i)\n }\n }\n }\n }\n\n // Find repeated quotes and reduce them to single one\n for (let i=0; i < query.length; ++i) {\n const q1 = query[i];\n const t1 = types[i];\n const q2 = query[i+1];\n const t2 = types[i+1];\n if (q1 === \"(\" && q1 === \"(\") {\n let p1 = pairs.get(i)\n let p2 = pairs.get(i+1)\n if (p2 == p1 - 1) {\n removed.push(i)\n removed.push(p1)\n }\n }\n }\n\n // Remove found parentheses\n removed = removed.sort((a, b) => b - a);\n for (let j=0; j < removed.length; ++j) {\n query.splice(removed[j], 1);\n types.splice(removed[j], 1);\n }\n return [query, types]\n }\n\n /**\n * Combines AND statements where either one or both of the statements has a\n * parenthesis. These statements are refactored using distributive laws of\n * set theory:\n * A AND (B OR C) == (A AND B) OR (A AND C)\n * in order to see if there are lists of elements that should be joined.\n */\n combineANDOut(query, types) {\n let newQuery = query\n let newTypes = types\n const pairs = this.findPairs(query, types)\n\n let combined = false\n for (let i=0; i < query.length; ++i) {\n\n // Look for AND surrounded by one or more parenthesis\n const q1 = query[i];\n const t1 = types[i];\n const q2 = query[i+1];\n const t2 = types[i+1];\n const q3 = query[i+2];\n const t3 = types[i+2];\n if (q2 === \"AND\" && (q1 === \")\" || q3 === \"(\" )) {\n combined = true\n\n // Get left hand and right hand side expressions\n let lhsQ\n let lhsT\n let rhsQ\n let rhsT\n let rhsEnd\n let lhsStart\n if (q1 === \")\") {\n lhsStart = pairs.get(i)\n lhsQ = query.slice(lhsStart, i+1)\n lhsT = types.slice(lhsStart, i+1)\n } else {\n if (query[i-1] === \"NOT\") {\n lhsStart = i-1\n } else {\n lhsStart = i\n }\n lhsQ = query.slice(lhsStart, i+1)\n lhsT = types.slice(lhsStart, i+1)\n lhsQ.unshift(\"(\")\n lhsT.unshift(\"P\")\n lhsQ.push(\")\")\n lhsT.push(\"P\")\n }\n if (q3 === \"(\") {\n rhsEnd = pairs.get(i+2)+1\n rhsQ = query.slice(i+2, rhsEnd)\n rhsT = types.slice(i+2, rhsEnd)\n } else {\n if (query[i+2] === \"NOT\") {\n rhsEnd = i+4\n } else {\n rhsEnd = i+3\n }\n rhsQ = query.slice(i+2, rhsEnd)\n rhsT = types.slice(i+2, rhsEnd)\n rhsQ.unshift(\"(\")\n rhsT.unshift(\"P\")\n rhsQ.push(\")\")\n rhsT.push(\"P\")\n }\n\n // Split the rhs into parts separated by OR\n const rhsParts = []\n let prevPos = 1\n for (let j=1; j < rhsQ.length - 1; ++j) {\n const sq1 = rhsQ[j];\n if (sq1 === \"OR\") {\n rhsParts.push([rhsQ.slice(prevPos, j), rhsT.slice(prevPos, j)])\n prevPos = j+1\n } else if (j === rhsQ.length - 2) {\n rhsParts.push([rhsQ.slice(prevPos, j+1), rhsT.slice(prevPos, j+1)])\n }\n }\n\n // Combine lhs with the different parts of rhs\n let comboQ = []\n let comboT = []\n for (let j=0; j < rhsParts.length; ++j) {\n let icomboQ = []\n let icomboT = []\n icomboQ = icomboQ.concat(rhsParts[j][0])\n icomboT = icomboT.concat(rhsParts[j][1])\n icomboQ.push(\"AND\")\n icomboT.push(\"S\")\n icomboQ = icomboQ.concat(lhsQ)\n icomboT = icomboT.concat(lhsT)\n if (j === 0) {\n if (rhsParts.length > 1) {\n comboQ.push(\"(\")\n comboT.push(\"P\")\n }\n comboQ = comboQ.concat(icomboQ)\n comboT = comboT.concat(icomboT)\n if (rhsParts.length > 1) {\n comboQ.push(\")\")\n comboT.push(\"P\")\n }\n } else {\n comboQ.push(\"OR\")\n comboT.push(\"S\")\n comboQ.push(\"(\")\n comboT.push(\"P\")\n comboQ = comboQ.concat(icomboQ)\n comboT = comboT.concat(icomboT)\n comboQ.push(\")\")\n comboT.push(\"P\")\n }\n }\n comboQ.unshift(\"(\")\n comboT.unshift(\"P\")\n comboQ.push(\")\")\n comboT.push(\"P\")\n newQuery = query.slice(0, lhsStart).concat(comboQ).concat(query.slice(rhsEnd, query.length))\n newTypes = types.slice(0, lhsStart).concat(comboT).concat(types.slice(rhsEnd, query.length))\n break\n }\n }\n return [newQuery, newTypes, combined]\n }\n\n /**\n * Recursive function for simplifying the query into a form that can be\n * correctly interpreted by Optimade.\n */\n simplify(query, types) {\n\n // Remove unnecessary parenthesis, combine all elements connected with\n // AND, and remove unnecessary parenthesis again.\n [query, types] = this.removeParenthesis(query, types);\n [query, types] = this.combineANDIn(query, types);\n [query, types] = this.removeParenthesis(query, types);\n\n // Combine parenthesis statements\n let combined\n [query, types, combined] = this.combineANDOut(query, types);\n\n // If a combination was done, run recursively until no combination is\n // done\n if (combined) {\n [query, types] = this.simplify(query, types)\n // After the last step perform on final cleanup\n } else {\n [query, types] = this.removeParenthesis(query, types);\n [query, types] = this.combineANDIn(query, types);\n [query, types] = this.removeParenthesis(query, types);\n }\n return [query, types]\n }\n}\n\n// EXPORTS\nmodule.exports = OptimadeTranslator;\n\n\n//# sourceURL=webpack:///./src/search-mod/OptimadeTranslator.js?");