Mare
Chefito qué código, parece que no es fácil la cosa, hay que mirarlo con tranquilidad. Claro que el que tiene que mirarlo con tranquilidad y aprender es avechuche, que en efecto parece que me ha dejado sólo ante el peligro
Chefito, creo que puedes simplificar algo la búsqueda de rangos [A-Za-z0-9] usando las funciones
StringIsAlpha y
StringIsDigit, aunque sean para cadenas por supuesto se pueden usar en un único carácter.
El programita de Chefito va fino, fino. Pero ayer hice avances con lo de expresiones regulares así que aquí va, es que tienta lo de hacer esto en una sólo línea ¿verdad?
Primero una puntualización: las llaves en el patrón de sustitución son opcionales, sólo hay que usarlas si estás mezclando números de grupo con números que quieres poner literalmente, así que en nuestro caso en vez de poner
"${1}(${2})${3}" se puede poner
"$1($2)$3", que queda más claro.
Ahora el avance. Resulta que en la ayuda de AutoIt para
StringRegExp pone un enlace a una descripción aún más detallada que lo que pone ahí. Si creía que era complejo, aún es más.
Pues en esa descripción, que por cierto está aquí:
http://www.autoitscript.com/autoit3/pcrepattern.html
En el apartado
"Lookahead assertions" podemos ver que se pueden hacer emparejamientos sin que una parte del mismo forme parte del grupo, por ejemplo
"\w+(?=;)" es una palabra seguida de un
";", pero el
";" no queda incluido en el emparejamiento, con lo que puede ser usado en el siguiente.
Si en vez de
"?=" usamos
"?!" es lo contrario, "que no esté", por ejemplo pone que
"foo(?!bar)" son las palabras
"foo" que NO están seguidas por
"bar".
Así que para que nuestra subexpresión
p^q NO esté seguida por paréntesis de cierre ponemos al final
"(?!\))"
Para el principio es parecido, está explicado en
"Lookbehind assertions". Se concluye que para que NO empiece por paréntesis abierto hay que poner
"(?<!\()" ¡menudo churro!
Estas "aserciones", tal como las llaman, no forman grupos, así que ahora tenemos sólo el grupo $1 que es nuestro p^q. Lo bueno es que esas aserciones se comprueban sin "consumir" los caracteres, con lo que se pueden reutilizar en la siguiente comprobación, así que las concatenaciones de operadores ya están solucionados.
Con estos cambios la expresión queda:
$res = StringRegExpReplace($cadena, "(?<!\()([[:alpha:]]\^[[:alpha:]])(?!\))", "($1)")
Ahora:
"p^q^r^s^t" queda
"(p^q)^(r^s)^t"
"p^(q^r)^s^t" queda
"p^(q^r)^(s^t)" ¡mantiene el paréntesis que hemos forzado y encuentra el siguiente!
"p^q^(r^s)^t" queda
"(p^q)^(r^s)^t", completando el que falta
"p^q^r^(s^t)" queda
"(p^q)^r^(s^t)", pues al emparejar el primero no puede tomar el segundo para respetar el último ya puesto.
¿Se me escapa algo?
Bueno, sí, aún hay una cosita... como miramos que por ejemplo la expresión no empiece por paréntesis, si empieza no la toma... aunque no acabe. Así por ejemplo
"(a^p^q^r^t^u)" la deja como
"(a^(p^q)^(r^t)^u)", en vez de la más correcta
"((a^p)^(q^r)^(t^u))" porque
a^p no la acepta pues empieza por paréntesis, aunque no acabe.
Por lo mismo
"(a^p^r)" la deja igual porque
"a^p" empieza por paréntesis y
p^r tiene al final el de cierre, con lo que no cuadra con nuestra expresión.
Quitando esto creo que los problemas anteriores están solucionados.
Por cierto, que el código "currado a mano" de
Chefito funciona perfectamente en esos casos.
Ahora estoy mirando una cosa que pone después en la ayuda de Aserciones múltiples y patrones condicionales, creo que podría mirarse a la vez que empiece y acabe por paréntesis, con lo que se solucionaría este último problema.