UNlVERSlDAD AUTóNOMA METROPOLITANA148.206.53.84/tesiuami/UAM3399.pdf · conocimiento experto en un...

63
UNlVERSlDAD AUTóNOMA METROPOLITANA UNIDAD tZTAPALAFA PROYECTO TERMINAL DE LA LICENCIATURA EN COMPUTACI6N SISTEMA DE AWfSTCI6M DE CONOCIMIENTOS PARA SISTEMAS EXPERTOS MANUAL T€CMICO ASESORES: ELIZABETH P€REZ CORTES JOHNGODDARD RENE MACKINNEY ALUMNOS: BEJARANO LUNA JOS€ MIGUEL CARRE@ MCNDEZ H€CTOR HORACIO RLVERA CANLPUZANO JACQUELINE

Transcript of UNlVERSlDAD AUTóNOMA METROPOLITANA148.206.53.84/tesiuami/UAM3399.pdf · conocimiento experto en un...

U N l V E R S l D A D A U T ó N O M A M E T R O P O L I T A N A

U N I D A D t Z T A P A L A F A

PROYECTO TERMINAL DE LA LICENCIATURA EN COMPUTACI6N

SISTEMA DE AWfSTCI6M DE CONOCIMIENTOS PARA SISTEMAS EXPERTOS

M A N U A L T € C M I C O

ASESORES: ELIZABETH P€REZ CORTES

JOHNGODDARD

RENE MACKINNEY

ALUMNOS: BEJARANO LUNA JOS€ MIGUEL

CARRE@ MCNDEZ H€CTOR HORACIO

RLVERA CANLPUZANO JACQUELINE

P&S

INTRODUCC16N ..................................................................................... i

1 . SISTEMAS EXPERTOS ......................................................................... 3

I I . SHELL ............................................................................................... 6

Ill . SISTEMA DE ADQUlSlCldN DE CONOCIMIENTO ................................... 8

CONCLUSIONES .................................................................................... 56

APENDICE A LEX .................................................................................. 58

APiNDlCE B YACC ................................................................................ 60

BILBIOGRAF~A ........................................................................................ 62

Un Sistema Experto es un programa que procede como un experto para algunos problemas de algún drea de dominio. Un Sistema Experto tiene tres componentes principales:

(1 1 Una base de conocimiento,

(2) Una mtlquina de inferencia,

(3) Una interface de usuario.

Un Sistema de Adquisicidn de Conocimientos (SAC) es una interfaz entre el usuario y la base de conocimiento. En este manual se explica en que consiste el Sistema de Adquisicidn de Conocimientos hecho por nosotros, y que herramientas utilizamos en su eíaboracidn. Hay varias formas de representar el conocimiento acerca de algun drea del saber. Nos parecid mas adecuado elegir una gramatica en Lenguaje Natural para representar el conocimiento, porque asf es fdcil para el usuario introducirlo en la base de conocimiento. Los Sistemas Expertos son usados en aplicaciones de diagn6stico medico, localizaci6n de fallas en equipo mecdnico, mMico, etc., para interpretar la medicidn de datos, para ayudar a tomar decisiones de planeacidn financiera, subscribir polizas de seguros, y tambien llevan a cabo otros servicios que requieren de un experto humano. El Sistema de Adquisici6n de Conocimientos es útil para un experto que lo utilice para atmacenar informaci6n de a l g h &ea del conocimiento, pues le permite introducir la informacidn a la base de conocimiento de una manera natural, gracias a una gramdtica establecida. Una vez que el experto introduce la informacidn de acuerdo a la gramdtica establecida; Bsta se transforma a una manera reconocible por la mtlquina de inferencia de Prolog y se guarda en la base de conocimiento. Este manual esta dirigido a aquellas personas que tengan nociones de Sistemas Expertos, lenguaje de programacion C y Prolog. Para aquellas personas que quieran tener un panorama mtls amplio de lo que realiza un Sistema Experto pueden referirse a la bilbiografía presentada al final de este manual. A continuacidn mencionamos los objetivos que se pretenden en este proyecto.

Objetivo General:

Construir una herramienta con la cual se le permita al usuario introducir informacidn a la base de conocimiento, por medio de Lenguaje Natural.

Objetivos Específicos:

- Construir una herramienta para adquisicibn de conocimiento que sea compatible con la msquina de inferencia de Prolog.

- Interpretar el conocimiento dado por el usuario y transformarlo a cbdigo en lenguaje Prolog.

I . S I S T E M A S E X P E R T O S

Un Sistema Experto es un programa que emula a un experto en algOn dominio de aplicaci6n limitado. Los Sistemas Expertos tienen que ser capaces de resolver problemas que requieren conocimiento experto en un dominio particular, y deben poseer ese conocimiento en alguna forma. Un Sistema Experto tambien tiene que ser capaz, de alguna manera, de explicar su comportamiento y sus decisiones al usuario, como los expertos humanos lo hacen. Dar explicaciones es especialmente necesario en dominios inciertos para aumentar la confianza del usuario en la advertencia del sistema, o para facilitar al usuario detectar posibles defectos en el razonamiento del sistema. Por lo tanto, los sistemas expertos deben tener una favorable capacidad de interacci6n con el usuario; lo que hard el razonamiento del sistema transparente al usuario. Un Sistema Experto, como ya se dijo, tiene tres componentes principales:

(1 1 Una base de conocimiento,

(21 Una msquina de inferencia,

(3) Una interfaz de usuario.

Una base de conocimiento es una representacidn declarativa del experto, a menudo en reglas SI ENTONCES. Comprende el conocimiento que es específico para el dominio de aplicacibn, incluyendo cosas tales como simples hechos, reglas que describen relaciones o fen6menos, y posiblemente tambien metodos y heurísticas para resolver problemas en este dominio. Una mdquina de inferencia es el c6digo en el núcleo del sistema que deriva recomendaciones a partir de la base de conocimiento y datos de un problema específico. Esta sabe como usar activamente el conocimiento en la base. Una interfaz de usuario es el cddigo que controla el didlogo entre el usuario y el sistema. Provee igual comunicaci6n entre el usuario y el sistema, y permite al usuario comprender el proceso de resoluci6n del problema llevado fuera por la mdquina de inferencia. A la mdquina de inferencia junto con la interface de usuario generalmente se le llama shell del sistema experto, o simplemente un shell. A continuacidn se presenta un esquema con la estructura de los Sistemas Expertos:

Base de usuario inferencías conocimientos lnterfaz de "---)- Máquina de

Shell I I

Las funciones típicas que realiza un Sistema Experto son:

- Solucidn de problemas en un drea de conocimiento específico

- Explicaci6n del proceso de soluci6n del problema

Una funci6n adicional que con frecuencia realiza un sistema experto es tratar con incertidumbre e incompletitud. La informaci6n acerca del problema a ser resuelto puede ser incompleta o inconfiable; las relaciones en el dominio del problema pueden ser aproximadas. Por ejemplo, podemos no estar del todo seguros de que algún síntoma este presente en un paciente, o de que algunas mediciones de datos sean absolutamente correctas; alguna droga puede causar algún problema, pero generalmente no. Todo esto requiere razonamiento probabilístico. Muchas veces en problemas de selecci6n estructurada la respuesta final no es conocida con completa seguridad. Las reglas del experto podrían ser vagas, y el usuario podría estar inseguro de las respuestas a las preguntas. Esto puede ser visto fdcilmente en los sistemas de diagndstico medico donde el experto no es capaz de ser preciso acerca de la relacidn entre los síntomas y las enfermedades. De hecho, el medico podría ofrecer múltiples diagn6sticos posibles. Para que los Sistemas Expertos trabajen en el mundo real ademas deben ser capaces de tratar con incertidumbre. Uno de los esquemas mds simples es asociar un valor numerico con cada pieza de informaci6n en el sistema. El valor numerico representa la certeza con la cual la informacidn es conocida. Hay numerosas maneras en las cuales pueden ser definidos esos números, y cbmo ellos son combinados durante el proceso de inferencia. A esos números tambien se les conoce como factores de certidumbre, los cuales son actualizados y mantenidos por la mdquina de inferencia mientras procede la inferencia.

A continuaci6n se presenta un ejemplo : Supongamos que los factores de certidumbre (precedidos por cf) son números enteros.

si las luces estdn bajas entonces la batería est3 mal cf 50

si hay dolor de cuerpo y

entonces hay resfriado cf 90 fiebre alta

Sería natural emplear probabilidades reales en lugar de asignar números a la informacibn, pero entonces surgen problemas por las siguientes razones:

- Los expertos humanos parece que tienen dificultades al pensar en tkrminos de probabilidades reales; sus estimaciones de probabilidad no corresponden del todo a las probabilidades definidas matemdticamente.

- El trato probabiiístico correcto matemdticamente requeriría informaei6n que no est3 disponible o algunas suposiciones de simplificaci6n que no son realmente del todo justificadas en una aplicaci6n prdctica.

El shell es una pieza de software que contiene la interfaz de usuario, un formato para representar el conocimiento en la base de conocimiento, y una mdquina de inferencia. El esquema de la estructura de los Sistemas Expertos muestra al conocimiento separado de los algoritmos que lo utilizan. Esta divisi6n se debe a que la base de conocimiento depende de la aplicaci6n y, el shell es un dominio independiente. As1 una manera racional de desarrollar sistemas expertos para varias aplicaciones consiste en desarrollar un shell que pueda ser usado universalmente, y despues conectar una nueva base de conocimiento para cada aplicaci6n. Por supuesto, todas las bases de conocimiento tendrdn que conformarse al mismo formalismo que es 'entendido' por el shell. La construcci6n de un shell implica decisiones con respecto al formalismo de representaci6n del conocimiento, el mecanismo de inferencia, la facilidad de interacci6n con el usuario y el trato de incertidumbre. En principio, cualquier formalismo 16gico en el cual podamos expresar el conocimiento acerca de algún Area de dominio, puede ser considerado para emplearse en un Sistema Experto. Sin embargo, el lenguaje de las reglas SI ENTONCES, tambidn llamadas reglas de producci6n, es con mucho el formalismo m& popular para representar el conocimiento.

Los shells proveen las siguientes caracterlticas:

1) Razonamiento del manejo de metas o encadenamiento hacia atrds. Es una t6cnica de inferencia que usa reglas SI ENTONCES para dividir repetitivamente una meta en submetas mds pequeñas que son mAs fAciles de encontrar.

2) Trato con incertidumbre. Es la habilidad del sistema para razonar con reglas y datos que no son precisamente conocidos.

3) Razonamiento del manejo de datos o encadenamiento hacia adelante. Es una t6cnica de inferencia que usa reglas SI ENTONCES para deducir la soluci6n de un problema de datos iniciales.

4) Representaci6n de datos. Es la manera en la cual los datos del problema específico en el sistema son almacenados y accesados.

5) Interface de usuario. ES aquella porcibn de c6digo que crea una facilidad para usar el sistema.

6) Explicaciones. Es la habilidad del sistema para explicar el proceso de razonamiento que us6 para llegar a una recomendací6n.

Prolog tiene una mdquina de inferencia construida con encadenamiento hacia atras que puede ser usada para implantar algunos sistemas expertos. Las reglas de Prolog son usadas para la representaci6n del conocimiento, y la maquina de inferencia de Prolog es usada para derivar conclusiones. Otras porciones del sistema, tales como la interfaz de usuario, debe ser codificada usando a Prolog como lenguaje de prQgramaCi6n. La maquina de inferencia de Prolog hace encadenamiento hacia atras. Cada regla tiene una meta y un número de submetas. La maquina de inferencia de Prolog prueba o desaprueba cada meta. No hay incertidumbre asociada con to$ resultados. Esta estructura de reglas y estrategia de inferencia es adecuada para muchas aplicaciones de sistemas expertos. Solamente el dialogo con el usuario necesita ser mejorado para crear un sistema experto simple.

111. SISTEMA DE ADQUiSlClON DE CONOCiMiENTOS

El intervalo semdntico es la diferencia entre la representacisn natural de algún conocimiento y la representacidn programada del mismo. Escribir el conocimiento del experto en el formato declarativo de reglas que reconoce Prolog puede ser una tarea difícil y tediosa. El objetivo de SAC es reducir el intervalo semdntico. El Sistema de Adquisicidn de Conocimientos (SAC) es una herramienta útil para un Sistema Experto, pues permite que un experto en algún drea del conocimiento introduzca informacidn de esa drea en la base de conocimiento, mediante Lenguaje Natural, de manera que el experto ya no tendrd que introducir esa informaci6n directamente con el formalismo reconocido por la mbquina de inferencia del Sistema Experto, sino que SAC transforma esa informacidn al formalismo.

El esquema de SAC es el siguiente:

Primero, se eligid una forma para interactuar con el sistema y comunicarle el conocimiento, se contaba con las siguientes opciones: - Guiar al experto por medio de menús para que introduzca el conocimiento. Es decir, un submenú del menú principal sería para que el experto introduzca oraciones completas (sujeto y predicado) por medio de opciones que lo lleven a irlas construyendo. Otro submenú sería para que introduzca oraciones simples (sujeto) por medio de opciones como el anterior. Tanto las oraciones simples como las completas serían traducidas a hechos en Prolog. Otro submenú sería para que introduzca reglas de produccidn (de la forma Si ENTONCES) que serían traducidas a reglas en Prolog. Podría haber mds submenús para introducir informacidn estructurada de diferente manera. - Otra opcidn consiste en establecer una gramdtica para que el experto introduzca el conocimiento. Aunque aquí puede haber varias modalidades debido a que se pueden definir diferentes grambticas. Elegimos esta última, porque nos pareci6 que al generar cddigo intermedio de acuerdo a una gram&tica, sería mds fdcil traducir ese cddigo a la forma deseada. Con propdsito de facilidad para el usuario, se eligid una gramdtica en Lenguaje Natural con reglas SI ENTONCES y con una lista de oraciones completas, con las cuales se pudiera interactuar con el usuario y el lenguaje Prolog. Se utilizd la mdquina de inferencia de Prolog, de manera que la informacidn dada por el experto se convierte en lenguaje de Prolog, pero se puede modificar el formato de salida a la base de conocimiento en la forma deseada, según sea el lenguaje del sistema experto que se este realizando. Con esta gramdtica se puede manipular la informacidn transformdndola a hechos y reglas reconocidos por Prolog, pues cada oracibn que se d6 se convierte en un hecho de Prolog y cada regla SI ENTONCES se convierte en una regla de Prolog. Los hechos declaran cosas que siempre son,

