sábado, 15 de octubre de 2016

Emacs Lisp - Función/comando para reemplazar coincidencias de texto.

Una de las grandes ventajas de la utilización del editor Gnu Emacs es su capacidad para extender las funciones del mismo editor a través de elisp.

 Para empezar a adentrarme en el lenguaje, decidí escribir una pequeña función que me ayudara a rápidamente cambiar todas las coincidencias de una cadena de texto en un buffer.  A continuación, explico como lograr esto de manera sencilla.

Funciones y sus argumentos.
Para definir una función en elisp, usamos la siguiente palabra (o símbolo, para ser más correcto según lisp):

defun 

Simplemente una abreviación de "define function". Este símbolo le dice a lisp, que el consecuente código dentro de la lista definida sea enlazado a otro símbolo: el nombre de nuestra función. Por ejemplo:

(defun suma (x y)
      (+ x y)
) 

¿Qué hace esta función? Es simple. Toma dos argumentos recibidos y los suma. En otras palabras, enlaza el símbolo "suma" con el código consecuente dentro de la función, el de la suma.
Ahora bien, observemos que después de la definición del nombre de la función viene otra lista. Esta es la lista de los parametros a usar en nuestro función y que deben ser pasados al momento de llamarla. Por ejemplo, evaluemos la siguiente función:

(suma 3 4)

Deberá imprimirse en nuestro mini-buffer el número 7.

La función interactive.

La función interactive hace que nuestra función pueda volverse un comando (pueda ser llamada con M-x/alt-x). Distintos argumentos dados a nuestra función, le dan distintos comportamientos a la llamada de nuestro comando/función.
Para nuestro caso, usaremos el argumento "s", como argumento de interactive. Este argumento, leerá del minibuffer y lo guardará en nuestra lista de argumentos dados a la función.

La función save-excursion.
Esta función nos permitirá guardar la posición actual del cursor (su punto, de acuerdo a la terminología de emacs). Esto debido a que mientras cuando se haga la búsqueda y reemplazo, el punto/point/cursor estará moviendose a través de todo el texto. Para evitar que, al usar la función, nos deje en donde no estabamos colocados usamos save-excursion.

La función beginning-of-buffer.
Retorna el cursor al principio del buffer.

re-search-forward, regexp-quote y replace-match.
Estas tres funciones son las que harán el trabajo fuerte de nuestro comando. 
re-search-forward nos moverá a través de las coincidencias del texto, como un puntero. Al ser llamada, regresará la ubicación de la coincidencia, en la siguiente iteración nos dará la siguiente coincidencia.
regexp-quote: Le daremos un string de argumento y nos retornará la forma de evaluar el string como si fuera una expresión regular.
replace-match: La llamamos para reemplazar la coincidencia que teníamos con re-search-forward dandole el string a reemplazar.

 Nuestra función: replace-all-ins-of.


(defun replace-all-inst-of (x y)
  ;; pregunta por los argumentos
  (interactive "sSearch (with regexp): \nsReplace: ")
  (save-excursion ;; guardar el actual punto
    (beginning-of-buffer) ;; moverse al principio del buffer 
    ;; Empezar la busqueda hacia adelante
    (while (re-search-forward (regexp-quote x) nil t)
      (replace-match y))
    )
  )

Evaluamos la función al ponernos al final de la misma y haciendo Ctrl-x CTRL-e. Esto también agregará nuestra función a los comandos de emacs.
Finalmente para probar la función, hacemos M-x replace-all-inst-of o ALT-x replace-all-inst-of.
Cabe destacar que para "instalar" de forma permanente nuestra función como un comando necesitamos guardarlo en nuestro archivo de inicio .emacs. De esta manera, siempre estará disponible en las siguientes sesiones de nuestro editor.

sábado, 17 de septiembre de 2016

Obtener el ensamblador de un programa en C en Linux.

El otro día me dio curiosidad de ver como se veía las instrucciones en ensamblador de un programa sencillo en C, en Linux. A continuación, muestro el ejemplo que hice de manera sencilla.

Código en C:
Yo decidí, para empezar con algo simple, hacer el dump de un pequeño código usando una sentencia for:

1
2
3
4
5
6
7
#include <stdio.h>

int main() {
  int i;
  for(i = 1; i <= 10; i++);  
  return 0;
}

Nombramos el archivo como main.c

Escribiendo el Makefile 
Para evitarnos el gran trabajo de escribir dos comandos, realizaremos un archivo Makefile que corra automáticamente los comandos con las flags necesarias.

1
2
3
all:
 gcc -g -c main.c -fverbose-asm -O0
 objdump -d -M intel -S main.o 

Cabe destacar que objdump obtendrá el código ensamblador con una sintaxis de intel, como se específica en el flag -M intel; si se desea obtener con otra sintaxis, debe buscarse en la manual del comando.


Obteniendo el ensamblador:
Corremos el siguiente comando:

1
$ make


Y se nos mostrará en la terminal el ensamblador resultado de nuestro código, para este caso específico:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
gcc -g -c main.c -fverbose-asm -O0
objdump -d -M intel -S main.o 

main.o:     formato del fichero elf64-x86-64


Desensamblado de la sección .text:

0000000000000000 <main>:
#include <stdio.h>

int main() {
   0: 55                    push   rbp
   1: 48 89 e5              mov    rbp,rsp
   4: 48 83 ec 10           sub    rsp,0x10
  int i;
  for(i = 1; i <= 10; i++);
   8: c7 45 fc 01 00 00 00  mov    DWORD PTR [rbp-0x4],0x1
   f: eb 04                 jmp    15 <main+0x15>
  11: 83 45 fc 01           add    DWORD PTR [rbp-0x4],0x1
  15: 83 7d fc 0a           cmp    DWORD PTR [rbp-0x4],0xa
  19: 7e f6                 jle    11 <main+0x11>
  i++;
  1b: 83 45 fc 01           add    DWORD PTR [rbp-0x4],0x1
  foo();
  1f: b8 00 00 00 00        mov    eax,0x0
  24: e8 00 00 00 00        call   29 <main+0x29>
  
  return 0;
  29: b8 00 00 00 00        mov    eax,0x0
}
  2e: c9                    leave  
  2f: c3                    ret   

Cualquier duda, comentario o sugerencia es bienvenida en los comentarios!

Interviewbit Question - Solución al problema de la potencia de dos enteros.

Este es un problema de tipo entrevista, encontrado en interviewbit.com. El problema nos menciona que debemos encontrar si dada una entr...