Coincidencia de construcciones anidadas en JavaScript

En el pasado, mencioné el uso de expresiones regulares para hacer coincidir construcciones anidadas hasta una profundidad predeterminada, que es lo mejor que puede hacer a menos que esté usando uno de los tres motores de expresiones regulares (Perl, PCRE y .NET) que son actualmente es capaz de manejar la verdadera recursividad.

Bueno, recientemente quería poder admitir una profundidad de anidamiento ilimitada de una manera rápida, flexible y fácil de usar al hacer coincidir cadenas en JavaScript, así que aquí está el código que escribí para ello. La documentación básica y los ejemplos se incluyen en los comentarios del código.

// (c) 2007 Steven Levithan <stevenlevithan.com>
// MIT License

/*** matchRecursive
	accepts a string to search and a format (start and end tokens separated by "...").
	returns an array of matches, allowing nested instances of format.

	examples:
		matchRecursive("test",          "(...)")   -> []
		matchRecursive("(t(e)s)()t",    "(...)")   -> ["t(e)s", ""]
		matchRecursive("t<e>>st",       "<...>")   -> ["e"]
		matchRecursive("t<<e>st",       "<...>")   -> ["e"]
		matchRecursive("t<<e>>st",      "<...>")   -> ["<e>"]
		matchRecursive("<|t<e<|s|>t|>", "<|...|>") -> ["t<e<|s|>t"]
*/
var matchRecursive = function () {
	var	formatParts = /^([\S\s]+?)\.\.\.([\S\s]+)/,
		metaChar = /[-[\]{}()*+?.\\^$|,]/g,
		escape = function (str) {
			return str.replace(metaChar, "\\$&");
		};

	return function (str, format) {
		var p = formatParts.exec(format);
		if (!p) throw new Error("format must include start and end tokens separated by '...'");
		if (p[1] == p[2]) throw new Error("start and end format tokens cannot be identical");

		var	opener = p[1],
			closer = p[2],
			/* Use an optimized regex when opener and closer are one character each */
			iterator = new RegExp(format.length == 5 ? "["+escape(opener+closer)+"]" : escape(opener)+"|"+escape(closer), "g"),
			results = [],
			openTokens, matchStartIndex, match;

		do {
			openTokens = 0;
			while (match = iterator.exec(str)) {
				if (match[0] == opener) {
					if (!openTokens)
						matchStartIndex = iterator.lastIndex;
					openTokens++;
				} else if (openTokens) {
					openTokens--;
					if (!openTokens)
						results.push(str.slice(matchStartIndex, match.index));
				}
			}
		} while (openTokens && (iterator.lastIndex = matchStartIndex));

		return results;
	};
}();

Puedes descargar el código aquí.

Tenga en cuenta que el format el argumento espera una cadena simple; no es una expresión regular. Sin embargo, el código podría modificarse fácilmente para que funcione con expresiones regulares si eso es lo que busca.

Actualización: He publicado una versión alternativa que acepta patrones regex como formato como matchRecursiveRegExp.