incondicionalmente verdaderas, por ejemplo! mujer(lit), padre(tom,lizi. Mediante esta gramatica el experto introduce el conocimiento, el cual debe guardarse en un archivo de tipo texto. A continuacidn se presenta esta graméítica :

<Conocimiento> --> <List-Hecho> fin I < List-tlecho> fin < Reglas> I <Reglas> I <Reglas> < List-Mecho> fin

<Reglas> -- > < Definicion > I < Reglas> < Definicion >

< Definition> --> SI-ENT fin I SI-ENT otro < EspecConsec> < List-Ora-Asig> fin

< SI-ENT> --> si < EspecConsec> < List-Exp-Ora> entonces < EspecConsec> < List-Ora-Asig >

< EspecConsec> --> pregunta I Nulo

< List-Hecho > -- > < List-Ora-Asig >

< List - Exp-Ora> -- > < Exp-Ari-Ora> I < Exp-Ari-Ora > < Texto > I <Texto> I 4 < List-Exp-Ora> 1 I < List-Exp-Ora > , c EspecConsec > < List-Exp-Ora > I < List-Exp-Ora > ; < EspecConsec > < List-Exp-Ora >

< List-Ora-Asig > -- > < Ora-Asig > I < Ora-Asig> <Texto> I <Texto> I ( < List-Ora-Asig > 1 I < List-Ora-Asig > , < EspecConsec > c List-Ora-Asig > I < List-Ora-Asig > ; < EspecConsec > < List-Ora-Asig > I < Definicion >

<Texto> --> t < Mensaje > I

< Mensaje> --> < Palabra> 1 < Mensaje > < Palabra >

c Palabra> -- > o 1 Y / # I ; I -> I + I - 1 “ I / I

< I > I > = I < = I < > / = I si I entonces / ( 1 1 1 ! I atomo / variable I articulo I verbo I numero / se I le I que I no

< Exp-Ari-Ora > --> < Comparacion > I < Ora-Asig >

< Ora-Asig > --> < Oracion > I < Asignacion >

< Asignacion > -- > < variable> < - < Exp-Ari >

c Exp Ari > --> < Expresion > I - < Exp-Ari> + < Exp-Ari> I < Exp-Ari> - < Exp-Ari> / < Exp-Ari> * < Exp-Ari> I < Exp-Ari> I < Exp-Ari> I - c Exp-Ari> I ( < Exp-Ari> 1

< Expresion> --> <variable> I (Palabra que empieza con mayúscula) < numero> (Reales o enteros)

< List-Sust> "> < L-S > I Nulo

< L-Sust> "> < Sust > I < L-Sust > < Sust >

<Sust> --> <An> <atomo> I < atomo > (palabra con minúsculas solamente)

<Art> --> el, la, lo, los, las, un, una, unos, unas

< List-Verbo> --> <Verbo> I (Definidos en un archivo, en tercera persona del singular o plural, presente)

< List-Verbo > y < List-VerboP I < List-Verbo > o < List-Verbo >

<Verbo> --> <verbo> J no <verbo> I le <verbo> f no le e verbo> I se <verbo> I no se <verbo> I que <verbo> I que no < verbo >

Contemplamos muchos aspectos del Lenguaje Natural; sin embargo, existen cantidad de detalles que seguramente no se tomaron en cuenta. La gramtltica no acepta combinaciones de oraciones y comparaciones o asignaciones en una misma frase. No es muy flexible, pues frases que no lleven verbos (el caso de oraciones que s6lO constan de sujeto) no son aceptadas por SAC; por ejemplo: el sol dorado. Aunque no tiene mucho sentido introducir informaci6n de este tipo. Desputls de haber elegido la gramatica, es necesario definir la forma en la que se va a transformar el conocimiento introducido por el experto para guardarlo en la base de conocimiento. Optamos por trabajar con la grametica en lenguaje C, debido a que se podkn utilizar los paquetes Lex y Yacc para manipularla. Toda la implantaci6n de SAC fue en lenguaje C. Et paquete Lex se utilizd para analizar lexicograficamente la informaci6n contenida en el archivo de tipo texto que contiene el conocimiento del, experto. A continuaci6n se presenta el archivo de especificaci6n para Lex :

%{

f

1

int yywrap (void)

return( 1 1;

int EsVerbo (char *Cadena) {

char VerboCon[ 1 61; int i = O, p = O, SiEsVerbo = FALSO;

while ( (i < CantVerbos) && (SiEsVerbo = = FALSO) { strcpy(VerboCon,""); if (strcrnp(TablaVerbos[il,Cadena) = = O)

strcat(VerboCon,TablaVerbos[il); strcat(VerboCon,"n"); if (strcmp(VerboCon,Cadena) = = O) {

SiEsVerbo = CIERTO;

SiEsVerbo = CIERTO; while ((p < CantVerbos) && (strcmp(TablaVerbos[pl,Cadena) != 0) )

+ +Pi if (p > = CantVerbos) {

strcpy(TablaVerbos[NurnVerbosl,Cadena); NurnVerbos + = 1; }

1 + +i; 1 if (i > = CantVerbos)

else return(0);

return( 1 1; 1 int EsPalabra (char *Cadena, int "sernaforo) { int i = O, SiEsPal = FALSO;

while ( (i < CantVerbos) && (SiEsPal = = FALSO) ) { if (strcrnp(Tabla[il,Cadena) = = O)

+ +i; SiEsPal = CIERTO;

1 if (i > = CantVerbos)

else { return(0);

*sernaforo = O; return( 1 I ;

1 1

void Ventana (int X1, int Y1, int X2, int y2) { int i;

gotoxy(X 1 ,Y 1 1; printf(" + "); for (i = X1 + I ; i < X2; i + + )

printf("-"); printf(" + "1; for (i = Y1 +l; i < Y2; i+ + )

{ gotoxy(X1,i); printf(" 1 "); gotoxy(X2,i); printf(" " I ;

1 gotoxy(X1 ,Y2);

printf(" + "); for (i = X1 + l ; i < X2; ¡++I

printf("-"); printf(" + "1; window(X1 + 1 ,Y1 + 1 ,X2-1 ,Y2-1); clrscr0; window(l,1,80,25);

1

void Pregunta (char Arreglo[], int *semaforo) { char resp;

Ventana(l7,10,63,16); gotoxy(21,13); printf("L'%s' es un verbolSIN)? ",Arreglo); resp = toupper(getch0); while (resp! = 'S' && resp! = 'N')

if (resp = = 'S') { resp = toupper(getch0);

strcpytTablaVerbos[NumVerbosl,Arreglo); NumVerbos + = 1 ; +semaforo = 1 ;

1 else {

strcpy(Tabla[NumPall,Arreglo); NumPal + = 1 ; *semaforo = O;

1 1 Revisa (char Arreglo[TamVerbol) {

int p = O; char letra; int semaforo;

while (Arreglotp +- 111 = '\O').

letra = Arreglotpl; switch (letra) {

+ +p;

case 'a': if (IEsPalabra(Arreglo,&sernaforo)) Pregunta(Arreglo,&semaforol;

case 'e': if (tEsPalabra(Arreglo,&semaforo)) Pregunta(Arreglo,&semaforo);

case 7': if (IEsPalabra(Arreglo,&sernaforo)) Pregunta(Arreglo,&semaforo);

case 'o': if (!EsPalabratArreglo,&semaforo)) Pregunta(Arreglo,&semaforo);

case 'u': if (!EsPalabralArreglo,&sernaforo)) Pregunta(Arreglo,&semaforo);

break;

break;

break;

break;

break; case 'n': if (IEsPalabra(Arreglo,&semaforo))

Pregunta(Arreglo,&semaforo); break;

1 default: semaforo = O;

if (semaforo) return(1);

else return(0);

1 %}

Tabulador [\tl SaltoLinea [\nl Espacio " "

Minuscula Ia-zl Digito 10-99

Entero { Digito} +

Articulo "el" I "la" I "los" I "las" Variable [A-ZI[A-Za-z0-91* Atomo {Minuscula} + Numero {Entero} I {Entero}".

Corch-lzq "[" Corch-Der "I" PuntC ";" Coma "," Par1 "(" ParD ")" Mayor ">" Menor " < " Mayor1 " > = " Menor1 " < = " IgualA " = " Diferente " < > " Asigna " < -"

%%

si { NumColumna + = 2; return(si1; 1

entonces { NumColumna + = 8; return(entonces); }

return(y1; }

returnlo); }

Y { NumColumna + = 1 ;

O { NumColumna + = 1 ;

corte { NumColumna + = 5;

1 "un" I "una" I "unos" I "unas" I "IO"

"{Entero}

return(corte1; }

return(otro);}

return(fin);}

otro { NumCotumna + = 4;

fin { NumColumna + = 3;

{Corch-lzq} { NumColumna + = 1;

{Corch-Der} { NumColumna + = 1; return(corch-izq); }

{PuntC}

{Coma}

{ Parl}

{Paro}

{MaYor}

{Menor}

{ Mayorl}

{Menorl}

{IgualA}

~.

returrdcorch-der); } { NumCotumna + = 1;

return(puntoycoma); } { NumColumna + = 1;

returnlcoma); } { NumColumna + = 1;

return(par-izq1; } { NumColumna + = 1 ;

returntpar-der); } { NumColurnna + = 1 ;

return(mayor-que); } { NumColumna + = 1;

return(menor-que); } { NumColumna + = 2;

return(mayor-igual); } { NumColumna + = 2;

returntmenor-igual); } { NumColumna + = 1;

return(igua1); } {Diferente}

{Asigna}

{ NumColumna + = 2; return(diferente1; }

{ NumColumna + = 2; returnfasignal; }

no { NumColumna 3. = 2;

se { NumColumna + = 2;

le { NumColumna + = 2;

que { NumColumna + = 3;

pregunta { NumColumna + = 8;

retumbo); }

return(se1; }

return(le1; }

return(que1; }

return(pregunta1; }

{ NumColumna + = 1; return(rnas); 1

n-n { NumColumna + = 1; return(menos1; }

{ NumColumna + = 1; return(por1; }

n/" { NumColumna + = 1 ; return(entre1; }

n*n

{Variable} {

NumColumna + = yyleng; Ultimold = (char *)malloc(strlen(yytext) + 1); strcpy(Ultimold,yytext); return(variable1;

1 {Articulo} { NumColumna + = yyleng;

return(articu1o); } {Atomo} { NumColumna + = yyleng;

if (EsVerbo(yytext1 = = 1 1 { UItimoVerbo = (char *)malIoc(strlen(yytext) + 1); strcpy(UItimoVerbo,yytext); return(verbo1;

else if (strcmp(yytext,"diagnostico") = = O) {

UltimoVerbo = (char *)malloc(strlen(yytext) + 1); strcpy(UltimoVerbo,yytext); return(verbo1;

1 else

if {

1

(Bandera) strcpy(Lista,yytext); if (Revisa(Lista)) {

UltimoVerbo = (char *)malloc(strlen(yytext) + 1 ); strcpy(UltimoVerbo,yytext1; returnberbo);

1 else

return(atomo1;

else

1 return(atom0);

{Numero} { NumColumna + = yyleng;

{SaltoLinea} { + + NumLinea; return(numero1; }

NumColumna = 1; SaltoDeLinea = Prendido;

1 {Tabulador} NumColumna + = 8; {Espacio} NumColumna + = 1 ; %%

El paquete Yacc se utiliz6 para analizar sint&ticamente la informaci6n contenida en ese archivo. Si se desea tener conocimiento de lo que es Lex y Yacc puede referirse al apendice presentado al final de este manual. Con ambos paquetes se realiz6 un compilador, que recibe como entrada el archivo de tipo texto en Lenguaje Natural, y lo transforma en un archivo de tipo texto en lenguaje Prolog el cual constituye una base de conocimiento. Esta transformacidn se realiza de la siguiente manera : Despues de que el experto introduce el conocimiento en un archivo de tipo texto, Bste se analiza lexicograficamente y sint8cticamente. Existe un archivo llamado VERBOS.SAC que contiene una lista de los verbos mas comunes y el cual debe estar disponible cuando se utilize a SAC para compilar el archivo que contiene el conocimiento del experto, esto se debe a que SAC reconoce verbos al compilar ese archivo. En el archivo de especificaci6n

para Lex se encuentra una funci6n llamada EsVerbo que revisa si cada token que se captura se encuentra en una tabla que contiene los verbos del archivo VERBOS.SAC, cuando ese token puede ser un verbo. Hay otra funci6n llamada Revisa que se encarga de preguntarle al usuario si un token es verbo o no, cuando ese token puede ser un verbo y no se encuentra en la tabla de verbos; si la respuesta es afirmativa, el archivo de verbos se actualiza, y si la respuesta es negativa, SAC guarda esa palabra en un archivo llamado PALABRAS.SAC que contiene aquellas palabras que SAC no sabia si eran verbos y que el usuario le indict3 que no lo son. La raz6n de crear este archivo se debe a que esas palabras pueden encontrarse varias veces en el archivo que se este compilando con SAC, y no es factible que SAC est6 preguntando al usuario por esa palabra cada vez que la encuentre, puesto Que si la primera vez le indict5 que no es verbo, las dernas ocasiones ya debe saber que no es para no volver a preguntarle. Por esta raz6n existe una funci6n llamada EsPalabra que se encarga precisamente de aquellas palabras que no se encuentran en VERBOS.SAC y que pueden ser verbos, revisando si existen en el archivo PALABRASSAC; en caso de que la palabra se encuentre, SAC ya no le pregunta al usuario por esa palabra. Tambidn se especifica que tokens deben ser reconocidos; y en la secci6n de reglas se especifica ta acci6n correspondiente a cada expresi6n regular. En seguida se da una explicaci6n sobre el archivo de especificaci6n para Yacc : Con la finalidad de poder cambiar en un momento dado las estructuras en la parte de definiciones, se opt6 por dejar formada la uni6n en base a un struct debido a la facilidad con la que se podrlan agregar campos en caso de necesitarse posteriormente; ya sea para correccidn o para ampliacibn. Para efectos de compilaci6n, no dejamos algunas constantes globales al inicio del programa como se hubiera deseado, para mayor claridad. Lo mismo sucedi6 con las estructuras elegidas. La jerarqula de los operadores se tom6 asi por ser la usual. Por cada fin se realiza, al igual que por cada oraci6n, una serie de funciones relacionadas con las tuplas. Estas funciones se resumen a pasar las tuplas a la lista global llamada ListaProlog, la cual contiene la que sera la salida al archivo del mismo nombre que el que contiene el conocimiento del experto pero con extensi6n ‘.SAC’. Las tuplas se ¡ran formando al terminar de revisar: a) Una oraci6n b) Una asignacibn c) Una comparacibn y por cada oraci6n se forman tantas tuplas como verbos tenga dicha oracibn. Los errores son manejados directamente por Yacc. Se sugiere tambiCtn que si se desea una mayor ayuda al usuario se le dedique un tiempo razonable a los errores pues no se obtuvo una forma eficiente de su manejo por el tipo de gramzltica elegida. Causa varios problemas el identificar el momento en que el compilador ya no acepta la entrada. Debido a que Yacc necesita leer un token adelantado para determinar por cual regla “irse”, se captur6 el último identificador (Variable) en la variable global Ultimold. Por la misma raz6n se hizo uso de las variables UltimoArt y UltimoVerbo. Los No Terminales en la gramdtica que tienen asignado el tipo AlNum se definieron asi pues solamente recibimos el string para representarlo en “Lenguaje Prolog - Lenguaje Natural” para facilitar búsquedas. Es por ello que en cantidad de ocasiones solamente se regresa el valor de un No Terminal y a su vez este lo asigna a un No Terminal superior y asi sucesivamente hasta llegar a formar una o varias tuplas. Por cada salto de linea que se encuentre el lexico pueden suceder dos cosas : a) Si se estaba ubicado dentro de un Texto, se formara una tupla para el rengldn que acaba de pasar, etiquetandolo con el número actual de etiqueta el cual lo contiene la variable EtiTexto; la que se ir4 incrementando conforme se pongan en el archivo de entrada mas Textos de aclaraciones. b) En otro caso solamente se seguir3 revisando la sintaxis del archivo de conocimiento. En cualquiera de los dos casos, se incrementare el número de rengldn y se igualara a uno la variable NumColumna.

Los textos sirven para aclaraciones a una frase o a una actitud que debe tomar el usuario en determinado caso. Estos textos se ¡ran concatenando conforme vaya avanzando Yacc y hasta que se encuentre un salto de línea. Exp-Ari y Comparacion estan definidos como comúnmente se tienen en un compilador para evaluar expresiones. Los verbos se aceptan en singular o en plural pero siempre en tiempo presente de la tercera persona. El archivo VERBOS.SAC contiene los verbos en presente y se le puede agregar a cada uno la letra n para hacerlo plural excepto el verbo es. Para algunos casos se pueden considerar expresiones con verbos conjugados en varios tiempos; si se desea que los verbos no contemplados en el archivo (debido a su conjugacibn) sean considerados como verbos y no como sustantivos o complementos, se debe cambiar la gramatica o bien agrandar el archivo de verbos, empleando tambien la tabla de verbos la cual permite por el momento un maxim0 de 200 verbos. Los verbos se van encolando para salir en el mismo orden como llegaron para ademas tener un "conteo" inmediato de la cantidad de tuplas a formar. No es necesario regresar el valor sobre $$ porque se tiene acceso a ellos por medio de la cola de verbos la cual esta identificada por una variable llamada ColaVerbos. La estructura para las colas, tambien es aprovechada para encolar las demas palabras en el Texto. Así cuando llega un salto de línea, simplemente concatenamos lo que se tenga en la cola. Esta cola de palabras est& guardada en una variable llamada ColaAtom. Los verbos representan el nombre del predicado. Los predicados y los hechos, tienen tres pardmetros : a) Lista de sustantivos b) Lista de complementos c) Texto al que debe dirigirse en caso de estar activo este Tanto la lista de sustantivos como la lista de complementos tienen la misma forma; es por ello que la gramatica las define a las dos como lista de sustantivos. Dentro de la lista se tienen dos posibilidades : a) Se tiene un solo elemento el cual es o un sustantivo (Lista de sustantivos) o bien un complemento (Lista de complementos). b) Se tienen tres elementos los cuales tienen un orden fijo. Este orden es : una nueva lista, una conjuncidn o disyuncidn y una lista mas. Estas listas internas estln a su vez hechas de la misma forma. Esta solucidn se propuso con el objeto de poder realizar búsquedas en una forma binaria como en ABB. La finalidad de ello se puede ver mejor con un ejemplo: el experto introduce como conocimiento :

el sol y la luna tienen forma de esfera a lo lejos

Se forma el predicado: tienen(~~el~soll,y,~la~lunall,~forma~de_esfera~a_lo~lejosl,").

El usuario puede desear saber si la luna tiene forma de esfera, sin importarle la forma del sol. Así que se puede tener de manera sencilla el predicado: tiene([la~lunal,~forma_de_esfera_a~lo~lejosl,"~.

Sin tener limites para la cantidad de sustantivos, aprovechando la recursividad de Prolog para realizar búsquedas de este tipo. (La forma en la que se esta haciendo uso de ello es con uno o dos sustantivos úricamente; en la parte de salida al usuario. Sin embargo, no est6 limitado para realizar búsquedas mas generales.) Existe una funci6n llamada DESANIDA que se encarga de hacer el desanidamiento de la informacidn que da el usuario cuando esta se encuentra anidada. Este desanidamiento se hace sobre la lista LTupla que contiene la informaci6n, insertando nodos en los lugares correspondientes al ir desanidando y posteriormente ordenando los nodos en el lugar correspondiente para que los nodos con el mismo nivel de anidamiento queden

consecutivos. De esta forma se obtiene LTupla desanidada y lista para pasarse a la lista LProlog que contiene la informaci6n traducida a Prolog que se guarda en la base de conocimiento. La salida al archivo del mismo nombre del que contiene el conocimiento del experto pero con extensi6n 'SAC', se realit6 conforme a la petici6n del otro equipo de trabajo del mismo proyecto. Esto se puede modificar al momento de trasladarse la informaci6n de las tuplas a la lista gtobal.

A continuaci6n se presenta el archivo de especificaci6n para Yacc:

#define SI o /* Asignaciones para indicar en que parte del #define ENTONCES 1 /* condicional se este, para la formacidn de tuplas. #define OTRO 2 #define HECHO -1 /* Asignaci6n para indicar que se trata de un hecho. * /

#define FALSO O /* Para la formacibn de tuplas, se necesita saber si #define CIERTO 1 /* una oraci6n, asignacibn o comparacibn; tiene

/* relacionado /*un texto para seguir la secuencia en la búsqueda de /*predicados en la mequina de inferencias.

#define NADA O /* A cada tupla se le asigna en uno de sus #define COMA

objeto de determinar si se trata de un y o un 1 /* campos, un signo de puntuaci6n con el #define PUNTOYCOMA 2 /*

o en Prolog.

#define Y 1 #define O 2

sttuct Subcadena { /* Aprovechando el uso que tiene Yacc de la unibn, char *Subcad; /* se us6 una estructura a pesar de no ser necesario

}; /* por ser un solo campo; sin embargo, en caso de que desee, solamente se agrega un campo y ya estuvo.

typedef struct Subcadena AlfaNum; %I

/* Especificaciones para Yacc */

%union { AlfaNum AINum;

1

%type < AINum> Oracion, List-Sust, L-S, Ora-Asig, Exp-ArjOra, Asignacion, Exp-Ari, Expresion, Comparacion, EspecConsec

%left o /* Para separar sustantivos y verbos dentro de una oraci6n. */ %left y %left coma /* Para separar oraciones. * / %left puntoycoma %right asigna /* Expresiones aritmdticas. */ %left mas menos

%left por entre %nonassoc menor-que mayor-que mayor-igual menor-igual diferente igual

%start Conocimiento I" Simbolo inicial de la gramdtica. " I

%token si entonces par-izq par-der corte atom0 variable articulo verbo numero se pero no otro fin le que corch-itq corch-der pregunta

1" Las especificaciones anteriores no deben causar ningún problema, y en caso contrario referirse al apendice de Yacc presentado al final de este manual para aclaraciones de este tipo. * I

%% I" Gramdtica y acciones semdnticas " I

Conocimiento : List-Hecho fin { I* Con esto se aceptan solamente hechos TrabajaTuplasO; HaceNulaTupla(&LTupla);

1 1 List-Hecho fin { I* Con esto se aceptan hechos seguidos

TrabajaTuplasO; I' de definiciones HaceNulaTupla(&LTupla);

1 Reglas

1 Reglas I* Con esto se aceptan solamente definiciones 1 Reglas I" Con esto se aceptan definiciones List-Hecho fin { /* seguidas de hechos

TrabajaTuplasO; HaceNulaTupla(&LTupla);

1;

Reglas : Definicion { DESANIDA(LTup1a); TrabajaTuplasO; /* TrabajaTuplas transfiere las

tuplas a una lista global, una vez que se form6 un bloque de tuplas. " I

HaceNulaTupla(&LTupla); I* Una vez que se ha agregado el bloque de tuplas en la lista global, se libera la memoria de estas tuplas. * I

while (Pila != NULL)

Push(HECHO,&Pila); Si-Entonces = Pop(&Pila);

1 I Reglas Definicion {

DESANIDA(LTupla1;

HaceNulaTupla(&LTupla); while (Pila ! = NULL)

PushlHECHO,&Pila);

TrabajaTuplasO;

Si-Entonces = Pop(&Pila);

1;

/* Definicion es un no terminal, en el cual se puede tener expresiones de tipo general hasta cierto punto. Las siguientes formas son todas las posibilidades de su uso. * / ........................................

si Las posibilidades $on equivalentes para usarse Oracion [Texto] despues de un otro, igual que despues de un entonces.

Oracion [Texto] entonces

fin

si Condicion

entonces Oracion

fin

si Oracion

entonces Asignacion

fin

"""""""""""""""""*"""""""""""""""""""""

"""""""""_"""""""""""""""""""""""""""""

si

entonces

fin

Condicion

Asignacion

"~""_C"_L"""""~~""""""""""""""""""""""""""

Definicion : SI-ENT fin {

1 otro {

--Internado;

SI-ENT

Push(OTRO,&Pila); } /* Se apilan para desapilar al momento de formar las tuplas. * /

EspecConsec List-Ora-Asig fin {

--Internado; 1 :

SI-ENT : si { Push(SI,&Pila); + -I- Internado;

1 EspecConsec List-Exp-Ora entonces {

Push(ENTONCES,&PilaJ; 1

EspecConsec List-Ora-Asig ;

EspecConsec : pregunta { Indicador = 1: }

1 /* nulo * / { Indicador = O; } ;

List-Hecho : List-Ora-Asig ;

List-Exp-Ora : Exp-Ari-Ora { Si-Entonces = Pop(&Pilal; VerTope(Pila,Si-Entonces,&Anidadol; /* Las especificaciones de variables globales

vienen despu6s. * I UltimoSi = Si-Entonces; FormaTuplas(S 1 .SubCad,FALSO,Anidado);

} /* FormaTuplas realiza por cada oracidn una serie de tuplas. Tantas como verbos haya en la oraci6n. * /

Exp-Ari-Ora Texto { Si-Entonces = Pop(&Pila);

UltimoSi = Si-Entonces; FormaTuplas(S1 .SubCad,CIERTO,Anidado);

VerTope(Pila,Si-Entonces,&Anidado);

1 Texto {

Si-Entonces = Pop(&Pila);

UltimoSi = Si-Entonces; FormaTuplas("",CIERTO,Anidado);

VerTope(Pila,Si-Entonces,&Anidado):

1 par-izq List-Exp-Ora par-der List-Exp-Ora coma {

Push(UltimoSi,&Pila); UltimoTupla-> Conector = COMA:

1 EspecConsec List-Exp-Ora List-Exp-Ora puntoycoma {

Push(UltimoSi,&Pila); UltimoTupla- > Conector = PUNTOYCOMA;

1 EspecConsec List-Exp-Ora ;

List-Ora-Asig : Ora-Asig { Si-Entonces = Pop(&Pila);

UltimoSi = Si-Entonces; VerTope(Pila,Si-Entonces,&Anidado);

FormaTuplas(S1 .SubCad,FALSO,Anidado); 1

I Ora-Asig Texto { Si-Entonces = Pop(&Pila),

VerTope(Pila,Si-Entonces, &Anidado); UttimoSi = Si-Entonces;

ForrnaTuplas(6 1 .SubCad,ClERTO,Anidado); 1

I Texto { Si-Entonces = Pop(&Pila);

UltirnoSi = Si-Entonces; FormaTuplas("n,CIERTO,Anidadol;

VerTope(Pila,Si-Entonces,&Anidadol;

1 I par-irq List-Ora-Asig par-der I List-Ora-Asig coma {

Push(UltirnoSi,&Pila); UitimoTupla- > Conector = COMA;

1 EspecConsec List-Ora-Asig

1 List-Ora-Asig puntoycoma { Push(UltimoSi,&Pila);

UltimoTupla- > Conector = PUNTOYCOMA; 1

EspecConsec List-Ora-Asig

1 Definicion ;

Texto : corch-izq { /* El corchete se requiere para distinguir y "separarlo" de la gramatica. * I

itoa(EtiText0, EtiTextoCad, 1 O); FrenteDeTexto = (char * I rnalloc (1 8); strcpy(FrenteDeTexto,"texto("); strcat(FrenteDeTexto,EtiTextoCad); strcat(FrenteDeTexto,n):- \""I; LineaTexto = Concatena("",""I; SaltoDeLinea = Apagado; Bandera = O;

1 Mensaje corch-der {

Bandera = 1; SaltoDeLinea = Prendido; /" Aclaraciones en

AgregaRenglon(""); I* Separa las reglas de los variables globales */

1; textos en el archivo de salida. * /

Mensaje : Palabra I Mensaje Palabra ;

Palabra : o { AgregaRenglod" o"); }

AgregaRenglon(" y") ; } I Y {

I coma {

I puntoycoma {

1 asigna {

AgregaRenglon(" , " I ; }

AgregaRenglon(" ; " I ; }

AgregaRenglonV' < -"I; } I mas i

AgregaRenglon(" + "1; }

AgregaRenglont" -"I; } I menos {

I por { AgregaRenglont" ""1; }

AgregaRenglon(" /"I; } 1 entre {

I menor-que {

I mayor-que {

1 mayor-igual {

I menor-igual {

I diferente {

I igual {

AgregaRenglon(" "1; }

AgregaRenglon(" > "1; }

AgregaRenglon(" > = "1; }

AgregaRenglont" < = " I ; }

AgregaRenglon(" < > "); }

AgregaRenglon(" = "1; } I si {

I entonces { AgregaRenglont" si"); }

AgregaRenglont" entonces"); }

AgregaRenglon(" ("); }

AgregaRenglon(" I " ) ; }

I Par-izq {

I par-der {

1 corte {

I atomo {

1 variable {

I articulo {

I verbo {

I numero {

AgregaRenglon(" ! " I ; }

AgregaRenglon(Concatena(" ",yytext)); }

AgregaRenglon(Concatena(" ",Ultimold)); }

AgregaRenglon(Concatena(" ",yytext)); }

AgregaRenglon(Concatena(" ",yytext)); }

AgregaRenglon(Concatena(" ",yytext)); } I se {

I le { AgregaRenglont" se"); }

AgregaRenglon(" le"); }

I que {

I no { AgregaRenglon(" que"); }

AgregaRenglon(" no"); 1 ;

Exp-Ari-Ora : Comparacion { $$.Subcad = (char *)malloc(strlen($l .Subcad) + 1); $ $ .Subcad = strcpy($ $ .Subcad, $ 1 .Subcad); Encola( " ", &ColaVerbos);

3 1 Ora-Asig {

$$.Subcad = (char *)maltoc(strlen(S 1 .Subcad) + 1 ) ; SSSubCad = strcpy($S.SubCad,$l .Subcad);

1 ;

Ora-Asig : Oracion { $$.Subcad = (char *)malloc(strlen(Sl .Subcad) + 1 1; $$.Subcad = strcpy($$.SubCad,Sl .Subcad);

1 I Asignacion

$$.Subcad = (char *)malloc(strlen($ 1 .Subcad) + 1 ) ; $$.Subcad = strcpy($$.SubCad,$l .Subcad); Encolal" ",&ColaVerbos);

1;

Asignacion : variable { Encola(Ultimold,&ColaAtom); TamVar = strlen(Ultimold1;

1 asigna Exp-Ari {

$ $ SubCad = (char * )malloc(strlen($4.SubCad) + TamVar + 1 O); $$.Subcad = strcpy($$.SubCad,"asigna("); $$.Subcad = strcat($$.SubCad,DesEncola(&ColaAtom)); $$.Subcad = srrcat(S$.SubCad,","); $$.Subcad = strcat($S.SubCad,S4.SubCad);

1 ;

Comparacion : Exp-Ari menor-que Exp-Ari { $$.Subcad = (char *)malloc(strlen($l .Subcad) t strlen(S3.SubCad) +4);

$$.Subcad = strcpyl$S.SubCad,Sl .Subcad); $$.Subcad = strcat(S$.SubCad," < "1; $$.Subcad = strcat($S.SubCad,S3.SubCad);

1 1 Exp-Ari mayor-que Exp-Ari {

$$.Subcad = (char *)malloc(strlen($l .Subcad) + strlen(S3.SubCad) f 4); $$.Subcad = strcpy($Q.SubCad,Sl .Subcad); $$.Subcad = strcat($$.SubCad," > "1; $$.Subcad = strcat($$.SubCad,$3.SubCad);

1

I Exp-Ari mayor-igual Exp-Ari { $$.Subcad = (char *)malloc(strlen($ 1 .Subcad) + strlen(S3.SubCad) + 5);

$$.Subcad = strcpy(S$.SubCad,Sl .Subcad); $$.Subcad = strcat($S.SubCad," > = " I ; $$.Subcad = strcat(S$.SubCad,S3.SubCad);

1 I Exp-Ari menor-igual Exp-Ari {

$$.Subcad = (char *)malloc(strlen($l .Subcad) +strlen(S3.SubCad) + 5); $$.Subcad = strcpy($$.SubCad,Sl .Subcad); $$.Subcad = strcat($$.SubCad,' < = "1; $$.Subcad = strcat($$.SubCad,$3.SubCad);

1 I Exp-Ari diferente Exp-Ari {

$$.Subcad = (char *)malloc(strlen($l .Subcad) +strlen($3.SubCad) + 5); $$.Subcad = strcpy($$.SubCad,$l .Subcad); $$.Subcad = strcat($$.SubCad," < > "1; $$.Subcad = strcat($$.SubCad,$3.SubCad);

1 I Exp-Ari igual Exp-Ari {

$$.Subcad = (char *)malloc(strlen($ 1 .Subcad) + strlen(S3.SubCad) +4); $$.Subcad = strcpy($$.SubCad,$l .Subcad); $$.Subcad = strcat(SS.SubCad," = "1; $$.Subcad = strcat($$.SubCad,$3.SubCad);

1 ;

Exp-Ari : Expresion { $$.Subcad = (char *)malloc(strlen(S 1 .Subcad) + 1); $$.Subcad = strcpy(SS.SubCad,Sl .Subcad);

1 1 Exp-Ari mas Exp-Ari { $$.Subcad = (char *)malloc(strlen($ 1 .Subcad) + strlen(S3.SubCad) + 2);

$$.Subcad = strcpy(S$.SubCad,Sl .Subcad); $$.Subcad = strcat(SS.SubCad," + "1; $$.Subcad = strcat(SS.SubCad,S3.SubCad);

1 I Exp-Ari menos Exp-Ari { $$.Subcad = (char *)malloc(strlen(S 1 .Subcad) + strlen(S3.SubCad) + 21;

$$.Subcad = strcpy(SS.SubCad,Sl .Subcad); $$.Subcad = strcat(SS.SubCad,"-"1; $$.Subcad = strcat(SS.SubCad,S3.SubCad);

1 1 Exp-Ari por Exp-Ari { $$.Subcad = (char *)malloc(strlen(Sl .Subcad) +strlen($3.SubCad) +2);

$$.Subcad = strcpy(SS.SubCad,Sl .Subcad); $$.Subcad = strcat(SS.SubCad,"*"); $$.Subcad = strcat(SS.SubCad,S3.SubCad);

1 I Exp-Ari entre Exp-Ari { $$.Subcad = (char *)malloc(strlen(Sl .Subcad) +strlen($3.SubCad) + 2);

$$.Subcad = strcpy(SS.SubCad,Sl .Subcad); $$.Subcad = strcat($$.SubCad,"/"); $$.Subcad = strcat($S.SubCad,$3.SubCad);

1

%prec por { $ $ . SubCad $$.Subcad $ $ .Subcad

1 Exp-Ari par-der {

$ $ . SubCad $ $ . SubCad $ $ . SubCad $ $ .Subcad

1;

(char *)malloc(strlen($2.SubCad) + 2); strcpy($$.SubCad,"-"1; strcat($$.SubCad,$2.SubCad);

(char *)malloc(strlen($2.SubCad) + 3); strcpy($$.SubCad,"("); strcat($$.SubCad,$2.SubCad); strcpy($$.SubCad,")");

Expresion : variable { $$.Subcad = (char *)malloc(strlen(Ultimold) + 1); $$.Subcad = strcpy($$.SubCad,Ultimold);

1 1 numero {

$$.Subcad = (char *)malloc(strlen(yytext)+ 1 I ; $$.Subcad = strcpy($$.SubCad,yytext);

1;

Oracion : List-Sust List-Verbo { Bandera = O; } List-Sust {

Bandera = 1;

$$.Subcad = strcpy($$.SubCad,""); $$.Subcad = strcat($$.SubCad,"("); $$.Subcad = strcat(SS.SubCad,Sl .Subcad); $$.Subcad = strcat($$.SubCad,","); $$.Subcad = strcat($$.SubCad,$4.SubCad);

$$.Subcad = (char *)malloc(strlen($ 1 .Subcad) + strlen($4.SubCad)+ 4);

1 I corte {

$$.Subcad= (char *)malloc(2);

$$.SubCad=strcat($$.SubCad,"!"); $$.Subcad = strcpy($$.SubCad,"");

1 ;

List-Sust : L-S { $$.Subcad = (char *)malloc(strlen($l .Subcad) + 1); strcpy(S$.SubCad,""); strcat($$.SubCad,$l .Subcad);

1 1 /* nulo "/ {

$$.Subcad = (char *)malloc(3);

$$.Subcad = strcat($$.SubCad,"[l"); $$.Subcad = strcpy($$.SubCad,"");

1;

L - S : L-Sust { $$.Subcad = (char *)malloc(21; strcpy($S.SubCad,"t"); while (ColaAtom.lnicio ! = NULL) {

$$.Subcad = Concatena($$.SubCad,DesEncola(&ColaAtom)l; if (ColaAtom.lnicio ! = NULL)

$$.Subcad = Concatena(SS.SubCad,"-"I; 1 $$.Subcad = Concatena($$.SubCad,"l");

1 I L-S Y L-S { $$.Subcad = (char *)malloc(strlen($l .Subcad) +strlen($3.SubCad) +6);

$$.Subcad = strcpy(SS.SubCad,""); $$.Subcad = strcat(SS.SubCad,"["); $$.Subcad = strcat($S.SubCad,Sl .Subcad); $$.Subcad = strcat(SS.SubCad,",y,"); $$.Subcad = strcat($S.SubCad,$3.SubCad); $$.Subcad = strcat(S$.SubCad,"l");

1 I L-S 0 L-S { $$.Subcad = (char *)malloc(strlen(S 1 .Subcad) + strlen(S3.SubCad) + 6);

$ .Subcad = $$.Subcad = $ $ .Subcad = $$.Subcad =

$ .Subcad = $$.Subcad =

1

strcpy($$.SubCad,""); strcat($$.SubCad,"["); strcat($$.SubCad,$l .Subcad); strcat(SS.SubCad,",o,"); strcat($$.SubCad,S3.SubCad); strcat(S$.SubCad,"l");

I par-izq L-S par-der { $$.Subcad =

$ .Subcad = $ $. SubCad =

1;

L-Sust : Sust 1 L-Sust Sust ;

Sust : articulo

atomo {

I atomo {

(char *)malloc(strlen($2.SubCad) + 1 I ; strcpy($$.SubCad,""); strcat(SS.SubCad,S2.SubCad);

Encola(Concatena(UltimoArt,yytext),&ColaAtom); }

Encola(yytext,&ColaAtom); } ;

List-Verbo : Verbo 1 Verbo y {

List-Verbo EncolaEntero(Y,&ColaUnionDeVerbos); }

I Verbo o { EncolaEntero(O,&ColaUnionDeVerbosl;

List-Verbo ;

Verbo : verbo { Encola(UltimoVerbo,&ColaVerbos); }

1 no verbo {

I le verbo {

1 no le verbo {

I se verbo {

\ que verbo {

1 que no verbo {

1 no se verbo {

Encola(Concatena("no_",UltimoVerbo),&ColaVerbos); }

Encola(Concatena("le_",UltimoVerbo),&ColaVerbos); 1

Encola(Concatena("no_le~",U~imoVerbo),&ColaVerbos~; }

Encola(Concatena("se-",UltimoVerbo),&ColaVerbos); }

Encola(Concatena("que_",UltimoVerbo),&ColaVerbosi; }

Encola(Concatena("que_no_",UltimoVerbo),&ColaVerbosl;)

Encola(Concatena("no_se_",UltimoVerbo),&ColaVerbos); };

Si la información es correcta lexicográficamente y sintácticamente, entonces se realiza la generación de código intermedio, que convierte la información a tuplas en código de Prolog, las cuales se guardan en un archivo con el mismo nombre del que contiene el conocimiento del experto pero con extensión '.SAC' Cada oración se transforma a una tupla que tiene la siguiente forma (como ya se mencionó anteriormente):

verbo(lista_sustantivos,lista_complementos,etiqueta_texto)

Donde: etiqueta-texto es una etiqueta que identifica el texto correspondiente a una oración, o es la palabra 'pregu' cuando se trata de una oración correspondiente a una pregunta.

El elegir la gramática en Lenguaje Natural nos pareció adecuado, pues al obtener tuplas de la información, la salida al archivo que contendrá la base de conocimiento se puede modificar de la manera deseada. El archivo de salida tiene alguna de las siguiente formas:

2) si X, y; z ...

u; v, w ...

entonces

fin

si x, y; z ...

u; v, w ...

fin

entonces

entonces u; v, w ...

fin

Donde a su vez, cada literal representa un si-entonces, comparación, asignación u oración. La siguiente sección describe, las funciones de usuario de la implantación del compilador, así como la explicación del funcionamiento y el porqué de cada funcitin. Conforme realizamos la generación de ccidigo intermedio, se fueron necesitando algunos archivos de librerias los cuales se llaman desde &a seccion antes de realizar acciones semiinticas e incluso antes de definir funciones, pues éstas necesitan dichas librefias.

010% '

"""Y"_I"""""""""II"~

lNCLUDES "~"~"""""""""----------"

#include <stdio.h>

##include <stdlib.h>

#include <string.h>

#include <ctype.h>

"cc"""~""-~"""""~""-

CONSTANTES GLOBALES """"""""""""""""""~"~"""

#define CantVerbos 200

La tabla de verbos tiene como número máximo de verbos la cantidad de verbos que existan en VERBOS.SAC, sin embargo no puede contener más allá de la cantidad señalada por Cantverbas que seguramente superar-á a los 200.

#define TamVerbo 15

El tamaño que pensamos que podría tener como máximo un verbo fué de 15 caracteres, pero si se desea cambiar, se debe modificar la constante TamVerbo.

#define Prendido 1 Wefine Apagado O

Prendido y Apagado son constantes que se ocupan en la parte de verificación del salto de línea. Prendido indica que acaba de haber un salto de línea, Apagado indica lo contrario.

Las constantes definidas al inicio del programa, se definieron ahí para fines de claridad pues solamente se usan en la parte de las acciones semánticas en la gramática.

____"___"___"""______l_l___________"""""""""""""""

ESTRUCTURA DE DATOS "___""_____"___""""""""""""""""""""""~""""---

struct Tupla { int SiEntonces, Inter, Conector, Pregunta, SiAnidado;

char *UnVerbo,*UnEnunciado,*ClaveTexto; struct Tupla *Liga; 1;

struct Tupla es una estructura en la cual se tienen variables necesarias para almacenar datos de cada una de las oraciones, asignaciones o comparaciones. Esta estructura sirve para ir guardando el código en semiprolog con datos necesarios para el "vaciado" a la lista. SiEntonces es un campo que se ocupa para indicar si se trata de una expresión en la parte de la causa o en la parte del efecto. Debido a que puede existir "si ... entonces" anidados, se pueden realizar varias acciones dependiendo de las necesidades al momento de vaciar en la lista global. Es decir, si se requiere que haya un fin por cada si, ser& diferente la acción por realizar que cuando no se necesita. El que la tupla contenga la información de qué tan anidado está un si ... entonces con respecto a otro, permite realizar acciones para las dos necesidades anteriores. Las utilidades son más claras mientras más se tengan necesidades no al momento de formar el archivo de salida, sino al momento de accesar la información para ofrecerla al usuario o para seguir razonamientos. Inter es un campo que se ocupa por el momento para dos cosas: a) Para que al final de cada si ... entonces NO anidado, se escriba sobre el

b) Para estética en el archivo de salida pues el tabulador se agranda conforme

Cada frase puede estar "sola" o conectada por medio de comas y por medio de puntos y comas. Para saber si al momento de vaciar a la lista global las tuplas se requiere agregar uno de estos conectores, se usa un campo llamado Conector. Pregunta es un campo que sirve para saber si la oración correspondiente es una pregunta o no. SiAnidado es un campo que sirve para saber si una oración corresponde a un si dentro de un otro. UnVerbo, UnEnunciado y ClaveTexto, forman en conjunto lo que denominamos un predicado. UnVerbo es exclusivamente el verbo con sus posibles combinaciones. UnEnunciado tiene ya incluidos los corchetes de las listas que se formaron. Estas listas se forman en las especificaciones de L-S. ClaveTexto, o bien es nula lllbll, o bien es de la forma "texto(centero>". El apuntador a la siguiente tupla está referenciado por el campo Liga. La ventaja de tener las tuplas con código intermedio es que la salida se puede facilitar si se desea modificar. No es necesario modificar todo el programa para cambiar el aspecto del archivo de salida; nada más se modifica la función que traslada las tuplas a la lista global y con eso se resuelve el problema.

archivo un fin-si.

se anida más algún si ... entonces.

struct Prolog { char *LineaProlog; struct Prolog *Liga; 1;

La estructura struct Prolog se creó para ir formando la salida antes de ser escrita en el archivo. En primer lugar, para ahorrar tiempo realizando de golpe una escritura, y en segundo lugar, para no crear un archivo que probablemente no quede terminado pues no se sabe si compilará el archivo de entrada.

strud Textual { char *Renglon;

struct Textual *Liga; 1;

stwt ListaTextos { char "Texto; struct ListaTextos *Liga; 1

struct Listaverbos { char *Cadenaverbo;

struct Listaverbo *Sig; 1;

struct ColaDeCadenas { strud Listaverbos *Inicio, *Fin;

1;

struct PilaEnteros { int SiEntOtro;

struct PilaEnteros *Liga; 1;

struct ColaDeEnteros { struct PilaEnteros *Inicio, *Fin;

1;

/* "-1""""""""""""""""""""""- VARIABLES GLOBALES

""""""""1"""""""""""""""" *I

int NumLinea = 1 ; int NumVerbos; int NumPal; int EtiTexto = O; int Etiqueta = O; int Si-Entonces = HECHO; int BanderaaTexto = FALSO; int Internado f -1 ; int TamVar; int UltimoSi; int SaitoDeLinea; int NumColumna = 1 ; int ConectorG = NADA; int EnteroAux; int Bandera = 1 ; int Indicador = O;

int Anidado; int Repetido = O;

char TablaVerbos[CantVerbos][TamVerbo] ;

Tablaverbos es necesaria para guardar los verbos que se encuentran en el archivo VERBOS.SAC.

char Tabla[CantVerbos][TamVerbo];

Tabla es necesaria para guardar las palabras que se encuentran en el archivo PALABRAS.SAC.

char *CadenaProlog = "" ;

Cadena prolog se pensó usar para ir formando la cadena que se transforma de tupla a un elemento de la lista de prolog, solamente que ya no se ocupó por el momento ya que se manda como parámetro la concatenación de cadehas.

char UltimoArt[S];

Para conservar el valor del artículo después de ser leido otro token, se respalda en esta variable.

char *Ultimold;

AI igual que la variable anterior, se usa ésta para respaldar et valor de la última variable que se haya capturado.

char *UltimoVerbo;

Nuevamente se tiene una variable para respaldar el último verbo que se haya capturado.

char *LineaTexto;

Se usa LineaTexto para ir formando la cadena que se introduce en la lista global de texto. Se va concatenando cada vez que llega un token diferente de un punto y se inicializa con nulo cada vez que llega un nuevo texto y cada vez que hay un salto de línea dentro del texto. Esta línea va a ser introducida tal y como la haya introducido el experto.

char EtiTextoCad[7];

Para transformar a cadena el valor que tenga la variable EtiTexto, se creó la variable EtiTextoCad en la cual se guarda ésta cadena numérica. Se concatena para el tercer parámetro del modelo de predicados elegido.

char *FrenteDeTexto;

Como todos los textos se iban a estar inicializando con Texto(<Número>):- ".., se inicializa una variable con éste texto y se le agrega posteriormente el número de texto al que corresponde. La salida del predicado de texto es de la forma: Texto(<Número>):- "<Texto>".

char Lista[TamVerbo];

Se utiliza en lexyy.c, para copiar yytext en esta variable y utilizada en la función Revisa.

struct Prolog *LProlog = NULL;

LProlog es la lista global en la que se tiene lo que saldrá al archivo. Esta lista ya debe de tener el formato que se desee para no perder tiempo en la escritura al archivo.

struct Prolog *UltimoProlog = NULL;

UltimoProlog es un apuntador al último elemento de la lista el cual se usa para tener acceso rápidamente al momento de insertar cada elemento en ListaProlog.

struct Textual *LTexto = NULL; strud Textual *UltimoTexto = NULL;

Estas dos variables tienen la misma filosofía que las anteriores pero para la lista de texto.

struct Lista Textos *LTex = NULL; struct Lista Textos WltimoT = NULL;

struct Tupla *LTupla = NULL; struct Tupla *UltimoTupla = NULL;

Como en la lista de tuplas se insertan los elementos también en la última posición, se usa una lista con un apuntador al elemento último. De la misma forma que las anteriores listas, se crean las tuplas aunque recuérdese que las estructuras a las que apuntan son completamente diferentes.

struct ColaDeCadenas ColaVerbos,ColaAtom;

Para cada verbo que se tenga en un enunciado, se crea una tupla con los mismos parámetros en todas estas tuplas. Para no perder los verbos que se tienen en una misma oración, y considerando que no se puede formar la tupla en el momento que llega un verbo pues todavía falta el complemento de la oración, los verbos se guardan en una cola, la que se desencola una vez que se terminó de compilar una oración.

struct PilaEnteros *Pila = NULL;

Esta pila naci6 con la necesidad de guardar el primer campo de la tupla sin

formar la salida a la lista, se desapilaran los valores. -. que se pedjera confome seanidan los "si ... entonces" para que al momento de

struct ColaDeEnteros ColaUníonDeVerbos;

#include "lexyy-c"

Se tiene que incluir este archivo (leer apéndice de Yacc) para las funciones de la parte Iéxica. La ubicación de este include se debe a que para compilar el código, marcaba error pues no conoce algunas de las variables globales.

/* .................................. */

La función concatena, se realizó debido a que fué muy común la concatenación de dos cadenas inicializadas, o bien de no inicializadas, además de aprovechar que se puede regresar el valor no en un parámetro. El código permite ver variar ventajas de esta función como por ejemplo :

Concatena(Concatena(Cadena,Cadena),Cadena).

char *Concatena (char *Cad1 , char *Cad2) { char *Cad;

Cad = (char *)malloc(strlen(Cadl)+strlen(Cad2)+1); Cad = strcpy(Cad,Cadl); Cad = strcat(Cad,Cad2); return(Cad);

1

Crea un nodo para la lista de prolog, regresando un apuntador a ese nodo. Estas acciones son repetidas en las funciones cuyo nombre sea Nodo .

struct Prolog *NodoProlog (void) { struct Prolog *ElementoAux;

if ((ElementoAux = (struct Prolog *)malloc(sizeof(struct Prolog))) != NULL) {

ElementoAux->LineaProlog = NULL; ElementoAux->Liga = NULL; return(E1ementoAux);

1 else

return(NULL); 1

En la siguiente función se requiere solamente un parámetro de tipo apuntador a caracter pues dentro de ésta se realizan los pasos necesarios para crear y colocar el nodo al final de la lista. Fue cómodo trabajar de esta forma (con variables globales) pues al momento de insertar en la lista, basta con instrucciones simples para ser ejecutadas las acciones. Ejemplo :

InsertaProlog(Concatena(Cadena,Cadena)).

void InsertaProlog (char *Elemento) { struct Prolog *NodoAuxProlog;

NodoAuxProlog = NodoProiogO; NodoAuxProlog->LineaProlog = (char 9malloc (strlen(Elemento)+l); strcpy(NodoAuxProlog->LineaProlog,Etemento); if (UltimoProlog == NULL)

LProlog = UltimoProlog = NodoAuxProlog; else {

UltirnoProlog->Liga = NodoAuxProlog; UltimoProlog = UltimoProlog->Liga;

1

Con el archivo VERBOS.SAC se llena la tabla de verbos. La lectura de estos verbos es al inicio del programa para evitar pérdidas de tiempo en la compilación. El comentario "Error al abrir el archivo VERBOS.SAC tal vez sea bueno no presentárselo al usuario sino algún otro que no presente nombres de nada.

int IniciaTablaVerbos (void) {

FILE *ArchVerbos; int ¡=O; char UnVerbo[TamVerbo];

NumVerbos = O; if ((ArchVerbos = fopen("VERBOS.SAC","rt")) != NULL) {

while (fscanf(ArchVerbos,"%s",UnVsrbo) != EOF) { strcpy(TablaVerbos[i],UnVerbo); ++i;

1 NumVerbos = i; while0 < CantVerbos) {

++i; strcpyCTablaVerbos[i],"");

1 fclose(ArchVerbos); return(1);

1 else {

cirscro;

printf("Error al abrir el archivo Verbos.Sac"); return(0);

gotoxy(l9,20);

1 1

Con el archivo PALABRAS.SAC se llena la taMa de palabras. La lectura de éstas es al inicio del programa para evitar pérdidas de tiempo en la compilación. Estas palabras son aquellas que el usuario indicó a SAC que no son verbos y se guardan, para que SAC al encontrarlas otra vez y vea que existen en esta tabla, sepa que no son verbos y no vuetva a preguntarle al usuario si es un verbo.

void IniciaTabla (void) { FILE *Archpal; int i=O; char UnaPal[TamVerlso];

NumPal = O; if ((Archpal= fopen("PALABWS.SAC","n")) != NULL) {

while (fscanf(ArchPal,"%s",UnaPal) != EOF) { strcpy(Tabla[i],UnaPal); ++I;

1 NumPal = i; while0 < CantVerbos) {

++i; str~py(TabIab],"~);

1 fclose(ArchPa1); 1 else {

if ((ArchPai = fopen("PALABWSSAC","w'9) != NULL) { i = o;

while(¡ CantVerbos) { ++j;

strcpy(TablaIi],""); } fclose(ArchPa1);

1 else {

clrscro; gotoxy(l9,20); printf("Error al abrir el archivo PalabrasSac"); }

1 1

IniciaVar es una función que se ocupó para inicializar variables globales a las que no se pudieron dar valor en su declaración. En este caso se trata de dos registros que son las colas.

void IniciaVar (void)

ColaVerbos.1nicio = NULL; /* verbos*/ ColaVerbos.Fin = NULL; ColaAtom.lnicio = NULL; /*atornos */ ColaAtom.Fin = NULL; ColaUnionDeVerbos.lnicio = NULL; ColaUnionDeVerbos.Fin = NULL;

void CambiaArchivo (char *Archival, char ArchivoFu) { int p,i=O;

while (Archivol[i] != l . ' )

++i;

for (p=O; p<=i; ++p)

ArchivoF[pJ = 'S'; A=hivoF[++p] = 'A'; ArchivoF[++p] = I C ;

ArchivoF[+*p] = '\o+;

ArchivoF[p] = toupper(ArchivoI[p]);

1

PROCESOS CON LISTA DE PROLOG

Cuando hubo algún error.de compilación, o bien cuando todo fué exitoso y ya se escribió en el archivo el resultado, la memoria debe quedar libre. Es por ello que se vacían las listas que se tengan.

Las funciones HaceNula se mandan llamar al final del programa en la función MeteAArchivo y en la función yyerror, las cuales son las dos posibilidades a las que puede llegar el compilador. ""i"""""""""""""""""""" *I

void HaceNulaProlog (struct Prolog "LProlog) { struct Protog *ListaAux;

if ((*LProlog) != NULL) { ListaAux = *LProlog; while (ListaAux != NULL) {

*LProlog = ListaAuxaLiga; free(ListaAux->tineaProlog); free(ListaAux); ListaAux = *LPrblog;

1 1 .

1

}* """"""- ""1"" """ *"" """"""

*I PROCESOS CON LISTA DE TEXTOS

"""""""""""""""""""""

void HaceNuiaTexto (struct Textual "LTexto) { struct Textual *ListaAux;

if ((%Texto) != NULL) { ListaAux = *LTexto; while (ListaAux != NULL) {

*LTexto = ListaAux->Liga; free(ListaAux->Renglon); free(ListaAux); ListaAux = *LTexto;

1 1

1

struct Textual *NodoTexto (void) { struct Textual *ElementoAux;

if ((ElementoAux = (struct Textual *)rnalloc(sizeof(struct Textual))) != NULL) c

ElementoAux->Renglon = NULL; ElementoAux->Liga = NULL; retum(E1ementoAux);

1 else

1 return(NULL);

void InsertaTexto (char *Elemento) { struct Textual *NodoAuxTexto;

NodoAuxTexto = NodoTextoO; NodoAuxTexto->Renglon = (char *)malloc (strlen(Elemento)+l); strcpy(NodoAuxTexto->Renglon,Elemento); if (UltimoTexto == NULL)

LTexto = UltimoTexto = NodoAuxTexto; else {

UltimoTexto->Liga = NodoAuxTexto; UltimoTexto = UltimoTexto->Liga;

1 1

/* """"""""""""""""""""""""""""""""

*I PROCESOS CON LISTA DE TUPIAS

.................................

void HaceNulaTupla (struct Tupla **LTupla) { struct Tupla *ListaAux;

if ((*LTupla) != NULL) { ListaAux = *LTupla; while (ListaAux != NULL) {

*LTupla = ListaAux->Liga; free(ListaAux->Unverbo); free(ListaAux-NnEnunciado); free(ListaAux-XYaveTexto); free(ListaAux); ListaAux = *LTupla;

1 1 UltimoTupla = NULL;

1

struct Tupla *NodoTupla (void)

{ struct TupJa *ElementoAux;

if ((ElementoAux = (struct Tupla *)malloc(sizeof(struct Tupla))) != NULL) {

ElementoAux->Unverbo= ElementoAux->UnEnunciado= ElementoAux-%laveTexto= NULL; ElementoAux->Liga = NULL; return(E1ementoAux);

1 else

teturn(NULL); 1

void InsertaTupla (int si-ent, int Internado, int Conec, char *Vert%),

{ struct Tupla *ElementoAux; char *Enunc, char "TextoEti, int Anida)

ElementoAux = NodoTuplaO; (ElementoAux->SiEntonces) = sjent; (ElementoAux->Inter) = Internado; (ElementoAux->Conedor) = Conec; (ElementoAux->Pregunta) f Indicador; (ElementoAux->SiAnidado) = Anida; (ElementoAux->Unverbo) = (char *)malloc(strlen(Verbo)+l); (ElernentoAux->UnVerbo) = strcpy((E1ementoAux->UnVerbo),Verbo); (ElementoAux->UnEnunciado) = (char *)malloc(strlen(Enunc)+l); (ElementoAux->UnEnunciado) = strcpy((E1ementoAux->UnEnunciado),Enunc); (ElementoAux->ClaveTexto) = (char *)malloc(strlen(TextoEti)+l); (ElementoAux->ClaveTexto) = strcpy((E1ementoAux->ClaveTexto),TextoEti); if (UltimoTupla == NULL)

else { LTupla = UltimoTupla = ElementoAux;

UltimoTupla->Liga = ElementoAux; UltimoTupla = UltimoTupla->Liga;

1 1

I* """"""1""""""""""" .. "" .. """""

*I PROCESOS CON COLA DE ATOMOS

"""""""C"""""""""""~"""""""~

void HaceNulaCola (struct ColaDeCadenas "Cola) { struct Listaverbos *ApAux;

if ((*Cola).lnicio != NULL) { ApAux = (*Cola).Inicio; while (ApAux != NULL) {

CCola).lnicio = ApAux->Sig; free(ApAux->Cadenaverbo);

free(ApAux); ApAux = (*Cola).Inicio;

ApAuX->Sig NULL;

1

(*Cola).Fin = NULL; 1

1

struct Listaverbos *Nodoverbo (void) { struct Listaverbos *ElementoAux;

if((ElementoAux=(strct Listaverbos *)malloc(sizeof(struct ListaVerbos)))!=NULL) {

ElementoAux->Cadenaverbo = NULL; ElementoAux->Sig = NULL; return(E1ementoAux);

1 else

return(NULL); 1

En las funciones anteriores se da por supuesto que no necesitan mayor explicación pues los razonamientos serían equivalentes a los mencionados en las primeras funciones.

void Encola (char *Elemento, struct ColaDeCadenas *C) { struct Listaverbos *ApAux;

ApAux = NodoVerboO; (ApAux->Cadenaverbo) = (char *)malloc(strlen(Elemento)+l); (ApAux->Cadenaverbo) = strcpy((ApAux->CadenaVerbo),E\emento); if ((*C).lnicio == NULL)

else { (*C).lnicio = (*C).Fin = ApAux;

((*C).Fin)->Sig = ApAux; (*C).Fin = ApAux;

1 1

char *DesEncola (struct ColaDeCadenas *C) { struct Listaverbos *ElementoAux;

char *CadAux;

if ((*C).lnicio == NULL) return("");

else {

1 }

ElementoAux = (*C).lnicio; (*C).lnicio = ((*C).Inicio)-rSig; CadAux = (char *)malloc(strlen(ElementoAux->CadenaVerbo)+l); CadAux = strcpy(CadAux,ElementoAux->Cadenaverbo); free(E1ementoAux->Cadenaverbo); free(E1ementoAux); return(CadAux);

strud PilaEnteros *NodoPila (void) { struct PilaEnteros *NodoAux;

NodoAux = (struct PilaEnteros *)malloc(sizeof(struct PilaEnteros)); NodoAux->Liga = NULL; NodoAux->SiEntOtro = SI; return(NodoAux);

1

void EncolaEntero (int Elemento, struct ColaDeEnteros *C) { struct PilaEnteros *ApAux;

ApAux = NodoPilaO; (ApAux-PSiEntOtro) = Elemento; if ((*C).lnicio == NULL)

(*C).lnicio = (*C).Fin = ApAux; else {

((*C).Fin)-zLiga = ApAux; (*C).Fin = ApAux;

1 1

int DesEncolaEntero (strud ColaDeEnteros *C) { struct PilaEnteros *ElementoAux; int EnteroAux;

if (("C).lnicio == NULL) return(NADA);

else {

1 1

ElementoAux = (*C).lnicio; (*C).lnicio = ((*C).Inicio)->Liga; EnteroAux ElementoAux->SiEntOtro; free(E1ementoAux); return(EnteroAux);

I* "- IU"""""""""""""""

ACCIONES PARA LAS TUPLAS "-"""""""""""""""""""" *I

void Formaiuplas (char *Cadena, int Num, int Anida) { char *EtiAux,*EtiAux2;

EtiAux = (char ")alloc(5); EtiAux2 = (char *)malloc(l2); white (ColaVerbos.lnicio != NULL) {

if (Num == CIERTO) {

if (Repetido)

else

EtiAux2 = strcpy(EtiAux2,"texto("); EtiAux2 = strcat(EtiAux2,EtiAux); EtiAux2 = strcat(EtiAux2,")");

EtiAux = itoa(Etiqueta,EtiAux,lO);

EtiAux = itoa(EtiTexto,EtiAux,l O);

InsertaTupla(Si-Entonces,lntemado,DesEn~laEntero(&ColaUnionDeVe~os), DesEncola(&CotaVerbos),Cadena,EtiAux2,Anida);

1 else InsertaTupla(Si_Entonces,Internado,DesEncolaEntero(&ColaUnionDeVerbos),

DesEncola(&ColaVerbos),Cadena,"",Anida); 1 UltimoTupla->Conectar = ConectorG; if ((Num == CIERTO) && (!Repetido))

else ++EtiTexto;

Repetido = O; 1

/* ~"""""_""""""""""""""""""""""""~ ""-"

*I PROCESOS CON PILAS

..................................

void Push (int Elemento, stnrct PilaEnteros "P) { struct PilaEnteros *NodoAux;

NodoAux = NodoPilaO; NodoAux->SiEntOtro = Elemento; if (*P == NULL)

else { *P = NodoAux;

NodoAux->Liga = *P; *P = NodoAux;

1 1

int Pop (struct PilaEnteros "P) { struct PilaEnteros *NodoAux;

int Elemento:

NodoAux = *P; *P = CP)->Liga; Elemento = NodoAux-rSiEntOtro; NodoAux->Liga = NULL; free(NodoAux); return(Element0);

1

void VerTope (struct PilaEnteros *P, int Cond, int *Anida) {

if (((P-SSiEntOtro) == OTRO) 84% (Cond == SI))

else *Anida = 1 ;

*Anida = O; 1

/* """"""""1""""""""" I """""""""

*/ OTROS PROCESOS CON LISTA DE TUPLAS PARA DESANlDAMIENTO """""""""""""""""""""""""""

int Anidada(struct Tupla *ListaTuplas) { struct Tupla *ListaAux;

ListaAux = ListaTuplas; while ( (ListaAux != NULL) && (ListaAux4nter <= O) )

{ ListaAux = ListaAux->Liga;

1; if (ListaAux->Inter > O)

retum(l);

return@); 81Se

1

struct Tupia *NodoAnterior(struct Tupla *ListaTuplas,struct Tupla *Elemento) { struct Tupla *ListaAux; struct Tupla +NodoAux; int Termina=Apagado;

NodoAux = ListaTuplas; UstaAux = ListaTuplas; while ((ListaAux != NULL) && (Termina == Apagado))

if (ListaAux->Liga == Elemento)

NodoAux = ListaAux; Termina = Prendido; 1

else ListaAux = ListaAux->Liga;

retum(NodoAux); 1

/* Inserta una tupla Hijo justo antes */ /* de la tupla Padre. La tupla Padre es */ /* un anidamiento de una regla, por lo */ r" tanto, antes del Padre debe existir */ f* un Hijo que juegue el papel de una */ /* consecuencia. Por ejemplo : */ I* *!

/* si a entonces */ /* si b entonces */ /* . . . */ /* */ /* se traduciría a el siguiente esquema: */ I* */ /* si a entonces Hijo */ I* *t /* si Padre entonces . . . */ /* */ /* donde Padre e Hijo son en realidad */ I* la misma oración b. */ /* */

void D-lnsertaTupla(struct Tupla *ListaTuplas, strud Tupla *Elemento, struct Tupla "Lugar)

{struct Tupla *Anterior; struct Tupla *Hijo; if (Lugar == NULL)

else Lugar = Elemento; I* AI final de la lista de Tuplas */

{ Anterior = NodoAnterior(ListaTuplas,Lugar); Hijo = NodoTuplaO; if (Elemento->SiAnidado == 1)

(Hijo->SiEntonces) = OTRO; (Hijo->Inter) = (Elemento->Inter)-I; (Hijo->SiAnidado) = NADA;

(Hijo->SiEntonces) = Anterior->SiEntonces + 1 ; (Hijo->Inter) = Anterior->Inter; (Hijo->SiAnidado) = Elemento-SiAnidado;

(Hijo->Pregunta) = Elemento->Pregunta; (Hijo->Conectar) = Elemento-BConector; (Hijo->UnVerbo) = (char *)malloc(strlen(Elemento->UnVerbo)+l); (Hijo->Unverbo) = strcpy((Hij0->UnVerbo),Elemento->Unverbo); (Hijo->UnEnunciado) = (char *)malloc(strlen(Elemento->UnEnunciado)+l); (Hijo->UnEnunciado) = strcpy((Hijo->UnEnunciado),Elemento->UnEnunciado); (Hijo->ClaveTexto) = (char *)malloc(strlen(Elemento->ClaveTexto)+l); (Hijo->C\aveTexto) = strcpy((Hij0->CIaveTexto),Elemento->CIaveTexto); if (ListaTuplas != Lugar)

{ Anterior->Liga = Hijo; Hijo->Liga = Lugar;

if (Lugar->Liga != NULL) Lugar = Lugar->Liga;

1

{ Hijo->Liga = ListaTuplas; ListaTuplas = Hijo;

else

/* Elimina un elemento de la lista de */ /* tuplas, pero sin perder de vista este */ P elemento, ya que se deberá de insertar */ P en alguna otra parte de la misma lista;*/ /* en realidad solo cambia de lugar. */

struct Tupla *D-ExtraeTupla(struct Tupla *ListaTupla,

{struct Tupla *Anterior; struct Tupla *Objetivo;

struct Tupla *Elemento)

Anterior = NodoAnterior(ListaTupla,Elemento); AnteriowLiga = Elemento->Liga;

Objetivo = (struct Tupia *)malloc(sizeof(struct Tupla)); Objetio->SiEntoncss = Elemento->SiEntonces; Objetivo->Inter = Elemento->Inter; Objetivo->Conectar = Elemento->Conectar; Objetivo->Pregunta = Elemento->Pregunta; Objetivo-SiAnidado = Elemento->SiAnidado;

Objetivo->Unverbo = (char *)malloc(strlen(Elemento->UnVerbo)+l); Objetivo->UnEnunciado= (char *)malloc(strien(Elemento->unEnunciado)*l); Objetivo-zClaveTexto = (char *)malloc(strlen(Elemento->ClaveTexto)+l);

(Objetivo-rUnVerbo) = strcpy(Objetivo->UnVerbo,Elemento->UnVerbo); (Objetivo->UnEnunciado)= strcpy(Objetivo->UnEnunciado,Elemento->UnEnunciado); (Objetivo->ClaveTexto) = strcpy(Objetivo->CiaveTexto,Elemento->ClaveTexto); Objetivo->Liga = NULL; free(Element0); return(0bjetivo);

1

I* D-Homogeiniza(Tup1as) */ r" Se encargará de restaurar los */ I* niveles de anidamiento de todas las */ /* tuplas. Es decir,el último proceso a la*/ /* lista de tuplas, sera el eliminar su */ /* campo de anidamiento (el campo en la */ /* estructura) y hacerlo cero. */

void D_latercambiaTuplas(struct Tupla *A, struct Tupla *B) { struct Tupla Tempo;

Tempo = (struct Tupla *)rnalloc(sizeof(struct Tupla)); Tempo->SiEntonces = A->SiEntonces ; Tempo->lnter = A->inter Tempo->Conector = A-rConector ; Temp&>Pregunta = A->Pregunta ; Tempo-SiAnidado = A-SiAnidado ;

Tempo->Unverbo = (char *)malloc(strlen(A->UnVerbo)+l); Tempo->UnEnunciado = (char *)malloc(strlen(A->UnEnunciado)+l); Tempo->ClaveTexto = (char *)malloc(strlen(A->ClaveTexto)+l); strcpy(Ternpo->UnVerbo,A-~UrNerbo) ; strcpy(Tempo->UnEnunciado,A->UnEnunciado) ; strcpy(Temp0->ClaveTexto,A->ClaveTexto) ;

A-BSiEntonces = B-~SiEntonces ; A->inter = B->lnter A-sConector = B->Conectar ; A->Pregunta = &>Pregunta ; A->SiAnidado = B->SiAnidado ; A->Unverbo = (char *)malloc(strlen(B->UnVerbo)+l); A-NnEnunciado = (char *)malloc(strlen(B->UnEnunciado)+l); A->ClaveTexto = (char *)malloc(strlen(B->ClaveTexto)+l); strcpy(A->UnVerbo,B->Unverbo) strcpy(A->UnEnunciado,B->UnEnunciado) ; strcpy(A->ClaveTexto,B->CIaveTexto) ;

B->SiEntonces = Tempo-sSiEntonces ; B->lnter = Tempo->Inter I

B->Conectar = Tempo->Conectar B->Pregunta = Tempo->Pregunta I

B->SiAnidado = Tempo->SiAnidado ; B->UnVerbo = (char *)malloc(strlen(Tempo->UnVerbo)+l); B->UnEnunciado = (char *)malloc(strlen(Tempo->UnEnunciado)+I); B->ClaveTextO = (char *)malloc(strlen(Tempo->ClaveTexto)+l); strcpy(B->UnVerbo,Tempo->UnVerbo) strcpy(B->UnEnunciado,Tempo-zUnEnunciado) ; strcpy(B->ClaveTexto,Tempo-XlaveTexto) ;

freevernpo);

void D-OrdenaTuplas(struct Tupla *ListaTuplas) { struct Tupla *Menor; struct Tupla *Puntero; struct Tupla TuplaTemporal;

if (ListaTuplas != NULL) { Menor = ListaTuplas; Puntero = ListaTuplas;

1 do {

do { if (Puntero->Inter Menor->Inter)

{ TuplaTemporal = D-ExtraeTupla(ListaTuplas,Puntero); D-lnsertaTupla(ListaTuplas,TuplaTemporal,Menor);

1

else { if (Puntero->Liga I= NULL)

else Puntero = Puntero->Liga;

if (Puntero->Inter e Menor->lnter) { Tuplalemporal = D-ExtraeTupla(ListaTuplas,Puntero);

D-lnsertaTupla(ListaTuplas,TuplaTernporal,Menor); 1

1 }while (Puntero->Liga != NULL);

if (Puntero->Inter Menor->Inter) { TuplaTemporal = D-ExtraeTupla(ListaTuplas,Puntero);

D-lnsertaTupla(ListaTuplas,TuplaTemporal,Menor);

if ((Menor != NULL) && (Menor->Liga != NULL)) 1

{ Puntero = Menor->Liga; Menor = MenowLiga; 1

)while (Menor->Liga I= NULL); 1

void D-CompletaTuplas(strct Tupla *ListaTuplas) { struct Tupla 'ListaAux;

ListaAux = ListaTuplas; do {

if ((ListaAux->Inter > O) && (ListaAux->SiEntonces == O))

D-lnsertaTupla(ListaTuplas,ListaAux,ListaAux); ListaAux = ListaAux->Liga;

/* Debe crear un Hijo de ListaAux y dejar ListaAux

*I

ListaAux = ListaAux->Liga;

{

1

apuntando a una tupla nueva o en su defecto a NULL.

else

}while (ListaAux != NULL);

I* Esta funcion es la que realiza el desanidamiento,

y en la misma lista de tuplas que recibe, deja a las tuplas totalmente desanidadas.

*/

void DESANIDA(struct Tupla *ListaTuplas) { if (ListaTuplas != NULL)

{ D-CompletaTuplas(ListaTup1as); /* VO.BO. */ D-OrdenaTuplas(ListaTup1as); I* VO.BO. */

1 1

/* ..................................

*I PROCESOS CON SALIDA A ARCHIVO

""""""""""""""""""""""""""""""""-

void AnalizaTupla (struct Tupla **ApTupla, char Tipo) {

int Status, Anidado, CantEspacios; char *CadPuntuacion, Tabulador = "I*;

Status = (*ApTupla)->SiEntoncs; Anidado = (*ApTupla)->Inter; InsertaProlog(Tipo); while ( ((*ApTupla)->Inter == Anidado) && (*ApTupla != NULL)

&& ((*ApTupla)->SiEntonces == Status) ) { CadPuntuacion = Concatena((*ApTupla)->UnEnunciado,",'?; if ((*ApTupla)->Pregunta == 'I)

else CadPuntuacion = Concatena(CadPuntuacion,"pregu");

if (strcmp((*ApTupla)->CIaveTexto,"") != O)

else CadPuntuacion = Concatena(CadPuntuacion,(*ApTupla)->ClaveTexto);

CadPuntuacion = Concatena(CadPuntuacion,"""); CadPuntuacion = Concatena(CadPuntuacion,")"); for (CantEspacios = O; CantEspacios e= ((*ApTupla)->Inter); CantEspacios++)

Tabulador = Concatena(Tabulador,(*ApTupla)->Unverbo); Tabulador = Concatena(Tabulador,CadPuntuacion); if (strcmp(Tipo,"") != O) {

Tabulador = Concatena(Tabulador," 'I);

if ((*ApTupla)->Conectar == COMA) Tabulador = Concatena(Tabulador," y");

if ((*ApTupla)->Conectar == PUNTOYCOMA) Tabulador = Concatena(Tabulador," o");

1 else

lnsertaProlog(Tabu1ador); freeuabulador); Tabulador = Concatena("",""); *ApTupla = (*ApTupla)->Liga; if (((*ApTupla)->SiEntonces) == SI)

1

Tabulador = Concatena(Tabulador,".");

InsertaProlog("fin-si.");

if ((*ApTupla)->Inter > Anidado) if (Status == SI) 1

1 insertaProlog("entonces");

void TrabajaTuplas(void) { struct Tupla *ApTupla;

ApTupla = LTupla; while (ApTupla != NULL) {

switch (ApTupla->Silhtoncs) { case SI : AnalizaTupla(&ApTupla,"si ");

case ENTONCES : AnalitaTupla(&ApTupla,"entonces ");

case OTRO : AnalizaTupla(&ApTupla,"otro ");

case HECHO : AnalizaTupla(&ApTupla,"");

break;

break;

break;

break; 1

1 1

struct ListaTextos WodoT (void) { struct ListaTextos *Aux;

if ((Aux = (struct ListaTextos *)malloc(sizeof(struct ListaTextos)))!=NULL) {

Aux+Texto = NULL;

retum(Aux); Aux->Liga = NULL;

1 else

return(NULL); 1

void InseFtaT (char *Elemento) { struct ListaTextos *NodoAuxT;

NodoAuxT = NodoTO; NodoAuxT->Texto = (char *)malloc(strlen(Elemento)+l); strcpy(NodoAuxT->Texto,Elemento); if (UltimoT == NULL)

else 1

LTex = UltimoT = NodoAuxT;

UltimoT->liga = NodoAuxT; UltimoT = UltimoT->Liga;

1 1

Existe (char *Linea) { struct ListaTextos *LT;

int es=O, cont=O;

LT = LTex; while ((LT != NULL) && (!es))

if (strcmp(LT->Texto,Linea) == O) { es = 1; Etiqueta = cont; Repetido = I ; }

LT = LT->Liga; cont += 1 ;

if (es)

else { return(1);

InsertaT(Linea); return(0); }

1

void AgregaRenglon (char *Letras) {

LineaTexto = Concatena(LineaTexto,Letras); if ((SaltoDeLinea == Prendido) && (!Existe(LineaTexto)))

InsertaTexto(Concatena(FrenteDeTexto,Concatena(LineaTe~o," \".Iv)));

free(LineaText0); LineaTexto = Concatena("",""); SaltoDeLinea = Apagado;

void ActualizaArchivo(void) { int p=o; FILE *ArchVerbos, *ArchVerbosl ;

if ((ArchVerbos = fopen("VERBOS.SAC","w")) != NULL) { while (p < NumVerbos) {

fprintf(ArchVerbos,"%s\n",TablaVerbos[p]); p += 1;

1 fclose(ArchVerbos); 1 p = o; if ((ArchVerbosl = fopen("VERBOS.ARI","w'')) != NULL) { while (p c NumVerbos) {

fprintf(ArchVerbos1 ,"%s",TablaVerbos[p]); fprintf(ArchVerbos1 ,".\n"); p += 1;

1

1 fclose(ArchVerbos1);

1

void ActualizaArch(void) { int p=O;

FILE *ArchPat;

if ((Archpal = fopen("PALABRAS.SAC","w")) != NULL) { while (p NumPal) {

fprintf(ArchPal,"%s\n",TaMa[p]); p *= 1 ;

I

I fclose(ArchPa1);

I

void MeteAArchivo (char Nuevofl) {

FILE *ArchProlog; struct Prolog *ListaAux; struct Textual *ListaAuxT;

if ((ArchProlog=fopen(Nuevo,"w")) != NULL) { ListaAux = LProlog; while (ListaAux != NULL) {

fprintf(ArchProlog,"%s\n",ListaAux->LineaProlog); ListaAux = ListaAux->Liga;

1 LidaAuxT = LTexto; while (ListaAuxT != NULL) {

fprintf(AArchProlog,"%s\n",ListaAuxT-sRenglon); ListaAuxT = ListaAuxT->Liga;

1 fclose(ArchPro1og); HaceNulaProlog(&LProlog); HaceNulaTexto(&LTexto);

1 1

p ""-"u""""-"""- """"""_ - """ """""_ *I

MAIN "-""""""""""""""""""""""""""

void main (int argc, char 'argvfl)

FILE *fopen(); char Nombre[l3];

IniciaVarO; Push(HECHO,&Pila);

if (argc == 1) { clrscro;

printf("Fa1ta nombre de archivo"); } gotoxy(l9,20);

else {

if ((yyin = fopen(argv[l],"r")) == NULL) { clrscro; gotoxy(l9,20); printf("Error al abrir el archivo , %s",argv[l]); 1

else {

CambiaArchivo(argv[l],Nombre); if (IniciaTablaVerbosO==l) {

IniciaTablao; clrscro; YYPaEe0; ActualizaArchivoO; ActualizaArchO; MeteAArchivo(N0mbre);

Ventana(l8,9,61 ,I 7); gotoxy(22,12); printf("La Base de Conocimiento se Encuentra"); gotoXy(22,14); printf("en el Archivo %s\n\n\n\n",Nombre);

clrscro;

1 fclose(yyin);

1 1

1

void yyerror (int NumError, char *Mensaje) {

HaceNulaProlog(&LProlog); HaceNulaTexto(&LTexto); gotoXy(l9,20); printf("ERR0R SINTACTICO en la Linea %d Columna %d",NumLinea,NumColumna); printf("%s",Mensaje); switch (NumError) {

case O : exit(1); 1

/* FIN DEL ARCHIVO DE ESPEClFlCACION PARA YACC */

Cada vez que el experto introduce nueva información por medio de SAC, se actualiza la base de conocimiento, agregando la nueva información traducida en código de Prolog. Pero hay un aspecto que cabe señalar, y es que si el experto introduce información repetida, SAC no revisa si ya existe esa información sino que la introduce a la base de conocimiento, por lo que existirá información repetida.

C O N C L U S I O N E S

Los procesos desarrollados por el s e r humano durante la vida diaria son tan comunes, que hemos perdido la capacidad de asombro ante fenómenos como el respirar, caminar, hablar, aprender, etc. Se trata de verdaderos milagros, de complicados procesos que en su mayoría pueden ser imitados tan solo parcialmente. Crear un modelo que sea un reflejo del ConOcimiento universal de un ser humano común, se ha visto acotado a áreas o sectores de especializacidn. El lenguaje natural que contempla SAC tuvo que ser acotado también. Este hecho no garantizó que los problemas concernientes disminuyeran; un problema interesante está incluido en la interpretación de reglas y su anidamiento, ¿Cómo desanidar las reglas anidadas. que dé el usuario de manera que se consente el sentido original que tienen?. La anterior fué una pregunta que nos planteamos casi al final de la realización de SAC y que no visualizamos al principio, y al tratar de resober este problema nos dimos cuenta de que era muy complejo para solucionado en el poco tiempo que nos quedaba para terminar SAC. Los verbos reconocibles por SAC fué un problema que al principio nos pareció simple y trivial, pero surgió la pregunta: ¿Cu& es la definición de un verbo?, si nos ponernos a pensar un momento, nos daremos cuenta de que en realidad no existe una regla que describa a un verbo; por lo que este problema ya no se vió tan trivial y la solución propuesta lo resuelve parcialmente.

Lo importante es que se cumplieron los objetivos propuestos antes de la elaboración de SAC. SAC es una herramienta que le permite al usuario introducir información a la base de conocimiento por medio de Lenguaje Natural, de manera que es de gran utilidad y de uso fácil. El conocimiento del usuario es traducido por medio de SAC a código de Prolog por lo que es compatible con la máquina de inferencias de Prolog.

SAC tiene ventajas y desventajas como cualquier otro sistema, y dependiendo de la aplicación será más o menos útil. A continuación se presentan las ventajas y desventajas que tiene SAC.

Las ventajas que presenta SAC son las siguientes:

- El poder introducir conocimiento en Lenguaje Natural es algo práctica, porque el usuario no necesita saber Prolog para introucir conocimiento sobre algún área, sino que SAC lo traduce a código de Prolog. - La actualizacicin de verbos al estar compilando el archivo que contiene el conocimiento del usuario es de gran utilidad, debido a que cuando SAC encuentra alguna palabra que puede s e r un verbo y no existe en el archivo VERBOS.SAC le pide al usuario que le indique si esa patabra es un verbo para actualizarla en VERBOS.SAC.

- La capacidad de desanidamiento. Cuando el usuario introduce información anidada, SAC la desanida para que la máquina de inferencia de Prolog no tenga ningún problema cuando se consulte.

conocimiento de manera diferente a la que está definido, según se desee para otros fines.

- La flexibilidad de SAC para modificar el formato de salida a la base de

Las tareas que no se implantaron en SAC son las siguientes:

- El manejo de errores no está contemplado, por lo que cuando SAC manda un mensaje de error al compilar el archivo que contiene e1 conocimiento del usuario, éste no puede saber exactamente el motivo del ermr, puesto que SAC solamente manda las coordenadas de la palabra en la que se detuvo porque encontró algo que no esta correcto.

La revisión de informacidn repetida en fa base de conocimiento es afgo que no es hecho por SAC. Por lo que es responsabilidad del usuario el no agregar informacirjn repetida. Tambib es responsabilidad del usuario introducir información que tenga sentido, porque tal y como la da es traducida a la base de conocimiento.

Según lo mencionado anteriormente; si se trabaja en resolver las tareas que no se implantaron en SAC, se obtendría un sistema muy completo que tendría una mayor utilidad y una aplicación más amplia. No fué posible resolver esas tareas, ya que requerían mucho más tiempo del asignado a la elaboración de SAC. Pero puede serle de gran utilidad para algunas aplicaciones que se adapten a1 esquema seguido por SAC.

Es interesante mencionar que la experiencia de realizar un sistema que maneje Lenguaje Natural, nos mostró lo complicado y extenso que es el lenguaje del ser humano y, que tratar de imitarlo es muy dificil; cosa que no creíamos que fuera de tanta dificultad al principio, hasta que nos empezamos a encontrar con diversas dificultades en la realización de SAC. Nos dimos cuenta de lo complicado y asombroso que es el proceso de razonamiento humano, que el simple hecho de decir "el perro come" nos hace asociar rápidamente la palabra perro con el animal y la palabra cone con una acción, y además asociar al animal con esa acción, sin ver esas palabras como palabras simplemente. Realmente la realización de SAC fue algo nuevo para nosotros, puesto que tuvimos que aprender sobre sistemas expertos, lenguaje de Programación en Prolog y manejar Lenguaje Natural; pero finalmente fue muy provechoso, puesto que nos enfrentamos con una serie de problemas que ya mencionamos y de los que aprendimos mucho, además de que nos hicieron ver lo maravilloso y complejo que es el cerebro del ser humano, y por consiguiente apreciaf cosas que nos parecen tan simples y que sin embargo encierran todo un proceso complejo como son el ver, oir, aprender, etc.

A P E N D I C E A

L E X

Lex es un generador de programas capaces de realizar el procesamiento léxico de archivos de texto, es decir programas que pueden reconocer ciertos patrones dentro del conjunto de caracteres que forman parte de un archivo de texto y realizar manipulaciones sobre tales cadenas. Los analizadores léxicos son programas que caen dentro de esta categoría, pues su función consiste en descubrir las secuencias de caracteres que forman tokens o delimitadores válidos de algún lenguaje fuente y realizan ciertas tareas sobre tales cadenas: eliminan los delimitadores de la entrada y asocian un código numérico a cada token.

Lex recibe como entrada un conjunto de expresiones regulares y produce como salida un programa escrito en algún lenguaje de programación de propósito general capaz de reconocer dentro de un archivo de caracteres las secuencias de símbolos que forman cadenas pertenecientes al lenguaje denotado por cada expresión regular. Cada expresión regular tiene asociado un conjunto de acciones que deben ser realizadas cada vez que una cadena con la forma indicada por la expresión regular sea encontrada en la entrada.

Lex cuenta con una notación propia para indicar las expresiones regulares que forman parte de la especificación de entrada. Las acciones asociadas a cada expresión regular se escriben en un lenguaje de programación de propósito general, de manera tal que el usuario de Lex tiene gran libertad para indicar el procesamiento que debe realizarse sobre las cadenas del archivo de entrada.

La notación empleada en Lex para indicar las expresiones regulares no constituye un lenguaje completo, pero puede verse como una extensión que puede ser agregada a diferentes lenguajes de programación denominados lenguajes anfitrión. Lex puede producir como salida programas en diferentes lenguajes anfitrión; el lenguaje anfitrión es empleado tanto para escribir el código generado por Lex, como los fragmentos de código con que el usuario especifica las acciones asociadas a cada expresión regular. Lex cuenta con bibliotecas compatibles con las distintas implementaciones de los lenguajes anfitrión soportados, lo cual lo vuelve adaptable a diferentes ambientes y distintos usuarios. Los lenguajes anfitrión actualmente soportados por Lex son C y RATFOR.

La especificación de entrada para Lex se proporciona en un archivo de texto que tiene el siguiente formato:

definiciones %% reglas %% funciones del usuario

donde donde las definiciones y funciones del usuario son opcionales. El segundo %% es opcional, pero el primero se requiere para marcar el inicio de las reglas. Las opciones para que el usuario defina variables para emplear tanto en su propio programa como en el generado por

Lex, pueden ir tanto en la sección de definiciones, como en la de reglas. En la sección de reglas, las líneas tienen el formato:

expresión acción

Las reglas constituyen una tabla, cuya columna izquierda contiene expresiones regulares y la columna derecha contiene acciones (fragmentos de programa que deben ser ejecutados conforme las expresiones son reconocidas).

l e x transforma las expresiones regulares y acciones proporcionadas por el usuario (llamadas especificación de entrada) en un programa, escrito en el lenguaje anfitrión, denominado yylex. Dicho programa reconoce tas cadenas que tienen la forma indicada por las expresiones regulares y ejecuta las acciones especificadas para cada expresión conforme tales cadenas son detectadas en la entrada. Para mayor información referirse a la bibliografía presentada al Rnal de este manual.

A P E N O I C E B

Y A C C

Yacc es un generador de analizadores sintáctícos, esto es, un programa para convertir la especificación gramatical de un lenguaje, en un analizador sintáctico (parser) para las oraciones del lenguaje. Yacc provee un medio para asociar significados con los componentes de la gramática, de tal manera que ai mismo tiempo que ei análisis sintáctico tiene lugar se "evalúa" el significado.

Las etapas para utilizar Yacc son las siguientes:

Primero, se escribe una gramática. Esto específica la sintaxis del lenguaje. Yacc se puede usar a este nivel para evitar errores y ambiguedades gramaticales.

Segundo, cada regla o producción de la gramática puede ser aumentada con una acción -una frase sobre lo que se debe hacer cuando se encuentra en un programa que se analiza sintácticamente una ocurrencia de la forma gramatical. La parte "qué se debe hacer" se escribe en C, con convenciones para conectar la gramática con el código de C. Esto define la semántica del lenguaje.

Tercero, se necesita un analizador lexicográfico, que leerá la entrada que se analiza sintácticamente y la dividirá en componentes útiles para el analizador sintáctico.

Finalmente, se necesita una rutina de control para llamar al analizador sintáctico que Yacc construyó.

Yacc procesa 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, el analizador lexicográfico y la rutina de control pueden ser compilados, tal vez encadenados con otras rutinas en C y ejecutados. La operación de este programa consiste en llamar repetidamente al analizador lexicográfico para obtener componentes sintácticos (tokens), reconocer la estructura gramatical (sintáctica) en la entrada y desarrollar las acciones semánticas cuando se reconoce la regla gramatical. La entrada al analizador lexicográfico se debe llamar yylex debido a que es la función que yyparse llama cada vez que quiere otro componente sintáctico. (Todos los nombres utilizados por Yacc empiezan con y.) Para ser un poco más precisos, el archivo especificación para Yacc tiene el siguiente formato:

declaraciones 0% % reglas gramaticales %% funciones del usuario

Las secciones de declaraciones y funciones de usuario son opcionales. La sección de reglas puede contener una o más reglas gramaticales. Una regla gramatical tiene la forma:

LADO-IZQUIERDO: CUERPO;

donde LADO-lZQIJlERD0 representa un no terminal y CUERPO representa una secuencia de cero o m& nombres y literales. Los dos puntos y el punto y coma son signos de puntuacidn para Yacc, el punto y coma es opcional. Con cada regla gramatical, el usuario puede asociar acciones que se ejecutan cada vez que la regla es reducida durante el proceso de análisis. Las acciones pueden contener una o varias instrucciones de C. Para mayor información referirse a la bibliografía presentada al final de este manual.

B I B L I O G R A F í A

Muchnick,Steven S., Peter Schnupp. Building Expert Systems in Prolog. Springer-Verlag, New York, 1989.

Bratko,Ivan. Prolog Programming for Artificial Intelligence. International Computer Science Series, Glasgow, 1986.

Hu,David. Programmets Reference Guide to Expert Systems. Howard W.Sams & Company, 1987.

Hart,Anna. Knowledge Adquisition for Expert Systems. 2a. ed., Kogan Page, 1989.

Child,Herbert. Turbo C-C++: The Complete Reference. McGraw-Hill, 1990.

Brian,W.Kernighan and D,M.Ritchie. The C Programming Languaje, Prentice-Hall, N.J, 1978.

Brian,W.Kernighan, Rob Pike. El Entorno de Programación UNIX. Prentice-Hall, México, 1987.

S.C.Johnson, "Yacc: Yet Another Compiler Compiler", INTERACTIVE System/One Programmer's Manual, 1981. O