In my domain-specific language, I am able to reference something inside a tree-like structure using a dotted path, except that I can also go "up" with two dots in a row:
"sibling" // gets sibling
".sibling" // same as above
".sibling.nested" // indexes into sibling with 'nested'
"..down" // goes up the hierarchy, then down the key 'down'
"down.." // doesn't do anything, really, goes down and then back up
".." // references parent
"down..down" // same as "down"
I need to split the above like this:
["sibling"]
["sibling"]
["sibling", "nested"]
[symbol, "down"]
["down", symbol]
[symbol]
["down", symbol, "down"]
In essence, replacing the .. with a symbol, but splitting by single . normally. I can split plain dotted nested paths like this (even respects escaping the period):
path.split(/(?<!\\)\./)
Keys can potentially contain any characters, so a word boundary will not work sometimes.
But I'm having a hard time finding a clean solution that can replace the ... My current solution is to replace all empty strings with the symbol (empty strings mean that there were two periods in a row), and append a character to the ends of the path, removing after the splitting is done:
const symbol = Symbol();
function split(path) {
path = "$" + path + "$"; // Any character can work; I just used '$' here
// Split by dots, replacing empty strings with the symbol
const split = path.split(/(?<!\\)\./).map((part) => (!part ? symbol : part));
// Remove prefixed character from start
split[0] = split[0].slice(1);
// Remove suffixed character from end
split[split.length - 1] = split[split.length - 1].slice(0, -1);
// Remove start or end if they are empty
if (!split[0]) split.shift();
if (!split[split.length - 1]) split.pop();
// Done
return split;
}
const tests = [
"sibling" , // gets sibling
".sibling" , // same as above
".sibling.nested", // indexes into sibling with 'nested'
"..down" , // goes up the hierarchy, then down the key 'down'
"down.." , // doesn't do anything, really, goes down and then back up
".." , // references parent
"down..down" , // same as "down"
];
// (Stack Overflow snippet console displays null instead of Symbol)
console.log(tests.map(split));
It gets the job done, passes all the test cases, but it's much too verbose and clumsy. I'm looking for a hopefully shorter and easier solution than this.