¿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
No hay comentarios. :
Publicar un comentario
Los comentarios son moderados, por lo que pueden demorar varias horas para su publicación.