Sesión 10. Calculadora Científica Programable. Etapa 1

Download Report

Transcript Sesión 10. Calculadora Científica Programable. Etapa 1

M en C Gabriel Castillo Hernández, Instituto de Ingeniería, UNAM. [email protected]
Desarrollo de una calculadora
científica programable
Etapa 1: operación básica
Utilización de Yacc
¿Cómo se construye un lenguaje de
programación?
1
M en C Gabriel Castillo Hernández, Instituto de Ingeniería, UNAM. [email protected]
Yacc
Yacc es un generador de analizadores sintácticos, es
decir es un programa que genera otro programa
(escrito en C) capaz de analizar sintácticamente
oraciones escritas con base en las especificaciones
gramaticales de un lenguaje. En este sentido, se
puede pensar en Yacc como un preprocesador.
Además Yacc provee un medio de asociar
significados a los componentes de la gramática, de
tal manera que al mismo tiempo que el análisis
sintáctico tiene lugar, se “evalúa” su significado.
¿Cómo se construye un lenguaje de
programación?
2
M en C Gabriel Castillo Hernández, Instituto de Ingeniería, UNAM. [email protected]
Regla
Regla
Regla
Regla
Regla
Regla
Regla
1. list: expr \n
resultado = expr
2. expr:NUMERO
resultado = valor del token
3. expr: expr + expr
resultado = resultado_1 – resultado_2
4. expr:expr - expr
resultado = resultado_1 – resultado_2
5. expr:expr * exprresultado = resultado_1 * resultado_2
6. expr:expr * exprresultado = resultado_1 / resultado_2
7. expr:( expr )
resultado = resultado_1
Analizador lexico
(Generador de Tokens)
En lenguaje C
YACC
Programa en C
¿Cómo se construye un lenguaje de
programación?
3
M en C Gabriel Castillo Hernández, Instituto de Ingeniería, UNAM. [email protected]
Etapas de Yacc
Primero. Se escribe una gramática, esto especifica la sintaxis del lenguaje. A este
nivel Yacc puede utilizarse para evitar errores y ambigüedades gramaticales.
Segundo. Cada regla producción de la gramática puede ser aumentada con una
acción , es decir una “frase” sobre lo que debe hacerse cuando se encuentra
en un programa que se debe aplicar la regla de producción. La parte de “qué
se debe hacer” se escribe en C. Esto define la semántica del lenguaje
Tercero. Se necesita un analizador lexicográfico o scanner, que leerá la entrada
que se desea analizar sintácticamente y la dividirá en tokens. NUMBER es un
ejemplo de un token. Los tokens formados por un solo carácter, como + - * /
también se consideran tokens y no requiere que se le asigne algún nombre.
Finalmente. Se requiere una rutina de control para llamar al analizador sintáctico
que se construyó con Yacc
¿Cómo se construye un lenguaje de
programación?
4
M en C Gabriel Castillo Hernández, Instituto de Ingeniería, UNAM. [email protected]
Yacc procesará la gramática y las acciones
semánticas en una función sintáctica
llamada yyparse, y la escribe como un
archivo de código en C.
Si Yacc no encuentra errores, el analizador
sintáctico (yyparse) , el analizador
lexicográfico (yylex) y la rutina de control
(main) pueden ser compilados y agregados
a una aplicación.
¿Cómo se construye un lenguaje de
programación?
5
M en C Gabriel Castillo Hernández, Instituto de Ingeniería, UNAM. [email protected]
Analizador Lexicográfico (yylex)
Tokens
Analizador sintáctico (yyparse)
Rutina de Control
Nota: Todos los nombres utilizados en Yacc comienzan con ´y´
¿Cómo se construye un lenguaje de
programación?
6
M en C Gabriel Castillo Hernández, Instituto de Ingeniería, UNAM. [email protected]
Estructura general de un archivo Yacc
%{
Proposiciones de C tales como #include, declaraciones, etc. Esta sección es opcional.
%}
%%
Declaraciones de yacc: componentes lexicográficos, variables gramaticales, información sobre precedencia y
asociatividad.
Reglas y acciones gramaticales
%%
Más proposiciones en C (opcional)
main()
{
}
algunas instrucciones de control del programa (apertura de archivos) verificación de parámetros, etc. En algún punto
se llamará al yyparse()
yylex()
{
}
analizador lexicográfico, separa la entrada en tokens y sú valor
¿Cómo se construye un lenguaje de
programación?
7
M en C Gabriel Castillo Hernández, Instituto de Ingeniería, UNAM. [email protected]
Analizado léxico etapa 1
El analizador léxico debe leer la entrada y separarla en tokens. Si es un número el token a
regresar en NUMBER. Cualquier otro caracter debe ser regresado como token y como
valor mismo.
Por ejemplo: 123.3 * ( 3+2):
Token
Valor
NUMBER 123.3
*
(
NUMBER 3
+
NUMBER 2
)
La variable yylval se utiliza para comunicación entre el analizador lexico y el sintáctico. Su
definición la generará Yacc y la utilizaremos para indicar el valor del token (si es que lo
hay)
¿Cómo se construye un lenguaje de
programación?
8
M en C Gabriel Castillo Hernández, Instituto de Ingeniería, UNAM. [email protected]
Analizador léxico utilizado en la etapa 1 de la calculadora
int yylex(void)
{
int c;
do {
c=getchar();
}
while ((c == ´ ´)||(c==´\t´));
if (c==EOF)
return 0; // se ha terminado la entrada
if ((c==´.´)||(isdigit(c))){ // se trata de un número...
ungetc(c,stdin);
// regresa el carácter para leer el número completo
scanf("%lf", &yylval); // yylval contiene el valor del número
return NUMBER;
// token número
}
if (c==´\n´)
lineno++;
// lineno lleva una cuenta en qué linea vamos
return c;
// como no fue un NUMBER regresa el valor del carácter
}
¿Cómo se construye un lenguaje de
programación?
9
M en C Gabriel Castillo Hernández, Instituto de Ingeniería, UNAM. [email protected]
Manejo básico de errores (yyerror)
Si existiese algún error de sintaxis, yyparse (el
analizador sintáctico) llama a yyerror. Esta rutina
puede ser proporcionada por el usuario de manera
que el manejo de errores sea adecuado.
¿Cómo se construye un lenguaje de
programación?
10
M en C Gabriel Castillo Hernández, Instituto de Ingeniería, UNAM. [email protected]
yyerror (verison 1)
void yyerror(char *s)
{
advertencia(s,NULL);
}
void advertencia(char *s, char *t)
{ printf("%s: %s", progname, s);
if (t!=NULL)
printf("%s",t);
printf("cerca de la línea %d\n", lineno);
}
¿Cómo se construye un lenguaje de
programación?
11
M en C Gabriel Castillo Hernández, Instituto de Ingeniería, UNAM. [email protected]
void main(int argc, char *argv[])
Versión 1
char *progname;
int lineno = 1;
void main(int argc, char argv[])
{
progname = argv[0]; // nombre del programa
yyparse();
// llama al analizador léxico;
}
¿Cómo se construye un lenguaje de
programación?
12
M en C Gabriel Castillo Hernández, Instituto de Ingeniería, UNAM. [email protected]
La pila en Yacc
Los valores de las reglas que no han podido ser completamente evaluadas se guardan en un stack (pila) y
se emplea para pasar de una regla a otra los valores.
El tipo de información que se guarda en la pila es por default entero (int).
El tipo de valores que procesará nuestra calculadora es de punto flotante de doble precisión (double) por
lo que se debe indicar que la pila almacenará valores double. Esto se consigue al declarar en la
primera sección del archivo yacc:
%{
// includes...
#include <malloc.h>
#include <stdio.h>
#include <ctype.h>
// prototipos...
int yylex(void);
void warning (char *s, char *t);
void yyerror (char *s);
// declaraciones generales...
#define YYSTYPE double /* tipo de datos de la pila de evaluación de yacc */
%}
¿Cómo se construye un lenguaje de
programación?
13
M en C Gabriel Castillo Hernández, Instituto de Ingeniería, UNAM. [email protected]
Tokens, asociatividad y precedencia
Los tokens que reconocerá Yacc deben ser declarados a
menos que sean literales de un solo carácter, tal como ´+´
´-´. Esto se consigue mediante la declaración %token
Cuano del token debe establecer una precedencia, la
asociatividad izquierda o derecha se especifica, si procede,
usando %left o %right en lugar de %token
La precedencia de operadores se determina por el órden de
aparición: los componentes en la misma declaración tiene
el mismo nivel de precedencia. Los declarados después
tienen precedencia más alta.
¿Cómo se construye un lenguaje de
programación?
14
M en C Gabriel Castillo Hernández, Instituto de Ingeniería, UNAM. [email protected]
// tokens y precedencia de operadores
%token NUMBER /* definición del token NUMBER
%left ´+´ ´-´ /* asociatividad a la izquierda con la misma precedencia */
%left ´*´ ´/´ /* asociatividad a la izquierda con la misma precedencia */
¿Cómo se construye un lenguaje de
programación?
15
M en C Gabriel Castillo Hernández, Instituto de Ingeniería, UNAM. [email protected]
Escritura de reglas y acciones en Yacc
Diferentes versiones de la misma regla (por ejemplo expr) se separa con |
(el símbolo pipe de unix).
Cualquier regla gramatical puede tener asociada una acción, la cual se
ejecutará cuando la regla sea aplicada.
Una acción es una secuencia de frases en C encerradas entre llaves { }
Dentro de una acción, $n (es decir, $1, $2, $3, etc) se refiere al valor
devuelto por el n-ésimo componente de la regla, y $$ es el valor
devuelto correspondiente a toda la regla.
En particular $$ = $1 puede ser omitido, pues siempre $$ es igual a $1 a
menos que se indique lo contrario.
¿Cómo se construye un lenguaje de
programación?
16
M en C Gabriel Castillo Hernández, Instituto de Ingeniería, UNAM. [email protected]
%%
/* Gramática de la versión 1 de la calculadora */
list: /* nada */
| list ´\n´
| list expr ´\n´
expr:
NUMBER
| expr ´+´ expr
| expr ´-´expr
| expr ´*´expr
| expr ´/´expr
| ´(´ expr ´)´
// fin de la gramática
%%
{
{
{
{
{
{
{
printf(”\t>>%.8g\n”, $2); }
$$ = $1; }
$$ = $1 + $3; }
$$ = $1 + $3; }
$$ = $1 * $3; }
$$ = $1 / $3; }
$$ = $2; }
¿Cómo se construye un lenguaje de
programación?
17