¿Quién no ha tenido alguna vez que encontrar en un programa el paréntesis cerrado asociado a un paréntesis abierto? Esta utilidad intenta dar solución a este problema.
Descripción:
Búsqueda del token asociado (que denominaremos "Pareado") al token seleccionado en la ventana activa.
Con el símil de la problemática, el token seleccionado sería el paréntesis abierto y el pareado el paréntesis cerrado.
Restricciones:
(R1) El token seleccionado sólo puede ser un carácter.
(R2) La correspondencia entre token seleccionado y su pareado está fija en el programa, restringiéndose a la siguiente:
'(' ')'
'[' ']'
(R3) El token seleccionado es siempre el carácter situado a la derecha del cursor.
(R4) Si se encuentra el Pareado, el cursor se situará a la izquierda del mismo.
Se cree conveniente esta forma porque las ejecuciones sucesivas, sin mover el cursor, siempre nos llevan a los mismos pareados.
Condicionantes:
(C1) Necesidad de enviar resultado a una ventana.
Aunque resulte curioso, si no se envía ningún resultado a otra ventana, ya sea la ventana principal de Visual FoxPro o una ventana activa definida por el usuario, el programa tarda mucho más en ejecutarse. No he logrado averiguar este extraño comportamiento. En la versión actual, por cada carácter procesado se escribe, en esa ventana, un único carácter (que se asemeja a un molino en movimiento [recuerdo de viejos tiempos ;)]) en la columna 1 y siempre en la misma línea.
(C2) Determinar condición de principio/fin de archivo por número de caracteres repetidos.
Al llegar el principio o fin del archivo en la ventana en la que hacemos la búsqueda, dado que el cursor no puede ir más allá, siemre se lee el mismo carácter (el primero o el último).
Ante la imposibilidad de determinar cuándo se ha llegado al principio o al final del archivo, se establece como heurística para que se cumpla dicha condición la lectura de un mismo carácter un número determinado de veces (véase parámetro).
(C3) Obtención de caracteres retrasada.
En cada ejecución del programa se obtiene como primer carácter el último carácter procesado en la ejecución anterior. Este extraño suceso que no me explico (¿quizá esté haciendo algo mal?) se ha solventado asignando al portapapeles (variable del sistema _CLIPTEXT) un carácter especial (CHR(255)).
Parámetros:
(P1) tnNumMaxCaracteresRepetidos (e) : Número de veces que se debe leer un mismo carácter para establecer la condición de princpio/fin de archivo. (véase (C2)).
Valor predeterminado: 100.
(P2) tlDebug (e) : Establece el programa en modo depuración para mostrar trazas.
.T. : Modo depuración.
.F. : Modo normal (Valor predeterminado).
(P3) tlCrearVentanaSalida (e) : Necesario por (C1).
.T. : Resultados a una ventana que crea el programa.
.F. : Resultados a la ventana principal de Visual FoxPro (Valor predeterminado).
Uso:
Para un uso cómodo se puede asociar a una combinación de teclas, como por ejemplo:
ON KEY LABEL ALT+8 DO "BuscarPareado" * ON KEY LABEL ALT+8 DO "D:\JavierValero\vfp\Experimentos\Proyecto\Pruebas\progs\BuscarPareado" WITH 50,.F.,.F.
Así, para una ejecución correcta, situar el cursor a la derecha de un carácter '(' o ')' y pulsar ALT+8, y el cursor se nos situaría en su paréntesis asociado.
Bugs:
(BUG1) Por la heurística adoptada en (C2), el programa puede devolver una búsqueda insatisfactoria en ciertos casos. Por ejemplo, es muy común poner comentarios incluyendo muchos '*'. Si el par de tokens asociados se encuentra entre líneas de ese tipo y el parámetro es muy pequeño, el programa puede devolver que se ha llegado al principio/fin de archivo cuando no es cierto. Para evitar esto, habría que aumentar el valor del parámetro mencionado.
(BUG2) Por el criterio adoptado en (C3) si el carácter en la ventana de búsqueda
Mejoras:
- No limitar los token a caracteres sino a palabras, para que así se puedan encontrar los IF-ENDIF, DO WHILE-ENDDO, ...
************************************** * BuscarPareado.prg ************************************** * Autor: JVA * Versión: 1.0 * Fecha creación: 27/03/2001 * Modificaciones realizadas: Ninguna * Notas: Debido a la inexperiencia del autor, * puede que haya conceptos mal * interpretados de Visual FoxPro y * además puede que parte del apartado * de 'Condicionantes:' sea erróneo. ************************************** LPARAMETERS tnNumMaxCaracteresRepetidos, tlDebug, tlCrearVentanaSalida PRIVATE ALL * Para simular un molinillo en movimiento cuando * el programa está en ejecución #DEFINE MOLINILLO "|/-" #DEFINE MOLINILLO_NUM_CARACTERES 20 #DEFINE MOLINILLO REPLICATE("|",5) + ; REPLICATE("/",5) + REPLICATE("-",5) + ; REPLICATE("",5) * Dirección de exploración en la ventana de * búsqueda: hacia la derecha o la izquierda #DEFINE DIRECCION_DERECHA "D" #DEFINE DIRECCION_IZQUIERDA "I" * Valores predeterminados para los parámetros #DEFINE DEFECTO_NUM_MAX_CARACTERES_REPETIDOS 100 #DEFINE DEFECTO_DEBUG .F. #DEFINE DEFECTO_VENTANA .F. * Condicionante (C3): Carácter especial para el portapapeles #DEFINE CARACTER_EN_PORTAPAPELES CHR(255) * Declaración de una variable como privada. #DEFINE PRIVATE_DECL * (C2) Número de caracteres repetidos para * determinar condición principio/fin de archivo LOCAL lnNumMaxCaracteresRepetidos * Carácter seleccionado por el usuario para * buscar su pareado. * Se coge el carácter a la derecha del cursor. LOCAL lcCaracterSeleccionado * Carácter pareado del . LOCAL lcCaracterPareado * Indica si debemos ir cogiendo caracteres * hacia la izquierda o la derecha. LOCAL lcDireccion * Retorno de procedimientos LOCAL llRetorno * Programa en depuración * Si .T. se muestran trazas en la ventana activa LOCAL llDebug * Crear ventana de salida para enviar los resultados LOCAL llCrearVentanaSalida * Para restaurar el antiguo valor de SET TYPEAHEAD LOCAL lcOldTypeAhead * Para restaurar el antiguo valor de SET STATUS BAR LOCAL lcSetStatusBar * Indice para mostrar el molinillo en movimiento PRIVATE_DECL pnMolinillo = 0 lcSetStatusBar = SET("STATUS BAR") IF lcSetStatusBar = "OFF" SET STATUS BAR ON ENDIF * Valores predeterminados lnNumMaxCaracteresRepetidos = DEFECTO_NUM_MAX_CARACTERES_REPETIDOS llDebug = DEFECTO_DEBUG llCrearVentanaSalida = DEFECTO_VENTANA * * Procesar parámetros * IF (PCOUNT() > 0) IF (UPPER(VARTYPE(tnNumMaxCaracteresRepetidos)) = 'N') lnNumMaxCaracteresRepetidos = tnNumMaxCaracteresRepetidos ENDIF ENDIF IF (PCOUNT() > 1) IF (UPPER(VARTYPE(tlDebug)) = 'L') llDebug = tlDebug ENDIF ENDIF IF (PCOUNT() > 2) IF (UPPER(VARTYPE(tlCrearVentanaSalida)) = 'L') llCrearVentanaSalida = tlCrearVentanaSalida ENDIF ENDIF IF (llDebug=.T.) CLEAR ENDIF * Si TYPEAHEAD = cero, no funciona el programa lcOldTypeAhead = STR(SET('TYPEAHEAD')) SET TYPEAHEAD TO 20 * Ventana donde se envían los resultados (C1)LOCAL lcNombreVentanaSalida * Nombre de la ventana de búsqueda LOCAL lcVentanaBusqueda IF ( llCrearVentanaSalida = .T.) lcNombreVentanaSalida = "W_TMP" + SYS(3) DO WHILE (WEXIST(lcNombreVentanaSalida) = .T.) lcNombreVentanaSalida = "W_TMP" + SYS(3) ENDDO lcVentanaBusqueda = WTITLE() DEFINE WINDOW (lcNombreVentanaSalida) FROM 1,1 TO 2,2 CLOSE FLOAT ACTIVATE WINDOW (lcNombreVentanaSalida) ACTIVATE WINDOW (lcVentanaBusqueda) ENDIF _CLIPTEXT = CARACTER_EN_PORTAPAPELES * Coger el carácter a la derecha del cursor (R3) lcCaracterSeleccionado = Caracter DIRECCION_DERECHA) * Condicionante (C3) DO WHILE (lcCaracterSeleccionado == CARACTER_EN_PORTAPAPELES) lcCaracterSeleccionado = Caracter(DIRECCION_DERECHA) ENDDO IF (llDebug=.T.) ? "Carácter seleccionado=[" + lcCaracterSeleccionado + "]" ENDIF * Obtener el Pareado del carácter Seleccionado llRetorno = CaracterPareado(lcCaracterSeleccionado, @lcCaracterPareado, @lcDireccion) IF (llRetorno = .F.) SET MESSAGE TO 'Se desconoce el pareado para el carácter [' + lcCaracterSeleccionado + ']' RETURN .F. ELSE SET MESSAGE TO "Buscando pareado " + lcCaracterPareado + " ..." ENDIF IF (llDebug=.T.) ?? " Carácter pareado=[" + lcCaracterPareado + "]" ? "" ENDIF IF (lcDireccion = DIRECCION_IZQUIERDA) * Nos quedamos en la posición original Caracter(DIRECCION_IZQUIERDA) ELSE * Nada, o entonces volveríamos a leer el * mismo carácter pareado. ENDIF * Número de Pareados encontrados, para tratar * la anidación de pareados LOCAL lnNumPareados * Número de veces que se repite un mismo carácter LOCAL lnNumCaracteresRepetidos * Último carácter leído LOCAL lcCaracter * Para comparar si se repite un carácter LOCAL lcOldCaracter lnNumPareados = 0 lnNumCaracteresRepetidos = 0 lcOldCaracter = CARACTER_EN_PORTAPAPELES * Condicionante (C2) DO WHILE ( (lnNumCaracteresRepetidos(lnNumPareados >= 0) ) lcCaracter = Caracter(lcDireccion) * * Tratar la anidación de los Pareados * * Anidamos pareado IF (lcCaracter == lcCaracterSeleccionado) lnNumPareados = lnNumPareados + 1 ENDIF * Desanidamos pareado IF (lcCaracter == lcCaracterPareado) lnNumPareados = lnNumPareados - 1 ENDIF IF (llDebug=.T.) ?? "NumPareados=" + STR(lnNumPareados) + " " ENDIF * * Tratar la condición de principio/fin de archivo * mediante el reconocimiento de la repetición * del mismo carácter. * IF (lcCaracter == lcOldCaracter) lnNumCaracteresRepetidos = lnNumCaracteresRepetidos + 1 ELSE lnNumCaracteresRepetidos = 0 ENDIF lcOldCaracter = lcCaracter IF (llDebug = .T.) ? lcCaracter + " ASC=" + ALLTRIM(STR(ASC(lcCaracter))) AT 1 ENDIF ENDDO IF ((lnNumPareados * Nos hemos pasado un carácter Caracter(DIRECCION_IZQUIERDA) ELSE * Nada, o entonces volveríamos a leer el * mismo carácter pareado ENDIF IF (lcCaracter == lcCaracterPareado) SET MESSAGE TO "Encontrado " + lcCaracterPareado + "." ELSE IF (lcDireccion == DIRECCION_DERECHA) SET MESSAGE TO "No se encuentra " + lcCaracterPareado + ". Se llegó al final del archivo." ELSE SET MESSAGE TO "No se encuentra " + lcCaracterPareado + ". Se llegó al principio del archivo." ENDIF ENDIF IF ( llCrearVentanaSalida = .T.) RELEASE WINDOW (lcNombreVentanaSalida) ENDIF _CLIPTEXT = CARACTER_EN_PORTAPAPELES * * Restaurar entorno * SET TYPEAHEAD TO &lcOldTypeAhead IF lcSetStatusBar = "OFF" SET STATUS BAR OFF ENDIF ?? "." AT 1 * SET MESSAGE TO RETURN .T. **************** PROCEDURE Caracter **************** LPARAMETERS tcDireccion LOCAL lcC IF (tcDireccion = DIRECCION_DERECHA) KEYBOARD '{SHIFT+RIGHTARROW}{CTRL+C}' PLAIN CLEAR KEYBOARD '{RIGHTARROW}' PLAIN ELSE KEYBOARD '{SHIFT+LEFTARROW}{CTRL+C}' PLAIN CLEAR KEYBOARD '{LEFTARROW}' PLAIN ENDIF DOEVENTS * * Condicionante (C1) * ?? SUBSTR(MOLINILLO, pnMolinillo+1, 1) AT 1 pnMolinillo = (pnMolinillo + 1) % 40 lcC = substr(_cliptext,1,1) return lcC ENDPROC && Caracter **************** PROCEDURE CaracterPareado **************** LPARAMETERS tcCaracterBuscar, tcCaracterPareado, tcDireccion * !!! Pasar los dos últimos por referencia !!! LOCAL llRetorno, lnPosicion LOCAL DIMENSION aPareadosBeg[2] LOCAL DIMENSION aPareadosEnd[2] aPareadosBeg[1] = '(' aPareadosEnd[1] = ')' aPareadosBeg[2] = '[' aPareadosEnd[2] = ']' llRetorno = .T. && Suponemos que sí está lnPosicion = ASCAN(aPareadosBeg,tcCaracterBuscar) IF (lnPosicion != 0) tcCaracterPareado = aPareadosEnd[lnPosicion] tcDireccion = DIRECCION_DERECHA ELSE lnPosicion = ASCAN(aPareadosEnd,tcCaracterBuscar) IF ( lnPosicion != 0) tcCaracterPareado = aPareadosBeg[lnPosicion] tcDireccion = DIRECCION_IZQUIERDA ELSE llRetorno = .F. ENDIF ENDIF RETURN llRetorno ENDPROC && CaracterPareado ****************Javier Valero