Skip to content

Instantly share code, notes, and snippets.

@rileyjshaw
Created April 20, 2021 02:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rileyjshaw/355a3ea910e0d645e8f5cf78b18ff0eb to your computer and use it in GitHub Desktop.
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.
/**
* 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));
}
});
}
<!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