lunes, 3 de septiembre de 2007

Reimplementando BIFs en REXX

En REXX es frecuente referirse oficiosamente a toda función que forma parte del núcleo del lenguaje como BIF: Built-In Function. El estándar ANSI de 1996 define una serie de funciones dentro de esta categoría como por ejemplo abbrev, words, date o address. En general las implementaciones del lenguaje tienen todas las BIFs definidas en el estándar, añadiendo aportaciones propias o tal vez careciendo de alguna concreta.

Lo peculiar del caso es que REXX permite que el programador defina las funciones internas de su programa (aquellas que se incluyen dentro del mismo fichero) utilizando los mismos nombres que alguna BIF y aun así seguir utilizando la BIF cuyo nombre hemos utilizado.


/* words.rex */

parse pull frase
say words(frase)

exit


Este sencillo ejemplo se limita a contar el número de palabras que tiene una cadena que introduce el usuario utilizando la BIF words. La línea con la sentencia parse pull frase se limita a meter la entrada del usuario en la variable frase. Y la siguiente línea nos dice la salida de la función words aplicada sobre la variable frase, es decir, muestra en pantalla el número de palabras.

Si el programador introduce una función en el código como en el siguiente caso:


/* words.rex */

parse pull frase
say words(frase)

exit

words: procedure
return 'No me apetece contar palabras'


El resultado es que en lugar de ejecutarse la BIF words, se ejecuta nuestra función. Es decir, que sea cual sea la entrada del usuario, el programa siempre imprimirá que no le apetece contar palabras.

A pesar de tener ocupado el nombre de la BIF words, podemos seguir accediendo a ella llamandola por su nombre en mayúsculas entrecomillado (por comillas simples). Así, en el siguiente ejemplo, se imprime siempre que no le apetece contar palabras e inmediatamente después el número de palabras tal como lo cuenta la BIF words:


/* words.rex */

parse pull frase
say words(frase)
say 'WORDS'(frase)

exit

words: procedure
return 'No me apetece contar palabras'


Evidentemente podemos ejecutar la BIF desde dentro de nuestra función:


/* words.rex */

parse pull frase
say words(frase)

exit

words: procedure
parse arg cadena
return 'WORDS'(cadena)


Esto nos permite cierto número de posibilidades como cambiar el comportamiento de las BIFs de nuestro intérprete. Esto puede ser interesante, por ejemplo, cuando un intérprete no implementa una BIF como estamos acostumbrados a utilizar. Imaginemos que un programador acostumbrado a las características de la BIF date para hacer aritmética de fechas propias del estándar y presentes en Object REXX y Regina desea que su código funcione en REXX clásico en OS/2 (que carece de esa característica). Si hace una función date que supla esas características ausentes en ese intérprete, solamente tiene que añadir esa función en la versión de su programa para OS/2 y no tendrá que tocar ninguna línea más de código.

Lo interesante es que podemos reimplementar muchas de las BIFs orientadas a manejo de cadenas solamente utilizando bucles condicionales y la instrucción PARSE. En el caso que nos ocupa, podríamos hacer:


words: procedure
parse arg string
n = 0
do while string <> ''
parse var string . string
n = n + 1
end
return n


parse coge el argumento de la función que hemos escrito y lo introduce en la variable string, mientras la variable string no esté vacía parse coge la primera palabra de la variable string y la descarta (.) metiendo el resto en string. Cada vez que descarta una palabra, aumenta el contador, y cuando las ha descartado todas, nos devuelve el valor de ese contador, es decir, del número de palabras. (Podríamos habernos ahorrado un par de líneas, pero he preferido dejarlo así por claridad.)

Como ejercicio para principiantes, el intentar reimplementar BIFs sin utilizar BIFs es algo, creo yo, muy educativo. Permite apreciar el valor de la instrucción parse y familiarizarse con las BIFs orientadas a cadenas.

Solamente podemos "redefinir" las BIFs a través de funciones internas de nuestro programa. Las funciones externas no nos valdrán y tampoco aquellas que estén en el macroespacio (si la implementación lo tiene). Pero cuando la BIF no está definida en nuestro intérprete (como ocurre con countstr en REXX clásico para OS/2), entonces podemos implementarla como una función externa y cargarla en el macroespacio...

Pero, como decía Jack Lemmon en Irma la dulce: Eso es otra historia.

No hay comentarios: