Created
April 20, 2021 02:11
-
-
Save rileyjshaw/355a3ea910e0d645e8f5cf78b18ff0eb to your computer and use it in GitHub Desktop.
Parse a string with [[enclosed [[nested]] double bracket pairs]]. I used this for https://github.com/rileyjshaw/font-comparison-tool.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Parse takes a string and returns a nested array. The array nests | |
* to a deeper level for each [[enclosed double bracket pair]]. | |
* | |
* eg: | |
* | |
* // Output: ['It goes ', ['deeper and ', ['deeper.']]] | |
* parse('It goes [[deeper and [[deeper.]]]]'); | |
*/ | |
function parse(str, startIdx = 0, isChild = false) { | |
const result = []; | |
for (let i = startIdx + 1, _len = str.length; i < _len; ++i) { | |
const char = str[i]; | |
let isOpen = false, | |
isClosed = false; | |
if (char === str[i - 1]) { | |
if (char === '[') isOpen = true; | |
else if (char === ']') isClosed = true; | |
} | |
if (isOpen || isClosed) { | |
// Ensure the possible substring between the prior token and here is captured. | |
if (i > startIdx + 1) result.push(str.slice(startIdx, i - 1)); | |
if (isOpen) { | |
const [contents, nextIdx] = parse(str, i + 1, true); | |
result.push(contents); | |
i = startIdx = nextIdx; | |
} else if (isClosed) { | |
return isChild ? [result, i + 1] : result; | |
} | |
} | |
} | |
if (str.length !== startIdx) result.push(str.slice(startIdx, str.length)); | |
return isChild ? [result, str.length] : result; | |
} | |
// Tests below here. | |
const testCases = [ | |
['A simple string', ['A simple string']], | |
['A simple string [ [ [][][] ] ] with single brackets', ['A simple string [ [ [][][] ] ] with single brackets']], | |
['[[An enclosed string]]', [['An enclosed string']]], | |
['[[Each]][[word]][[separately]][[enclosed]]', [['Each'], ['word'], ['separately'], ['enclosed']]], | |
['[[Some words are enclosed]] but some are not', [['Some words are enclosed'], ' but some are not']], | |
[ | |
'This [[[[is a test]] of some nested groups]] and the [[resulting]] data structure [[should [[be [[nested]] accordingly]]]]', | |
[ | |
'This ', | |
[['is a test'], ' of some nested groups'], | |
' and the ', | |
['resulting'], | |
' data structure ', | |
['should ', ['be ', ['nested'], ' accordingly']], | |
], | |
], | |
['This: ]] will close early…', ['This: ']], | |
['This: [[[[[[ will close at the end…', ['This: ', [[[' will close at the end…']]]]], | |
['This will have an empty array at the end: [[', ['This will have an empty array at the end: ', []]], | |
['', []], | |
]; | |
// Util. | |
function miniDeepArrayCompare(array1, array2) { | |
return ( | |
Array.isArray(array2) && | |
array1.length === array2.length && | |
array1.every((a, i) => { | |
const b = array2[i]; | |
return Array.isArray(a) ? miniDeepArrayCompare(a, b) : a === b; | |
}) | |
); | |
} | |
const results = testCases.map(([str, expectedResult]) => miniDeepArrayCompare(parse(str), expectedResult)); | |
const nPassed = results.filter(x => x).length; | |
if (nPassed === results.length) { | |
document.body.textContent = `💫 All ${results.length} tests passed! 💫`; | |
console.log(`💫 All ${results.length} tests passed! 💫`); | |
} else { | |
document.body.textContent = `${nPassed - results.length} of ${results.length} tests failed.`; | |
console.log( | |
`${nPassed} of ${results.length} tests passed:`, | |
results.map((r, i) => `${i}: ${r ? 'PASS' : 'FAIL'}`) | |
); | |
results.forEach((result, i) => { | |
if (!result) { | |
console.log(`\n--- Test case ${i + 1}: expected vs. actual result ---`); | |
const [str, expectedResult] = testCases[i]; | |
console.log(expectedResult, parse(str)); | |
} | |
}); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Brackets tester</title> | |
<style> | |
html { | |
font: 30px -apple-system, BlinkMacSystemFont, avenir next, avenir, helvetica neue, helvetica, Ubuntu, roboto, noto, segoe ui, arial, sans-serif; | |
place-content: center; | |
display: grid; | |
height: 100vh; | |
} | |
</style> | |
</head> | |
<body> | |
<script src="./brackets.js"></script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment