UNIVERSIDAD DEL ISTMO Ingeniería en computación Compiladores PROFESOR M. en C. J. Jesús Arellano Pimentel NÚMERO DE PRÁCTICA 5 NOMBRE DE LA PRÁCTICA Redireccionamiento de la entrada-salida en Flex y apertura de archivos en aplicaciones nativas Win32. OBJETIVO GENERAL OBJETIVOS ESPECÍFICOS EQUIPO REQUERIDO SOFTWARE REQUERIDO Aprender a redireccionar la entrada-salida en especificaciones Flex y el manejo del cuadro de diálogo estándar para la apertura de archivos en aplicaciones nativas Win32. - Construir una especificación Flex en consola que redireccione la entrada a través del yyin y la salida a través del yyout. - Construir una aplicación Win32 que implemente la apertura de archivos a través del uso del cuadro de diálogo estándar para la apertura de archivos. - Construir una aplicación Wi32 que integre una el generador léxico generado por Flex y permita redireccionar la entrada (yyin) empleando el cuadro de diálogo estándar para la apertura de archivos. Computadora personal con 512 MB de RAM mínimo. - Windows 7/8 - Microsoft Visual Studio Express 2012 para escritorio de Windows 1.- Fundamentos. 1.1.- Cuadros de diálogo comunes Uno de los objetivos primarios de Windows es promover una interfaz de usuario estándar [Petzold96]. Windows provee de una “librería cuadros de diálogo comunes” la cual esta formada por varias funciones que invocan cuadros de diálogo estándares para: abrir y guardar archivos, buscar y sustituir, imprimir, entre otros. El uso de estas funciones básicamente consiste en rellenar los datos (campos) de una estructura y pasarla por referencia a una función de la librería de cuadros de diálogo comunes. La función crea y muestra el cuadro de diálogo común. Cuando el usuario termina su trabajo con el cuadro de diálogo, la función de librería retorna el control a la aplicación principal dejando la información capturada por el usuario en la estructura que previamente se le pasó a la función de librería. En esta práctica se va a emplear el cuadro de diálogo estándar para abrir archivos. El propósito es obtener en nombre del archivo a abrir el cual que será asignado como valor de inicio a yyin y con ello redireccionar la entrada de la especificación Flex para analizar léxicamente el archivo de texto plano que el usuario determine. Ingeniería en Computación M. C. J. Jesús Arellano Pimentel 1 2.- Desarrollo 2.1 Aplicación Flex de consola para redireccionar la entrada-salida Paso 1 Crear la especificación Flex. La especificación debe permitir copiar la entrada a la salida sustituyendo la palabra hola por “primero” cuando la línea donde se encuentre inicie con la letra a, por “segundo” cuando la línea inicie con la letra b, por “tercero” cuando la línea inicie con la letra c. Todas las demás palabra y todas la líneas no varían. Dicha especificación emplea el uso de flags y es la siguiente: %{ #include<stdlib.h> #include<stdio.h> #include<string.h> int %} %% ^a ^b ^c \n hola flag ='\0'; {flag = 'a'; ECHO;} {flag = 'b'; ECHO;} {flag = 'c'; ECHO;} {flag = '\0'; ECHO;} switch(flag){ case 'a': fprintf(yyout, "primero"); flag ='\0'; break; case 'b': fprintf(yyout, "segundo"); flag ='\0'; break; case 'c': fprintf(yyout, "tercero"); flag ='\0'; break; default: ECHO; break; } %% int yywrap() { return 1; } int main(int argc, char *argv[]) { if(argc != 3){ printf("Parámetros incorrectos\n Uso:%s entrada.txt salida.txt\n", argv[0]); return 0; } yyin = fopen(argv[1], "r"); if(yyin == NULL){ printf("Imposible abrir archive de entrada %s\n",argv[1]); return 0; } yyout = fopen(argv[2], "w"); if(yyout == NULL){ printf("Imposible abrir archivo de salida %s\n",argv[2]); return 0; } yylex(); return 0; } Ingeniería en Computación M. C. J. Jesús Arellano Pimentel 2 Utilizando la aplicación de Notepad de Windows editamos la especificación anterior. Es importante que al guardarla seleccionemos en Tipo de documento “Todos los archivos”, en lugar de “Documentos de texto *.txt”, de esta forma es posible guardar el archivo con extensión .l (punto ele). Se sugiere que el nombre del archivo sea EjeLex2.l y que se guarde en la misma ruta donde se tiene el archivo flex.exe en la carpeta bin. Paso 2 Compilar la especificación guardada en el archivo EjeLex2.l con flex. Para compilar la especificación .l se debe abrir la consola de comandos y cambiarse a la ruta donde previamente se descomprimió el archivo Flex.exe, que es el mismo lugar donde debe encontrarse el archivo EjeLex2.l. El comando de compilación puede verse en la Figura 1. La opción -o indica el nombre del archivo fuente a generar, en nuestro caso el archivo se llamará EjeLex2.c. Figura 1. Carpetas contenidas en el archivo flex-2.5.4a-1-bin.zip y compilación con Flex. Hecho lo anterior se debe crear una aplicación de consola en el Visual Studio Express 2012 para compilar el código generado por Flex. Paso 3 Iniciar el MVS Express 2012 y crear un nuevo proyecto Win32 de consola vacío. Seleccionar la opción del menú Archivo->Nuevo Proyecto. Del cuadro de diálogo Nuevo proyecto en Plantillas seleccionar Visual C++ y Win32; en tipo de aplicación seleccionar Aplicación de consola Win32 Visual C++. El nombre del proyecto o solución podría ser EjeLex2. Presionar el botón Aceptar. En el Asistente para aplicaciones Win32 presionar el botón siguiente; después seleccionar la casilla de verificación correspondiente a la opción adicional Proyecto vacío, posteriormente deshabilitar la casilla de verificación de Comprobación del ciclo de vida de desarrollo de seguridad(SDL); finalmente oprimir el botón Finalizar. Ingeniería en Computación M. C. J. Jesús Arellano Pimentel 3 Paso 4 Adicionar al proyecto vacío el archivo generado por Flex. Copiar el archivo generado por Flex a la ruta del proyecto generado por el MVS Express 2012. En nuestro caso esta en: \Documents\Visual Studio 2012\Projects\EjeLex2\EjeLex2 Una vez copiado el archivo, en la interfaz del MVC Express 2012 dar clic derecho sobre el elemento Archivos de código fuente del Explorador de soluciones situado a la izquierda, del menú que aparece seleccionar Agregar -> Elemento existente … (ver Figura 2). Cuando aparezca el cuadro de diálogo para seleccionar el archivo se debe elegir el archivo copiado anteriormente, es decir: EjeLex2.c. Figura 2. Adicionando un archivo de código fuente al proyecto vacío. Paso 5 Compilar y Ejecutar la aplicación Compilar oprimiendo la tecla F7. El resultado de la compilación puede producir algunas advertencias, sin embargo se debe generar un ejecutable en la carpeta Debug de la solución. Lo anterior es posible verificarlo en el sistema de archivos. La ejecución se debe realizar desde la consola de comandos de Windows de la forma como se presenta en la Figura 3. Observe que no solo se invoca el nombre del archivo ejecutable (EjeLex2.exe), también se incluyen los archivos que han de tomarse como entrada y como salida. El contenido del archivo de entrada puede ser el siguiente: a b c x hola hola hola hola cara cara cara cara de de de de bola bola bola bola El resultado final de la ejecución debe generar el segundo archivo con un contenido similar al archivo de entrada pero tomando en cuenta la especificación del archivo .l, de tal forma que el archivo de salida quedaría de la siguiente forma: a b c x primero cara segundo cara tercero cara hola cara de Ingeniería en Computación de bola de bola de bola bola M. C. J. Jesús Arellano Pimentel 4 Figura 3. Ejecución de la aplicación EjeLex2.exe. 2.2 Aplicación Win32 para implementar el cuadro de diálogo estándar “Abrir archivo” Paso 1 Iniciar el MVS Express 2012. Paso 2 Crear un nuevo proyecto Win32. Seleccionar la opción del menú Archivo->Nuevo Proyecto. Del cuadro de diálogo Nuevo proyecto en Plantillas seleccionar Visual C++ y Win32; en tipo de aplicación seleccionar Proyecto Win32 Visual C++ (ver Figura 1). El nombre del proyecto o solución podría ser CDlgAbrir. Presionar el botón Aceptar. Figura 4. Cuadro de diálogo Nuevo Proyecto para la aplicación CDlgAbrir. Paso 3 Configurar el Proyecto Actual. En el Asistente para aplicaciones Win32 presionar el botón siguiente; después deshabilitar la casilla de verificación de Comprobación del ciclo de vida de desarrollo de seguridad(SDL); finalmente oprimir el botón Finalizar. Ingeniería en Computación M. C. J. Jesús Arellano Pimentel 5 Paso 4 Adicionar un nuevo elemento al menú. Para adicionar un nuevo elemento al menú es necesario editar el archivo de recursos con extensión .rc. En el explorador de soluciones del Visual Studio podemos localizar el archivo CDlgAbrir.rc correspondiente a los recursos (ver Figura 5), para editarlo será necesario dar un clic derecho sobre el archivo y del menú flotante elegir la opción <>Ver código. Figura 5. Apertura del archivo de recursos de la aplicación CDlgAbrir. Una vez abierto el archivo se adicionará, después de la línea 41, la siguiente línea de código: MENUITEM "&Abrir", IDM_ABRIR de tal forma que la sección del menú quede de la siguiente forma: IDC_MENUWIN32 MENU BEGIN POPUP "&Archivo" BEGIN MENUITEM "&Abrir", MENUITEM "&Salir", END POPUP "Ay&uda" BEGIN MENUITEM "&Acerca de...", END END Ingeniería en Computación IDM_ABRIR IDM_EXIT IDM_ABOUT M. C. J. Jesús Arellano Pimentel 6 Una vez hecho lo anterior, adicionar al archivo de encabezado Resource.h la constante simbólica asociada al menú (IDM_ABRIR). La Figura 6 muestra un segmento de dicho archivo con la nueva línea de código resaltada en azul. Figura 6. Adición de la constante simbólica IDM_ABRIR en el archivo Resoruce.h. Finalmente se deberán guardar ambos archivos. Paso 5 Modificar el archivo stdafx.h El Archivo stdafx.h deberá ser modificado con la adición de la librería que permite el uso de los cuadros de diálogo comunes en Windows. La línea de código a agregar en este archivo debe ir al final del mismo y es la siguiente: #include <commdlg.h> Paso 6 Procesar el mensaje de la nueva opción del menú. El procesamiento del mensaje para la nueva opción del menú se hará a través de su constante simbólica asociada. Para esto será necesario editar el archivo MenuWin32, en particular la función WndProc que inicia aproximadamente en la línea de código #126. Habrá que adicionar un nuevo caso a la estructura switch (wmId) que es la encargada de procesar los mensajes generados por las opciones del menú, dicha estructura inicia aproximadamente en la línea de código #138. El nuevo caso deberá colocarse previo al caso default:, de esta forma, el código de la estructura deberá quedar de la siguiente forma1: 1 Las líneas de código resaltadas en amarillo son las que deberán adicionarse a la estructura original. Ingeniería en Computación M. C. J. Jesús Arellano Pimentel 7 // Analizar las selecciones de menú: switch (wmId) { case IDM_ABOUT: DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); break; case IDM_EXIT: DestroyWindow(hWnd); break; case IDM_ABRIR: TCHAR szFile[MAX_PATH], szCaption[64 + _MAX_FNAME + _MAX_EXT]; OPENFILENAME ofn; ZeroMemory(szFile, MAX_PATH); ZeroMemory(&ofn, sizeof(OPENFILENAME)); ofn.lStructSize = sizeof(OPENFILENAME); ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_CREATEPROMPT; ofn.hwndOwner = hWnd; ofn.lpstrFilter = _T("Tipos de Archivos soportados(*.txt) \0*.txt\0Archivos de texto (*.txt)\0\0"); ofn.lpstrTitle = _T("Abrir archivo"); ofn.lpstrFile = szFile; ofn.nMaxFile = MAX_PATH; if (IDOK == GetOpenFileName(&ofn)){ wsprintf(szCaption,_T("%s - %s"),szTitle, szFile[0] ? szFile : _T("Sin archivo abierto")); SetWindowText(hWnd, szCaption); } break; default: return DefWindowProc(hWnd, message, wParam, lParam); } En el código resaltado en amarillo la primera línea corresponden a las variables que van a almacenar el nombre del archivo y el título de la ventana principal, la segunda línea corresponde al nombre de la estructura que se debe configurar para personalizar el cuadro de diálogo estándar de Abrir archivo. Las siguientes líneas de código limpian las variables definidas y establecen los valores para la estructura ofn configurando el cuadro de diálogo para que solo acepte archivos del tipo *.txt. El llamado a la función GetOpenFileName(&ofn) permite visualizar el cuadro de diálogo y retorna IDOK en caso de que el usuario seleccione un archivo para abrir y pulse el botón de Aceptar. Si lo anterior ocurre entonces se cambia el título de la ventana principal adicionando el nombre, con la ruta, del archivo seleccionado para abrir. Nota: Es importante mencionar que la línea de código correspondiente a ofn.lpstrFilter debe ser editada en una sola línea, en este documento quedó en 2 líneas por razones de espacio. Paso 6 Compilar y ejecutar para visualizar los cambios Una vez realizados los cambios de los pasos anteriores guardar el archivo y ejecutar presionando la tecla F5. El resultado debería ser el mostrado en la Figura 7 una vez que seleccione el la nueva opción del menú Archivo. Ingeniería en Computación M. C. J. Jesús Arellano Pimentel 8 Figura 7. Ejecución de la aplicación CDlgAbrir. Después de que se ha seleccionado un archivo el título de la ventana principal debe contener el archivo a abrir. 2.3 Integrar a la aplicación CDlgAbrir la especificación Flex del apartado 2.1 Paso 1 Modificar la especificación Flex eliminando la función main. Debido a que las aplicaciones Win32 cuentan con su propia función principal será necesario eliminar la función main de la especificación .l descrita en el apartado 2.1 e incluir una nueva librería (io.h) Ahora la especificación deberá ser la siguiente: %{ #include<stdlib.h> #include<stdio.h> #include<string.h> #include<io.h> int %} %% ^a ^b ^c \n hola flag ='\0'; {flag = 'a'; ECHO;} {flag = 'b'; ECHO;} {flag = 'c'; ECHO;} {flag = '\0'; ECHO;} switch(flag){ case 'a': fprintf(yyout, "primero"); flag ='\0'; break; case 'b': fprintf(yyout, "segundo"); flag ='\0'; break; case 'c': fprintf(yyout, "tercero"); flag ='\0'; break; default: ECHO; break; } %% int yywrap() { return 1; } Ingeniería en Computación M. C. J. Jesús Arellano Pimentel 9 Esta nueva especificación se puede renombrar como EjeLex3.l. Paso 2 Compilar la especificación guardada en el archivo EjeLex3.l con flex. Para compilar la especificación .l se debe abrir la consola de comandos y cambiarse a la ruta donde previamente se descomprimió el archivo Flex.exe, que es el mismo lugar donde debe encontrarse el archivo EjeLex3.l. El comando de compilación cambia en cuanto a la extensión del archivo a generar, dicho comando es el siguiente: flex.exe -oEjeLex3.h EjeLex3.l La ejecución anterior del comando generará un archivo de encabezado .h en lugar de un archivo fuente .c. Paso 3 Adicionar el archivo EjeLex3.h generado por flex a la aplicación CDlgAbrir Copiar el archivo EjeLex3.h a la ruta del proyecto CDlgAbrir y agregarlo al proyecto como “Archivo de encabezado” (esta operación es similar a la descrita en el paso 4 del apartado 2.1 de ésta práctica). Hecho lo anterior, se debe agregar la inclusión de este nuevo archivo de encabezado al archivo CDlgAbrir.cpp, aproximadamente después de la línea #5 con la siguiente línea de código: #include "EjeLex3.h" Paso 4 Modificar el procesamiento del mensaje IDM_ABRIR Ahora el código del procesamiento de dicho mensaje deberá ser el siguiente: case IDM_ABRIR: TCHAR szFile[MAX_PATH], szCaption[64 + _MAX_FNAME + _MAX_EXT]; OPENFILENAME ofn; ZeroMemory(szFile, MAX_PATH); ZeroMemory(&ofn, sizeof(OPENFILENAME)); ofn.lStructSize = sizeof(OPENFILENAME); ofn.Flags= OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_CREATEPROMPT; ofn.hwndOwner = hWnd; ofn.lpstrFilter = _T("Archivos soportados(*.txt)\0*.txt\0Archivos de texto (*.txt)\0\0" ); ofn.lpstrTitle = _T("Abrir archivo"); ofn.lpstrFile = szFile; ofn.nMaxFile = MAX_PATH; if (IDOK == GetOpenFileName(&ofn)){ wsprintf(szCaption,_T("%s - %s"),szTitle, szFile[0] ? szFile : _T("Sin archivo abierto")); SetWindowText(hWnd, szCaption); char scFile[MAX_PATH]; wcstombs(scFile,szFile,MAX_PATH); yyin = fopen(scFile, "r"); if(yyin == NULL){ MessageBox(hWnd, L"No es posible cargar yyin", L"Error",MB_OK | MB_ICONERROR); }else{ yyout = fopen("salidaw.txt", "w"); if(yyout == NULL){ MessageBox(hWnd, L"No es posible crear yyout", L"Error",MB_OK | MB_ICONERROR); }else{ yylex(); fclose(yyin); fclose(yyout); } } } break; Ingeniería en Computación M. C. J. Jesús Arellano Pimentel 10 Paso 5 Guardar, compilar, corregir y ejecutar Guardar todos los archivos y compilar. El resultado de la compilación arrojará un error en el archivo EjeLex3.h, en la línea correspondiente a al inclusión de la librería unistd.h, basta con comentar esta línea y el error se corrige (//#include <unistd.h>). Entonces estamos en la posibilidad de ejecutar la aplicación redireccionando la entrada (yyin) de Flex al archivo seleccionado desde el cuadro de diálogo estándar para abrir archivos en Windows. 3.- Referencias [Petzold96] Petzold Charles, et al. Programación en Windows 95. Mc Graw Hill – Microsoft Press. 1996. Ingeniería en Computación M. C. J. Jesús Arellano Pimentel 11