Este documento discute varios temas relacionados con los lenguajes de programación, incluyendo herramientas para la construcción de compiladores, aplicaciones de los lenguajes de programación, una breve reseña histórica de los primeros lenguajes de programación y cómo se instalan y usan las herramientas Flex y Bison.
1. NOMBRE: Esteban P. Pillajo M.
FECHA: 20 de junio de 2019
ESCUELA DE INGENIERÍA EN SISTEMAS
TALLER DE COMPILADORES
Utilizando la herramienta de búsqueda de la Web, Investigar los siguientes temas:
Herramientas para la construcción de procesadores de lenguaje.
A continuación se muestran algunas de las herramientas disponibles que pueden utilizarse
para la realización de la Práctica de Procesadores de Lenguajes. Estas herramientas
funcionan bajo Windows, aunque se puede utilizar, si se desea, cualquier otra
herramienta. No obstante, se recuerda que los ficheros que debe generar la práctica no
son generados automáticamente por estas herramientas (y en concreto, el árbol sintáctico
generado por la herramienta Gold Parser no es correcto ni válido para aprobar la práctica).
Aplicación de los lenguajes.
Los lenguajes de programación hoy en día tienen una infinidad de aplicaciones,
básicamente cualquier objeto electrónico tiene cierto grado de programación. Algunos de
los más comunes son C++ y JAVA, también existe HTML, HTTP, XML, XAML y C#,
este último actualmente es el
Más utilizado en todos los dispositivos y redes basados en MICROSOFT (Xbox 360,
Windows Mobile, Windows Phone, Windows Cloud, Zune, etc.). Ya que los lenguajes
de programación son informáticamente un puente entre el Hardware y el Software estos
permiten que las computadoras puedan establecer conexión con un celular, una cámara o
una consola portátil de videojuego. Otra de las aplicaciones de los lenguajes de
programación son las matemáticas como las calculadoras, cajas registradoras, cajeros
automáticos, por solo mencionar algunos ejemplos sencillos. Existen también niveles de
programación mucho más complejos como los videojuegos o los pilotos automáticos de
los aviones comerciales o las máquinas de juego de los casinos que siguen un patrón de
probabilidad a partir de un arreglo de números al azar establecido por una programación
numérica. La robótica es la combinación de mecánica, electrónica y programación, la cual
en base a sensores y mecanismos sigue una serie de instrucciones algorítmicas las cuales
le permiten por ejemplo a un brazo robótico montar una rueda, ajustar un tornillo o cortar
2. un rectángulo de 3 cm cuadrados en una placa de acero. Con el avance de la tecnología
los límites de la programación se vuelven cada vez más distantes.
Reseña Histórica
Los primeros lenguajes de programación surgieron de la idea de Charles Babagge, la cual
se le ocurrió a este hombre a mediados del siglo XIX. Era un profesor matemático de la
universidad de Cambridge e inventor inglés, que al principio del siglo XIX predijo
muchas de las teorías en que se basan los actuales ordenadores. Consistía en lo que él
denominaba la maquina analítica, pero que por motivos técnicos no pudo construirse
hasta mediados del siglo XX. Con él colaboro Ada Lovedby, la cual es considerada como
la primera programadora de la historia, pues realizo programas para aquélla supuesta
máquina de Babagge, en tarjetas perforadas. Como la maquina no llego nunca a
construirse, los programas de Ada, lógicamente, tampoco llegaron a ejecutarse, pero si
suponen un punto de partida de la programación, sobre todo si observamos que en cuanto
se empezó a programar, los programadores utilizaron las técnicas diseñadas por Charles
Babagge, y Ada, que consistían entre otras, en la programación mediante tarjetas
perforadas. A pesar de ello, Ada ha permanecido como la primera programadora de la
historia. Se dice por tanto que estos dos genios de antaño, se adelantaron un siglo a su
época, lo cual describe la inteligencia de la que se hallaban dotados.
En 1823 el gobierno Británico lo apoyo para crear el proyecto de una máquina de
diferencias, un dispositivo mecánico para efectuar sumas repetidas. Pero Babagge se
dedicó al proyecto de la máquina analítica, abandonando la máquina de diferencias, que
se pudiera programar con tarjetas perforadas, gracias a la creación de Charles Jacquard
(francés). Este hombre era un fabricante de tejidos y había creado un telar que podía
reproducir automáticamente patrones de tejidos, leyendo la información codificada en
patrones de agujeros perforados en tarjetas de papel rígido. Entonces Babagge intento
crear la máquina que se pudiera programar con tarjetas perforadas para efectuar cualquier
cálculo con una precisión de 20 dígitos. Pero la tecnología de la época no bastaba
parahacer realidad sus ideas. Si bien las ideas de Babagge no llegaron a materializarse de
forma definitiva, su contribución es decisiva, ya que los ordenadores actuales responden
a un esquema análogo al de la máquina analítica. En su diseño, la máquina constaba de
cinco unidades básicas: 1) Unidad de entrada, para introducir datos e instrucciones; 2)
Memoria, donde se almacenaban datos y resultados intermedios; 3) Unidad de control,
para regular la secuencia de ejecución de las operaciones; 4) Unidad Aritmético-Lógica,
que efectúa las operaciones; 5) Unidad de salida, encargada de comunicar al exterior los
resultados. Charles Babbage, conocido como el "padre de la informática" no pudo
completar en aquella época la construcción del computador que había soñado, dado que
faltaba algo fundamental: la electrónica. El camino señalado de Babbage, no fue nunca
abandonado y siguiéndolo, se construyeron los primeros computadores.
Cuando surgió el primer ordenador, el famoso ENIAC (Electronic Numerical Integrator
And Calculator), su programación se basaba en componentes físicos, o sea, que se
programaba, cambiando directamente el Hardware de la máquina, exactamente lo que sé
hacia era cambiar cables de sitio para conseguir así la programación de la máquina. La
entrada y salida de datos se realizaba mediante tarjetas perforadas.
3. Diseño y construcción de un compilador.
En el proceso de construcción de compiladores se integran muchos conceptos diferentes
de las Ciencias de la Computación:
Algoritmos de búsqueda.
Árboles, Hashing.
Programación modular.
Lenguaje Assembly.
Análisis: Se trata de la comprobación de la corrección del programa fuente, e incluye las
fases correspondientes al Análisis léxico (que consiste en la descomposición del
programa fuente en componentes léxicos), Análisis sintáctico (agrupación de los
componentes léxicos en frases gramaticales) y Análisis semántico (comprobación de la
validez semántica de las sentencias aceptadas en la fase de Análisis Sintáctico).
Síntesis: Su objetivo es la generación de la salida expresada en el lenguaje objeto y suele
estar formado por una o varias combinaciones de fases de Generación de Código
(normalmente se trata de código intermedio o de código objeto) y de Optimización de
Código (en las que se busca obtener un código lo más eficiente posible).
Las herramientas Flex y Bison.
Son herramientas para el desarrollo de lenguaje, flex sirve para construir analizadores
léxicos, esta herramienta genera código en c que puede ser compilado. Bison es la
herramienta encargada de la generación de analizadores sintácticos, es una herramienta
compatible con YACC, también genera código en C o C++ y puede ser fácilmente
integrado con Flex.
¿Qué es flex?
El Flex define las reglas de reconocimiento de símbolos (Tokens) a partir de
expresiones regulares. Cuando un Token es reconocido por uno de estos patrones de
agrupamiento se le define una acción, por lo general esta acción es devolver el Tipo y el
valor (lexema). El Flex cuando se utiliza combinado con el Bison, utiliza las
definiciones de los Tokens realizadas en el Bison para la comunicación entre ellos.
Como se instala Flex y Bison
1. Descarga el software disponible en el sitio de la cátedra.
2. Instalar el software en la unidad C: (para explicar a partir del punto 4 se tendrá como
hipótesis de que flex y bison han sido instalados en la ruta: C:GnuWin32 donde
contiene una subcarpeta llamada bin donde se encuentran los programas respectivos)
3. Flex y bison son aplicaciones de consola, por lo que se deberá entrar al Símbolo del
sistema y tipear líneas de comando para ejecutar Flex. Una alternativa es crear un
archivo de proceso por lotes (*.bat) que contenga las líneas de comando para la
ejecución de Flex y Bison y/o la compilación del archivo generado.
4. 4. Si deseas que flex y bison se integren al conjunto de variables del entorno (esto te va
a permitir llamar a flex/bison desde cualquier ubicación en la línea de comandos) debes
hacer lo siguiente:
Clic derecho en “Mi PC”.
Selecciona “Propiedades”
Clic en la pestaña “Opciones Avanzadas”
Presiona el botón “Variables de entorno”
En la ventana de variables de entorno, ubicarse en la sección “Variables del
sistema” luego haz clic en PATH y luego en el botón “Modificar” (si no está
hacer clic en “Nueva” y agregar PATH)
En la nueva ventana, escribir la ruta completa al directorio “bin” de la aplicación
flex/bison. Si existe otro valor, separarlos con comas.
Aceptar los cambios y luego reiniciar el sistema operativo.
5. Si deseas instalar un compilador de C como MinGwin, deberás integrar la ruta de
acceso al compilador a las variables de entorno para facilitar la llamada al programa.
Por ejemplo si se instaló MingWin en “C:Mingw” y dentro de la carpeta “bin” se
encuentra “gcc.exe” que es el ejecutable, entonces de deberá agregar (análogo a los
pasos anteriores) lo siguiente:
6. Cuando tengas listo podrás llamar a flex/bison desde el símbolo del sistema sin
necesidad de ubicarte en la carpeta donde ha sido instalado flex/bison.
Patrones en flex
Los patrones en la entrada se escriben utilizando un conjunto extendido de expresiones
regulares. Estas son:
`x'
empareja el caracter `x'
`.'
cualquier caracter (byte) excepto una línea nueva
`[xyz]'
una "clase de caracteres"; en este caso, el patrón empareja una `x', una `y', o una `z'
`[abj-oZ]'
una "clase de caracteres" con un rango; empareja una `a', una `b', cualquier letra desde
la `j' hasta la `o', o una `Z'
`[^A-Z]'
una "clase de caracteres negada", es decir, cualquier caracter menos los que aparecen en
la clase. En este caso, cualquier caracter EXCEPTO una letra mayúscula.
`[^A-Zn]'
cualquier caracter EXCEPTO una letra mayúscula o una línea nueva
`r*'
5. cero o más r's, donde r es cualquier expresión regular
`r+'
una o más r's
`r?'
cero o una r (es decir, "una r opcional")
`r{2,5}'
donde sea de dos a cinco r's
`r{2,}'
dos o más r's
`r{4}'
exactamente 4 r's
`{nombre}'
la expansión de la definición de "nombre" (ver más abajo)
`"[xyz]"foo"'
la cadena literal: [xyz]"foo
`x'
si x es una `a', `b', `f', `n', `r', `t', o `v', entonces la interpretación ANSI-C de x. En otro
caso, un literal `x' (usado para indicar operadores tales como `*')
`0'
un caracter NUL (código ASCII 0)
`123'
el caracter con valor octal 123
`x2a'
el caracter con valor hexadecimal 2a
`(r)'
empareja una R; los paréntesis se utilizan para anular la precedencia (ver más abajo)
`rs'
la expresión regular r seguida por la expresión regular s; se denomina "concatenación"
6. `r|s'
bien una r o una s
`r/s'
una r pero sólo si va seguida por una s. El texto emparejado por s se incluye cuando se
determina si esta regla es el "emparejamiento más largo", pero se devuelve entonces a la
entrada antes que se ejecute la acción. Así que la acción sólo ve el texto emparejado por
r. Este tipo de patrones se llama "de contexto posterior". (Hay algunas combinaciones
de r/s que flex no puede emparejar correctamente. See section Deficiencias / Errores, las
notas a cerca del "contexto posterior peligroso".)
`^r'
una r, pero sólo al comienzo de una línea (es decir, justo al comienzo del análisis, o a la
derecha después de que se haya analizado una línea nueva).
`r$'
una r, pero sólo al final de una línea (es decir, justo antes de una línea nueva).
Equivalente a "r/n". Fíjese que la noción de flex de una "línea nueva" es exáctamente lo
que el compilador de C utilizado para compilar flex interprete como `n'; en particular,
en algunos sistemas DOS debe filtrar los r's de la entrada used mismo, o explícitamente
usar r/rn para "r$".
`<s>r'
una r, pero sólo en la condición de arranque s (See section Condiciones de arranque,
para una discusión sobre las condiciones de arranque)
`<s1,s2,s3>r'
lo mismo, pero en cualquiera de las condiciones de arranque s1, s2, o s3
`<*>r'
una r en cualquier condición de arranque, incluso una exclusiva.
`<<EOF>>'
un fin-de-fichero
`<s1,s2><<EOF>>'
un fin-de-fichero en una condición de arranque s1 ó s2
7. Emparejamiento de la entrada
El escáner asocia las cadenas ingresadas que concuerden con los patrones de caracteres
guardados. Si encuentra más de un emparejamiento, toma el que empareje más texto
(para reglas de contexto posterior, se incluye la longitud de la parte posterior, incluso si
se devuelve a la entrada). Si encuentra dos o más emparejamientos de la misma
longitud, se escoge la regla listada en primer lugar en el fichero de entrada de flex.
Una vez que se determina el emparejamiento, el texto correspondiente al
emparejamiento (denominado el token) está disponible en el puntero a caracter global
yytext, y su longitud en la variable global entera yyleng. Entonces la acción
correspondiente al patrón emparejado se ejecuta, y entonces la entrada restante se
analiza para otro emparejamiento.
Si no se encuentra un emparejamiento, entonces se ejecuta la regla por defecto: el
siguiente caracter en la entrada se considera reconocido y se copia a la salida estándar.
Así, la entrada válida más simple de flex es:
%%
Que genera un escáner que simplemente copia su entrada (un caracter a la vez) a la
salida.
Condiciones de arranque
Las condiciones de arranque se declaran en la (primera) sección de definiciones de la
entrada usando líneas sin sangrar comenzando con `%s' ó `%x' seguida por una lista de
nombres. Lo primero declara condiciones de arranque inclusivas, lo último condiciones
de arranque exclusivas. Una condición de arranque se activa utilizando la acción
BEGIN. Hasta que se ejecute la próxima acción BEGIN, las reglas con la condición de
arranque dada estarán activas y las reglas con otras condiciones de arranque estarán
inactivas. Si la condición de arranque es inclusiva, entonces las reglas sin condiciones
de arranque también estarán activas. Si es exclusiva, entonces sólamente las reglas
calificadas con la condición de arranque estarán activas.
Variables disponibles para el usuario
Esta sección resume los diferentes valores disponibles al usuario en las acciones de la
regla.
1. `char *yytext' apunta al texto del token actual. Este puede modificarse pero no
alargarse (no puede añadir caracteres al final). Si aparece la directiva especial
`%array' en la primera sección de la descripción del analizador, entonces yytext
se declara en su lugar como `char yytext[YYLMAX]', donde YYLMAX es la
definicion de una macro que puede redefinir en la primera sección si no le gusta
el valor por defecto (generalmente 8KB). El uso de `%array'produce
analizadores algo más lentos, pero el valor de yytext se vuelve inmune a las
llamadas a `input()' y `unput()', que potencialmente destruyen su valor cuando
yytext es un puntero a caracter. El opuesto de `%array' es `%pointer', que se
encuentra por defecto. Usted no puede utilizar `%array' cuando genera
analizadores como clases de C++ (la bandera `-+').
2. `int yyleng' contiene la longitud del token actual.
8. 3. `FILE *yyin' es el fichero por el que flex lee por defecto. Este podría redefinirse
pero hacerlo solo tiene sentido antes de que el análisis comience o después de
que se haya encontrado un EOF. Cambiándolo en medio del análisis tendrá
resultados inesperados ya que flex utiliza buffers en su entrada; use `yyrestart()'
en su lugar. Una vez que el análisis termina debido a que se ha visto un fin-de-
fichero, puede asignarle a yyin el nuevo fichero de entrada y entonces llamar al
analizador de nuevo para continuar analizando.
4. `void yyrestart( FILE *new_file )' podría ser llamada para que yyin apunte al
nuevo fichero de entrada. El cambio al nuevo fichero es inmediato (cualquier
entrada contenida en el buffer previamente se pierde). Fíjese que llamando a
`yyrestart()' con yyin como argumento de esta manera elimina el buffer de
entradda actual y continúa analizando el mismo fichero de entrada.
5. `FILE *yyout' es el fichero sobre el que se hacen las acciones `ECHO'. Este
puede ser reasignado por el usuario.
6. YY_CURRENT_BUFFER devuelve un handle YY_BUFFER_STATE al buffer
actual.
7. YY_START devuelve un valor entero correspondiente a la condición de
arranque actual. Posteriormente puede usar este valor con BEGIN para retornar a
la condición de arranque.
Compilación y ejecución en flex de un programa.
Al ejecutar el comando flex nombre_fichero_fuente se creará un fichero en C llamado
“lex.yy.c”. Si se compila este fichero con la instrucción “gcc lex.yy.c –lfl –o
nombre_ejecutable” (-lfl indica enlazar con la biblioteca de flex) se obtendrá como
resultado un
fichero ejecutable llamado nombre_ejecutable).
Una vez se ha creado el fichero ejecutable se puede ejecutar directamente. Flex esperará
que se introduzca
texto por la entrada estándar (teclado) y lo analizará cada vez que se pulse el retorno de
carro. Para
terminar con el análisis normalmente hay que pulsar <CTRL>-D. Para poder probarlo
con entradas más
largas, lo más sencillo es crear archivos de texto y usar redirecciones para que el
ejecutable lea estos
ficheros como entrada a analizar. Por ejemplo si hemos creado un analizador llamado
prueba1 y un
fichero de texto con datos para su análisis, llamado entrada.txt, podemos ejecutar
$prueba1 <
entrada.txt.
Si se quiere ejecutar algún código al final de un análisis de Flex (para mostrar resultados
por ejemplo) hay
al menos dos opciones:
%%
reglas...
8
Introducción a Flex y Bison
9. %%
main() {
yylex();
código a ejecutar al final del análisis
}
o bien
%%
reglas...
%%
yywrap() {
código a ejecutar al final del análisis
return 1;
}
Introducción a Bison
Bison es un generador de analizadores sintácticos de tipo LALR(1) se distribuye bajo
licencia GNU y genera automáticamente código fuente C en base de una gramática libre
de contexto.
Símbolos terminales y no terminales
Los símbolos terminales de la gramática se denominan en Bison tokens y deben
declararse en la sección de definiciones. Por convención se suelen escribir los tokens en
mayúsculas y los símbolos no terminales en minúsculas. 10 Introducción a Flex y Bison
Los nombres de los símbolos pueden contener letras, dígitos (no al principio),
subrayados y puntos. Los puntos tienen sentido únicamente en no-terminales. Hay tres
maneras de escribir símbolos terminales en la gramática. Aquí se describen las dos más
usuales:
Un token declarado se escribe con un identificador, de la misma manera que un
identificador en C. Por convención, debería estar todo en mayúsculas. Cada uno
de estos nombres debe definirse con una declaración de %token.
Un token de carácter se escribe en la gramática utilizando la misma sintaxis
usada en C para las constantes de un carácter; por ejemplo, ‘+’ es un tipo de
token de carácter. Un tipo de token de carácter no necesita ser declarado a
menos que necesite especificar el tipo de datos de su valor semántico,
asociatividad, o precedencia. Por convención, un token de carácter se utiliza
únicamente para representar un token consistente en ese carácter en particular.
10. Sintaxis de las reglas gramaticales (producciones)
Una regla gramatical de Bison tiene la siguiente forma general: resultado:
componentes... ; donde resultado es el símbolo no terminal que describe esta regla y
componentes son los diversos símbolos terminales y no terminales que están reunidos
por esta regla. Por ejemplo, exp: exp ‘+’ exp ; dice que dos agrupaciones de tipo exp,
con un token ‘+’ en medio, puede combinarse en una agrupación mayor de tipo exp. Los
espacios en blanco en las reglas son significativos únicamente para separar símbolos.
Puede añadir tantos espacios en blanco extra como desee. Distribuidas en medio de los
componentes pueden haber acciones que determinan la semántica de la regla. Una
acción tiene el siguiente aspecto: {sentencias en C} Normalmente hay una única acción
que sigue a los componentes. Se pueden escribir por separado varias reglas para el
mismo resultado o pueden unirse con el caracter de barra vertical ‘|’ así: resultado:
componentes-regla1... | componentes-regla2... ... ; Estas aún se consideran reglas
distintas incluso cuando se unen de esa manera. Si los componentes en una regla están
vacíos, significa que resultado puede concordar con la cadena vacía (en notación formal
sería ε). Por ejemplo, aquí aparece cómo definir una secuencia separada por comas de
cero o más agrupaciones exp: expseq: /* vacío */ | expseq1 ;
Declaraciones en Bison
La sección de declaraciones de Bison de una gramática de Bison define los símbolos
utilizados en la formulación de la gramática y los tipos de datos de los valores
semánticos. Todos los nombres de tokens (pero no los tokens de carácter literal simple
tal como ‘+’ y ‘*’) se deben declarar. Los símbolos no terminales deben ser declarados
si necesita especificar el tipo de dato a utilizar para los valores semánticos.
Precedencia de operadores
Use las declaraciones %left, %right o %nonassoc para declarar un token y especificar su
precedencia y asociatividad, todo a la vez. Estas se llaman declaraciones de
precedencia.
La sintaxis de una declaración de precedencia es la misma que la de %token: bien
%left símbolos... o
%left símbolos...
Y realmente cualquiera de estas declaraciones sirve para los mismos propósitos que
%token. Pero además, estos especifican la asociatividad y precedencia relativa para
todos los símbolos:
• La asociatividad de un operador op determina cómo se anidan los usos de un operador
repetido: si ‘x op y op z’ se analiza agrupando x con y primero o agrupando y con z
primero. %left especifica asociatividad por la izquierda (agrupando x con y primero) y
%right especifica asociatividad por la derecha (agrupando y con z primero). %nonassoc
especifica no asociatividad, que significa que ‘x op y op z’ se considera como un error
de sintaxis.
• La precedencia de un operador determina cómo se anida con otros operadores. Todos
los tokens declarados en una sola declaración de precedencia tienen la misma
precedencia y se anidan conjuntamente de acuerdo a su asociatividad. Cuando dos
tokens declarados asocian declaraciones de diferente precedencia, la última en ser
declarada tiene la mayor precedencia y es agrupada en primer lugar
11. Funcionamiento del analizador
La fuente de Bison se convierte en una función en C llamada yyparse. Aquí describimos
las convenciones de interfaz de yyparse y las otras funciones que éste necesita usar.
Tenga en cuenta que el analizador utiliza muchos identificadores en C comenzando con
‘yy’ e ‘YY’ para propósito interno. Si utiliza tales identificadores (a parte de aquellos
descritos en el manual) en una acción o en código C adicional en el archivo de la
gramática, es probable que se encuentre con problemas. La Función del Analizador
yyparse:
Se llama a la función yyparse para hacer que el análisis comience. Esta función lee
tokens, ejecuta acciones, y por último retorna cuando se encuentre con el final del
fichero o un error de sintaxis del que no puede recuperarse. Usted puede también
escribir acciones que ordenen a yyparse retornar inmediatamente sin leer más allá. El
valor devuelto por yyparse es 0 si el análisis tuvo éxito (el retorno se debe al final del
fichero). El valor es 1 si el análisis falló (el retorno es debido a un error de sintaxis).
La Funcion del Analizador Léxico yylex:
La función del analizador léxico, yylex, reconoce tokens desde el flujo de entrada y se
los devuelve al analizador. Bison no crea esta función automáticamente; usted debe
escribirla de manera que yyparse pueda llamarla. En programas simples, yylex se define
a menudo al final del archivo de la gramática de Bison. En programas un poco más
complejos, lo habitual es crear un programa en Flex que genere automáticamente esta
función y enlazar Flex y Bison.
2 Ejemplos de la creación de un compilador utilizando Flex y Bison.
Calculadora
12.
13.
14.
15.
16. Ejemplo 2
Ante la siguiente entrada a =12+2*cos(3.14) ; La salida debe ser: El valor del
identificador a es 10.
Fichero léxico_solo.l
%{
/* Ejemplo para una pequeña calculadora que permite
trabajar con numeros enteros y reales con las operaciones
básicas de suma, resta, producto, division y trigonometricas como el seno y el coseno */
#include <stdio.h>
#include <stdlib.h>
int nlines=0;
%}
DIGITO [0-9]
ID [a-zA-Z][a-zA-Z0-9_]*
%%
{DIGITO}+ {printf("Encontrado TKN_NUM_ENTERO: %d",atoi(yytext));}
{DIGITO}+"."{DIGITO}+ {printf("Encontrado TKN_NUM_REAL:
%f",atof(yytext));}
"=" {printf("Encontrado TKN_ASIGN: %s",yytext);}
";" {printf("Encontrado TKN_PTOCOMA: %s",yytext);}
"*" {printf("Encontrado TKN_MULT: %s",yytext);}
17. "/" {printf("Encontrado TKN_DIV: %s",yytext);}
"+" {printf("Encontrado TKN_MAS: %s",yytext);}
"-" {printf("Encontrado TKN_MENOS: %s",yytext);}
"(" {printf("Encontrado TKN_PAA: %s",yytext);}
")" {printf("Encontrado TKN_PAC: %s",yytext);}
"cos" {printf("Encontrado TKN_COS: %s",yytext);}
"sen" {printf("Encontrado TKN_SEN: %s",yytext);}
{ID} {printf("Encontrado TKN_ID: %s",yytext);}
"n" {nlines++;}
%%
void main(int argc,char **argv)
{
if (argc>1)
yyin=fopen(argv[1],"rt");
else
yyin=stdin;
yylex();
printf("nNumero lineas analizadas: %dn", nlines);
}
/* para compilar
flex lexico.l
cc lex.yy.c -o milex -lfl -lm
*/
Fichero léxico.l (versión a enlazar con Bison)
%{
/* Ejemplo para una pequeña calculadora que permite trabajar
con las operaciones básicas de suma, resta, producto, division y
trigonometricas como el seno y el coseno */
#include <stdio.h>
19. return(TKN_SEN);}
{ID} {//printf("Encontrado TKN_ID: %sn",yytext);
return(TKN_ID);}
"n" {nlines++;}
.
%%
/********
Para el lexico solo
void main(int argc,char **argv)
{
if (argc>1)
yyin=fopen(argv[1],"rt");
else
yyin=stdin;
yylex();
printf("nNumero lineas analizadas: %dn", nlines);
}
*******/
/* para compilar
flex lexico.l
cc lex.yy.c -o milex -lfl -lm
*/
Fichero sintactico.y (Bison)
%{
/* Ejemplo para una pequeña calculadora que permite trabajar
con numeros enteros y reales con las operaciones básicas de
suma, resta, producto, division y trigonometricas como el seno y el coseno */
#include <stdio.h>
#include <stdlib.h>