Escolar Documentos
Profissional Documentos
Cultura Documentos
TRABAJO DE INVESTIGACIN
Fecha 27/07/2016
Anlisis Sintctico del lenguaje JavaScript utilizando teora de autmatas
CONTENIDOS
INTRODUCCIN ............................................................................................................................................. 3
Objetivo ..................................................................................................................................................... 3
Alcance ...................................................................................................................................................... 3
ANLISIS ........................................................................................................................................................ 4
El Lenguaje................................................................................................................................................. 4
Caractersticas ........................................................................................................................................ 4
Consideraciones ..................................................................................................................................... 4
Palabras reservadas ............................................................................................................................... 4
Especificacin limitada ........................................................................................................................... 5
Gramtica .................................................................................................................................................. 6
Definicin ............................................................................................................................................... 8
Vocabulario No Terminal........................................................................................................................ 8
Vocabulario Terminal ............................................................................................................................. 8
Producciones.......................................................................................................................................... 9
Expresiones Regulares ............................................................................................................................. 10
Modelo a Desarrollar ............................................................................................................................... 11
Anlisis Lxico ...................................................................................................................................... 11
Anlisis Sintctico ................................................................................................................................. 13
IMPLEMENTACIN ...................................................................................................................................... 16
Autmata Finito Determinista ................................................................................................................. 16
El Analizador ............................................................................................................................................ 17
LEXER ....................................................................................................................................................... 20
agregar_token ...................................................................................................................................... 21
analizar_lxico...................................................................................................................................... 21
generar_tokens .................................................................................................................................... 22
PARSER..................................................................................................................................................... 24
analizar_sintaxis ................................................................................................................................... 25
reducir_tokens ..................................................................................................................................... 26
Ejemplo de ejecucin............................................................................................................................... 28
Eficiencia .................................................................................................................................................. 32
CONCLUSIONES ........................................................................................................................................... 34
BIBLIOGRAFA .............................................................................................................................................. 36
INTRODUCCIN
El presente documento est destinado a demostrar algunos casos de uso donde la utilizacin de
autmatas resulta efectiva para reconocer un lenguaje. Se intentar implementar la resolucin de las
tareas necesarias a travs de los modelos estudiados en la materia durante el cuatrimestre actual.
OBJETIVO
Se desarrollar una implementacin de analizador sintctico para una especificacin limitada del lenguaje
de programacin JavaScript especificado por el estndar ECMAScript ECMA-262, haciendo uso de las
herramientas y conceptos adquiridos durante la cursada. Ser analizada la tcnica utilizada y los
resultados de eficiencia alcanzados, como as tambin los desafos encontrados y la solucin propuesta
frente a los mismos.
ALCANCE
ANLISIS
EL LENGUAJE
Se ha seleccionado al lenguaje JavaScript como estructura a reconocer. La razn de esta decisin se debe
a que resulta interesante y prctico disponer de un analizador sintctico y, eventualmente, semntico
que permita detectar errores en la estructura de una codificacin del mismo ya que es un lenguaje
interpretado. Por lo tanto, al no disponer de una compilacin previa, el intrprete presenta los errores en
consola a medida que es ejecutado el cdigo.
Esto ltimo varias veces puede ocasionar algn comportamiento no deseado de la aplicacin ya que no es
frecuente implementar capturas de errores bien diseadas en este tipo de implementaciones. Es por ello
que si se dispone de un analizador del lenguaje puede inclusive ser importante para prevenir la ejecucin
de un cdigo vulnerado como as tambin ayudar a la temprana deteccin de errores.
Como base estructural de la gramtica se utiliza la especificacin Ecma-262 agregada como anexo.
Caractersticas
A continuacin, se detallan algunas de las propiedades del lenguaje que son importantes considerar para
la construccin de la gramtica que lo describe
Tipado Dinmico: Las variables se definen sin especificar un tipo inicial ya que el mismo se determina
dinmicamente en la ejecucin.
Funcional: Si bien es posible generar funciones anidadas y en Javascript son en realidad objetos, esta
implementacin se centra en el lenguaje imperativo sin considerar objetos. Se utilizar la declaracin de
funcin y su llamada respectiva.
Consideraciones
Palabras reservadas
El lenguaje especifica un conjunto de palabras reservadas que no pueden utilizarse como identificadores
de variables dentro del programa. Las mismas son:
break, case, catch, continue, default, delete, do, else, finally, for, function, if,in, instanceo
f, new, return, switch, this, throw, try, typeof, var, void, while, with
Especificacin limitada
La especificacin que se implementar en este analizador ser limitada y descripta a travs de los
siguientes elementos que determinarn la aceptacin del lenguaje.
Sentencia:
- Expresin
- Declaracin
- Sentencia de retorno (return expresin)
- Asignacin ( identificador = expresin)
- Estructura
Declaracin:
- Variable
- Funcin
Expresin:
- Llamadas a funcin
- Expresiones algebraicas
- Expresiones Lgicas
o Expresin booleana y operaciones lgicas
o Comparacin
- Identificadores
- Literales
Estructura:
- IF
- IF-ELSE
- WHILE
Literales:
- Nmero real positivo
- Cadena delimitada por comilla simple
Exclusiones:
- Funciones lambda y annimas
- Tipos de datos compuestos
- Asignacin en parmetros de funcin
- Evaluacin de expresin en la asignacin (+=, *=)
- Estructura de objetos
- Estructura for, switch
GRAMTICA
En principio, se intent definir la gramtica libre de contexto que especifica el lenguaje de forma similar a
la documentacin oficial. De esa manera, se poda ver claramente cmo se formaba una sentencia a
partir de elementos de la gramtica que se reutilizaban y daban valor no slo sintctico, sino tambin
semntico a cada smbolo.
Este anlisis inicial permiti descubrir algunos problemas interesantes de compartir. En primer lugar, la
gramtica expuesta en ECMA-262 genera un lenguaje que es aceptado por un autmata no
determinstico. Esto claro no cambiar segn qu gramtica lo genere, ya que el objetivo es en s
reconocer el mismo lenguaje, pero el problema encontrado con esta gramtica y autmata es que
requiere probar varias posibilidades para detectar la derivacin correcta, si es que existe. Es sin dudas la
ms representativa y de fcil interpretacin para las personas, pero resulta complicada en implementar
debido a que no puede utilizarse un autmata determinista y ello suele llevar a algoritmos con
complejidad en tiempo exponencial.
La solucin que se intent aplicar fue la de ajustar la gramtica libre de contexto y el algoritmo de anlisis
sintctico para que sea lo ms eficiente posible hacer su derivacin. Por lo tanto, se ver luego cmo se
descompusieron algunas producciones para remover el indeterminismo en el algoritmo en los casos que
ocurriese. Es importante aclarar que el indeterminismo no fue resuelto en la gramtica ni en el modelo de
autmata de pila, sino en la implementacin del mismo. En el caso visto previamente, se decidi
modificar la gramtica y el algoritmo (lookahead del <operador_asignacin>) para determinar qu
produccin aplicar luego:
Si bien aqu tambin existir una indeterminacin en el smbolo <identificador>, luego se ver que gracias
al lookahead selectivo, en realidad se resuelve este tipo de problemas ya que siempre se leer el smbolo
<operador_asignacin> si est despus de un <identificador>.
A medida que se avanz con el anlisis de la gramtica, tambin se debi determinar cmo sera su
derivacin, ya que este punto sera central en su definicin y en la implementacin del autmata.
Probablemente, la forma ms natural de encarar este punto sera utilizar la derivacin por la izquierda, a
travs de la cual siempre se reemplaza primero el smbolo no terminal ms a la izquierda de la
produccin. Entonces, sabiendo que la lectura de smbolos se hace de izquierda a derecha, este tipo de
anlisis se denomina LL(1), donde el 1 corresponde a la cantidad de smbolos ledos por vez. El problema
encontrado con el analizador LL(k) es que comienza la derivacin desde el smbolo inicial, es decir que su
anlisis ser descendente. Por lo tanto, aqu no hay forma de resolver el indeterminismo a menos que se
implementen algn algoritmo predefinido que mejora la eficiencia de la natural bsqueda exhaustiva que
propone el anlisis descendente. Entonces utilizando esa tcnica de derivacin que explora todas las
posibilidades el costo sera exponencial en tiempo y memoria, ya que se utilizara un algoritmo similar a la
bsqueda en amplitud de rboles (BFS).
Para reducir el costo en memoria, se podra utilizar otra tcnica con backtracking y recursividad
descendente de forma similar a la bsqueda en profundidad (DFS), aunque el tiempo seguira siendo
exponencial. A su vez, analizando los algoritmos LL para esta ltima opcin, se encontraban ciertas
dificultades cuando se tena una gramtica recursiva a izquierda, debido a que podan darse situaciones
de recursin infinitas. Por lo cual, por ejemplo la definicin de la siguiente produccin deba ser
redefinida para evitar dicha recursin:
Entonces, para alcanzar el tiempo lineal, se suelen modificar las gramticas para ser implementadas por
parsers predictivos. Esto lleva a intercambiar expresividad de la misma por rendimiento en el anlisis de la
entrada. Como se intentaba implementar un autmata eficiente y sin replicar una tcnica predefinida, se
busc una solucin en el anlisis ascendente, para lo cual es til la tcnica de derivacin por derecha. Este
tipo de anlisis se llama LR(k).
La tcnica que utilizan los algoritmos para anlisis LR suele ser siempre la misma, pero con algunas
variaciones. Consta de un autmata que segn el smbolo ledo realiza dos acciones: shift, reduce. La
primera prcticamente incorpora el smbolo terminal a la pila, considerando los smbolos que estaban
cargados previamente, mientras que la segunda reduce justamente la cadena de smbolos cuando
corresponde al lado derecho de una produccin y los reemplaza en la pila por su smbolo no terminal.
Como cada elemento de la pila debe representar al ltimo smbolo seguido de los smbolos por debajo, se
suelen utilizar tablas de apoyo para permitir al autmata resolver cada situacin de forma determinista.
Esto lo hace ms eficiente y permite derivar los elementos de forma ascendente ya que se comienza a
derivar a partir de los smbolos terminales ledos.
En esta implementacin se utilizar una tcnica de anlisis ascendente pero sin disponer de tablas de
apoyo. En cambio, se decidi adaptar la gramtica libre de contexto para poder construir un algoritmo
que evite utilizar recursin. De esta forma fue posible lograr una eficiencia similar a los algoritmos LR(k)
estudiados. Debido a que el autmata definido siempre deriva primero por derecha, no ser
problemtico que la gramtica sea ambigua ya que siempre se generar el rbol primero reduciendo los
smbolos a la derecha.
Definicin
G: Gramtica de tipo 2
G = { Vn, Vt, P, S }
Vocabulario No Terminal
Vocabulario Terminal
<terminador> ::= ;
<coma> ::= ,
<comparador> ::= > | < | <operador_asignacin> <operador_asignacin> | >= | <=
<operador_lgico> ::= && | ||
<operador_aritmtico> ::= + | - | * | /
<operador_asignacin> ::= =
<llave_inicio> ::= {
<llave_fin> ::= }
<parntesis_inicio> ::= (
< parntesis _fin> ::= )
Producciones
EXPRESIONES REGULARES
Algunos de los smbolos terminales de la gramtica previa corresponden a simples caracteres o cadenas
fijas que representan exactamente a los mismos. Existen casos donde esta correspondencia no puede ser
aplicada, ya que es necesario contemplar smbolos que pueden se generados a partir de gramticas
regulares, como es el caso de los identificadores.
Estos lenguajes generados son de tipo 3, por lo tanto pueden ser representados por autmatas finitos. Es
por ello que en el anlisis lxico se comprueba si algn smbolo ledo es aceptado por el autmata
configurado para reconocer alguna de estas expresiones regulares.
MODELO A DESARROLLAR
Se mencion previamente que el modelo utilizado parte del compilador de lenguajes de programacin. El
mismo comprende las siguientes operaciones o pasos para llegar al rbol de sintaxis abstracta.
Arbol de
Cdigo anlisis
Tokens AST
Fuente sintctico
El alcance definido para este proyecto se concentra en los primeros dos pasos, donde se presentar el
resultado de aceptacin del lenguaje ingresado. En caso de ser afirmativo, tambin se podr generar el
rbol de anlisis sintctico en un archivo de imagen png. De ser negativo, se presentar el detalle del
error asociado.
Entonces, se definirn dos etapas que pueden funcionar conjuntamente en la implementacin, pero se
decidi separarlas para expresar de forma ms sencilla el modelo codificado.
Anlisis Lxico
La operacin de anlisis lxico tiene como objetivo leer el cdigo fuente de entrada y obtener as los
lexemas o tokens que contiene. El lexema se puede definir como una cadena de caracteres que conforma
una unidad sintctica, mientras que el token sera la representacin del lexema en una estructura
particular que adems contiene otra informacin en relacin al mismo, como por ejemplo clasificacin o
ubicacin en la entrada.
Otra de las funciones que tiene el analizador lxico es remover los espacios y lneas en blanco, como as
tambin obviar los comentarios que puedan encontrarse en la entrada, ya que no sern tiles para el
anlisis sintctico posterior. En este caso, slo se consider el comentario de una nica lnea expresado a
continuacin como una expresin regular:
La estructura del token se arm para poder identificar el lugar del error. Se muestra la ubicacin del error
en el cdigo tanto para el anlisis lxico, como as tambin para el sintctico. Por lo tanto, se detecta el
error cuando un token no es reconocido o resulta que no existe regla asociada para reducir un conjunto
de smbolos terminales y/o no terminales al smbolo inicial.
La estructura de un token reconocido vlido es la siguiente:
Para el anlisis sintctico posterior, se utilizar el token completo. En un principio, slo se consideraba el
smbolo en la gramtica, pero de esa forma se perda trazabilidad del token para subsiguientes
evaluaciones. Es por ello que se cuenta con un identificador nico para cada token, logrando as detectar
un error en la aceptacin del lenguaje y poder proveer a qu seccin del cdigo representa.
Los desafos particulares de esta actividad fueron apareciendo durante el desarrollo. El ms problemtico
probablemente haya sido la deteccin de tokens compuestos, aquellos que se forman con dos o ms
tokens individuales. Un ejemplo es el de los comparadores ==, >= y <=. Cada uno de estos smbolos
est formado por dos tokens que si estn juntos deben considerarse como uno diferente. Por lo tanto,
para resolver este conflicto se ley el prximo carcter para determinar si era un token diferente. A esta
tcnica se la llama lookahead.
Otro detalle interesante fue cmo determinar la separacin de los tokens. Si bien el espacio en blanco y la
finalizacin de lnea son elementos separadores, existen otros que tambin deben separar tokens, como
el terminador ; o los operadores aritmticos. Entonces, era importante definir si el separador era en s
un token, ya que de serlo debera contemplarse.
Por ejemplo, los siguientes casos difieren en cmo se trata la separacin:
Id_prueba1 Id_prueba1;
Finalmente, se agreg un terminador para casos donde no se encuentre al finalizar la lnea. Aunque
existen ciertas situaciones donde no se debe agregar: lnea terminada en {, }, ).
El resultado de este proceso es un listado de tokens que representan cada unidad sintctica aceptada por
el lenguaje, es decir los smbolos terminales de la gramtica que ser utilizada para reconocerlo en el
prximo paso. Como se mencionaba previamente, algunos terminales son en s expresiones regulares.
Por lo tanto, para que un token sea aceptado por el analizador lxico, debe estar contenido en alguna de
las listas de smbolos terminales predefinidos, o bien ser aceptado por los autmatas finitos deterministas
que se configuraron para cada expresin regular particular.
Anlisis Sintctico
La funcin principal de esta etapa est en reconocer el lenguaje a partir de los tokens detectados en el
anlisis lxico. Aqu es donde se har uso de la gramtica libre de contexto especificada que ser utilizada
por el autmata de pila para aceptar o no el lenguaje ingresado. Los smbolos terminales de la gramtica
son los tokens ledos, mientras que las producciones proporcionarn la derivacin de los mismos hasta el
smbolo inicial.
La derivacin entonces es por derecha, intentando siempre derivar primero el smbolo ms a la derecha.
Esto implica que siempre la primera derivacin partir de un smbolo terminal, realizando as un anlisis
ascendente del lenguaje. Como la lectura de tokens sigue siendo de izquierda a derecha, entonces se
puede decir que utilizaremos una tcnica del estilo LR(k), pero no ser una de las implementaciones
existentes que hacen uso de tablas. El motivo es justamente para intentar implementar una solucin
propia. De todas formas, s se puede decir que la tcnica utilizada se basa en las operaciones de shift y
reduce que son comunes en los algoritmos LR.
Anlisis Sintctico
SENTENCIA
DECLARACIN
VARIABLE2
EXPRESIN
DECLARACIN
VARIABLE1
TRMINO
ASIGNACIN1
LITERAL
El analizador sintctico lee un token por vez, a excepcin de ciertos casos donde hace un lookahead
selectivo para leer el siguiente token y agregarlo si corresponde. En el ejemplo, el token OP_ASIGNACIN
es agregado por el lookahead que indica que luego de leer un IDENTIFICADOR se debe analizar el token
siguiente. Ante cada lectura de token, el parser implementado analiza junto con elementos de la pila del
autmata (smbolos terminales y no terminales previamente cargados) si existe una produccin que
coincida su parte derecha. Esto se hace leyendo de a un smbolo por vez de la pila y comparando la
produccin candidata. En caso de coincidir, se reemplazan los smbolos comparados con el no terminal a
la izquierda de la produccin que aplica. Esta operacin se la llama reduce.
Si no se encontrase produccin para ninguna lista de smbolos armada desde la pila, entonces se agrega
el token ledo a la misma y se avanza. Esta operacin se la llama shift.
En el ejemplo, primero se lee VAR y se agrega a la pila que est vaca, luego se lee IDENTIFICADOR y
OP_ASIGNACIN mediante el lookahead, entonces ah se detecta una produccin que tiene del lado
derecho IDENTIFICADOR OP_ASIGNACIN, por lo cual se reemplaza por el no terminal ASIGNACIN1
(reduce), el cual luego se reemplaza junto con VAR (que se encontraba en la pila) por DECLARACIN
VARIABLE1 (reduce). Entonces, al leer el prximo token CADENA, en la pila se encuentra el no terminal
DECLARACIN VARIABLE1.
A continuacin se detalla la representacin del autmata de pila para aceptar el lenguaje. En principio, se
muestra el concepto general donde se tienen slo dos transiciones que hacen referencia a las acciones de
shift y reduce.
t, | t
, S | , Z0 | Z0 S: Smbolo inicial
q0 q1 q1
q3
t: smbolo terminal (token)
Producciones de la gramtica: { pi ::= pd }
, | pi
, pd |
Parte izquierda Parte derecha
q2
q7
q8
, L |
, N |
, | L , T | q9
, | T
var, | V , | E
id1, | I
, S | , Z0 | Z0
=, | O
q0 qx q1
qf
1, | N
, E | , | DV2
Los estados qx y qf estn punteados porque en realidad el autmata no est completo para su
aceptacin, sino que se est representando la cadena sin el terminador ; que sera el que completa la
produccin DV TERMINADOR que se reducira a SENTENCIA y finalmente a PROGRAMA. Entonces ah s se
aceptara el lenguaje porque en la pila quedara slo el smbolo inicial PROGRAMA. Siempre para llegar al
estado de aceptacin es necesario que slo quede en la pila el elemento correspondiente al smbolo
inicial de la gramtica, en este caso <programa>.
Luego se ver cmo se implement este autmata de pila para que pueda derivar todas las producciones
de la gramtica presentada. El resultado obtenido por el analizador sintctico ser entonces el estado de
aceptacin del lenguaje de entrada.
IMPLEMENTACIN
En primer lugar, se implement una clase que defini a un Autmata Finito Determinista (AFD) el cual es
utilizado para reconocer las expresiones regulares que suelen formar algunos de los lexemas de
JavaScript.
class AFD:
def __init__(self):
# Se inicializan los parametros de definicion del AFD
self.entrada = ""
self.estado_actual = 1
self.ftransicion = {}
self.estados_aceptacion = []
self.vocabulario_terminal = []
def setEstadoInicial(self,estado):
# Especifica el estado inicial
self.estado_actual = estado
def setEntrada(self,entrada):
# Especifica el estado inicial
self.entrada = entrada
self.estado_actual = 1
def agregarTransicion(self,actual,elem,siguiente):
# Incorporar nueva transicion (estado, elem) : estado_siguiente
# Acepta un elemento o una cadena de elementos leidos
for i in range(0, len(elem)):
self.ftransicion[(actual,elem[i])] = siguiente
if (elem[i]) not in self.vocabulario_terminal:
# Agrega x al vocabulario terminal si no estaba
self.vocabulario_terminal.append(elem[i])
def agregarAceptado(self,estado):
# Agrega el estado al conjunto de aceptacion
self.estados_aceptacion.append(estado)
def evaluar(self):
# Condicion de salida (entrada vacia)
if self.entrada == "":
return self.estado_actual in self.estados_aceptacion
else:
# Se extrae el primer elemento de entrada
elemento = self.entrada[0]
self.entrada = self.entrada[1:]
# Verifica si el elemento leido tiene transicion en el estado actual
if (self.estado_actual,elemento) in self.ftransicion:
# Se pasa al siguiente estado
self.estado_actual = self.ftransicion[(self.estado_actual,elemento)]
return self.evaluar()
else:
# El elemento lleva a un estado Trampa => Rechaza
return False
En el alcance del proyecto actual, se definieron tres expresiones regulares para determinar la aceptacin
de los tokens al momento del anlisis lxico, las cuales son:
Nmero
Cadena
Identificador
Segn las expresiones regulares definidas y los autmatas construidos para las mismas, se utiliz la
implementacin de la clase AFD para evaluar la aceptacin. Por lo tanto, se ver que como parte de las
variables de clase del analizador se encuentra el seteo de estos tres autmatas:
La ltima variable tokens_expresiones es una lista de tuplas donde el primer elemento es la variable que
referencia al objeto AFD inicializado y configurado, y el segundo elemento es smbolo terminal del token
asociado si fuera aceptada la entrada. Por lo tanto, en en el anlisis lxico se evala la entrada para cada
AFD de la lista y, si es aceptado, se agrega el token con el smbolo terminal correspondiente.
EL ANALIZADOR
La clase principal que gestiona el anlisis completo del lenguaje es la llamada Analizador. Lo primero que
se debi determinar son las variables de clase constantes que representaran la gramtica que genera el
lenguaje seleccionado. Como se mencion previamente, esta gramtica est adecuada para que el
algoritmo del parser implementado opere eficientemente. Es por ello que algunos smbolos no terminales
suelen tener un sufijo numrico, haciendo entender as que son una variante del concepto semntico real
o bien una representacin parcial del mismo. Esto de todas maneras no impacta en el anlisis sintctico,
sino que ser relevante para el prximo paso de un compilador, el anlisis semntico, que no est
contemplado en este trabajo.
Los smbolos de la gramtica son:
# Simbolos Terminales
T_TERMINADOR = 'TERMINADOR'
T_COMPARADOR = 'COMPARADOR'
T_OP_LOGICO = 'OP_LOGICO'
T_OP_ARITMETICO = 'OP_ARITMETICO'
T_OP_ASIGNACION = 'OP_ASIGNACION'
T_ID = 'IDENTIFICADOR'
T_NUMERO = 'NUMERO'
T_CADENA = 'CADENA'
T_VAR = 'VAR'
T_FUNCTION = 'FUNCTION'
T_IF = 'IF'
T_ELSE = 'ELSE'
T_WHILE = 'WHILE'
T_RETURN = 'RETURN'
T_LLAVE_INICIO = 'LLAVE_INICIO'
T_LLAVE_FIN = 'LLAVE_FIN'
T_PAREN_INICIO = 'PARENTESIS_INICIO'
T_PAREN_FIN = 'PARENTESIS_FIN'
T_COMA = 'COMA'
Las producciones se implementaron en una lista de tuplas, donde cada una de ellas tiene la siguiente
representacin:
El primer elemento de la tupla es el smbolo no terminal de la izquierda de la regla, mientras que los
elementos restantes corresponden a los smbolos terminales y/o no terminales que componen la parte
derecha.
producciones = [(S_PROGRAMA,S_PROGRAMA,S_SENTENCIA),
(S_PROGRAMA,S_SENTENCIA),
(S_PROGRAMA,S_PROGRAMA,S_PROGRAMA),
(S_SENTENCIA,S_DV,T_TERMINADOR),
(S_SENTENCIA,S_DV2,T_TERMINADOR),
(S_SENTENCIA,S_DF),
(S_SENTENCIA,S_RETURN,T_TERMINADOR),
(S_SENTENCIA,S_ASIG,T_TERMINADOR),
(S_SENTENCIA,S_EXPRESION,T_TERMINADOR),
(S_SENTENCIA,S_ESTRUCTURA),
(S_DV,T_VAR,T_ID),
(S_DV,S_DV,T_COMA,T_ID),
(S_DV,S_DV2,T_COMA,T_ID),
(S_DV1,T_VAR,S_ASIG1),
(S_DV1,S_DV,T_COMA,S_ASIG1),
(S_DV1,S_DV2,T_COMA,S_ASIG1),
(S_DV2,S_DV1,S_EXPRESION),
(S_DV2,S_DV2,T_OP_ARITMETICO,S_EXPRESION),
(S_ASIG,S_ASIG1,S_EXPRESION),
(S_ASIG,S_ASIG,T_OP_ARITMETICO,S_EXPRESION),
(S_ASIG1,T_ID,T_OP_ASIGNACION),
(S_DF,T_FUNCTION,T_ID,T_PAREN_INICIO,T_PAREN_FIN,S_BLOQUE),
(S_DF,S_DF1,T_PAREN_FIN,S_BLOQUE),
(S_DF1,T_FUNCTION,T_ID,T_PAREN_INICIO,T_ID),
(S_DF1,S_DF1,T_COMA,T_ID),
(S_BLOQUE,T_LLAVE_INICIO,S_PROGRAMA,T_LLAVE_FIN),
(S_BLOQUE,T_LLAVE_INICIO,T_LLAVE_FIN),
(S_EXPRESION,T_PAREN_INICIO,S_EXPRESION,T_PAREN_FIN),
(S_EXPRESION,S_TERMINO),
(S_EXPRESION,S_EXPRESION,T_OP_ARITMETICO,S_EXPRESION),
(S_TERMINO,S_LITERAL),
(S_TERMINO,T_ID),
(S_TERMINO,S_CF),
(S_TERMINO,S_CONDICION),
(S_TERMINO,S_COMPARACION),
(S_LITERAL,T_NUMERO),
(S_LITERAL,T_CADENA),
(S_CONDICION,S_EXPRESION,T_OP_LOGICO,S_EXPRESION),
(S_COMPARACION,S_EXPRESION,T_COMPARADOR,S_EXPRESION),
(S_RETURN,T_RETURN,S_EXPRESION),
(S_RETURN,S_RETURN,T_OP_ARITMETICO,S_EXPRESION),
(S_CF,T_ID,T_PAREN_INICIO,T_PAREN_FIN),
(S_CF,S_CF1,T_PAREN_FIN),
(S_CF1,T_ID,T_PAREN_INICIO,S_EXPRESION),
(S_CF1,S_CF1,T_OP_ARITMETICO,S_EXPRESION),
(S_CF1,S_CF1,T_COMA,S_EXPRESION),
(S_ESTRUCTURA,S_ESTR_IF),
(S_ESTRUCTURA,S_ESTR_WHILE),
(S_ESTR_IF,S_ESTR_IF1,T_LLAVE_FIN),
(S_ESTR_IF,S_ESTR_IF1,T_LLAVE_FIN,T_ELSE,S_BLOQUE),
(S_ESTR_IF1,T_IF,S_EXPRESION,T_LLAVE_INICIO,S_PROGRAMA),
(S_ESTR_IF1,S_ESTR_IF1,S_PROGRAMA),
(S_ESTR_WHILE,S_ESTR_WHILE1,T_LLAVE_FIN),
(S_ESTR_WHILE1,T_WHILE,S_EXPRESION,T_LLAVE_INICIO,S_PROGRAMA),
(S_ESTR_WHILE1,S_ESTR_WHILE1,S_PROGRAMA)
]
Se decidi utilizar este tipo de estructura para las producciones debido a la facilidad en Python para
realizar bsquedas por tuplas. Por lo tanto, es sencillo comparar con particiones de tuplas, en este caso
en todos los elementos menos el primero (parte derecha de la produccin) y a su vez iterar en cada tupla
de la lista.
Es un diccionario (similar al formato JSON) que contiene el estado de aceptacin en su primer elemento,
la cantidad de iteraciones que fueron necesarias en la evaluacin sintctica, los tokens ledos por el
analizador lxico y los errores correspondientes. Recordamos que el error_lxico hace referencia a un
token invlido, el cual se especifica con su ubicacin en el campo detalle_error. El resultado del anlisis
sintctico se refleja en el estado de aceptacin del autmata donde, en caso de ser falso, se devolvern
en el detalle_error los tokens que no han sido derivados correctamente hasta el smbolo inicial.
Si el lenguaje es aceptado, existe la opcin de generar y almacenar en un archivo de imagen el rbol de
derivacin sintctico. Es una alternativa muy til para representar de forma grfica el proceso utilizado
para reconocer un lenguaje.
- archivo: Ruta del archivo con el lenguaje a analizar. Se configura en la instanciacin del objeto Analizador.
- mostrar_anlisis: Si est en True, imprime en consola el proceso de anlisis sintctico.
- token_id: Determina el id para el prximo token ledo
- arbol_sintactico: rbol sintctico generado luego del anlisis
- archivo_arbol: Nombre de archivo de imagen png para guardar el rbol de derivacin
def ejecutar(self):
# Analizar el cdigo fuente JS
# Lexer
self.analizar_lexico()
# Parser
if self.mostrar_analisis:
print "\n### Anlisis Sintctico ###\n"
self.analizar_sintaxis()
return self.resultado
LEXER
Para definir y determinar qu lexemas son vlidos se pueden diferenciar en dos clases. Los prefijados,
donde el valor de la cadena es exactamente el nico valor aceptado para ese lexema, o las expresiones
regulares que representan lexemas con distintas combinaciones de smbolos terminales. Por ende, se
especificaron dos listas de tuplas que representan los lexemas y as permiten generar los tokens con su
smbolo terminal asociado. Como se mencion anteriormente, cada tupla est compuesta por el patrn
de aceptacin del lexema (sea fijo, o una expresin regular) y por el smbolo terminal correspondiente.
Entonces, se tienen dos variables que representan listas de tokens fijos o de expresiones. Estas listas son
consultadas por el mtodo que valida y agrega un token al listado de tokens ledos.
agregar_token
def __agregar_token(self,valor_token,linea,columna):
token_valido = False
El mtodo previo primero valida si el token ledo es fijo, o sea, si corresponde a alguno de los smbolos
terminales definidos en la gramtica. En caso de no encontrar validez, se prueba con los autmatas finitos
deterministas si la cadena leda es aceptada, dando lugar as a una expresin regular vlida.
analizar_lxico
Si bien hasta ahora se vi cmo se valida un token particular, el analizador lxico se basa en dos mtodos
principalmente. El primero llamado analizar_lexico cumple la funcin de abrir el archivo de entrada y leer
lnea a lnea su contenido. Para cada lnea, se generan los tokens vlidos llamado al mtodo
generar_tokens.
def analizar_lexico(self):
# Lee el archivo fuente y general los tokens
try:
fh = open(self.archivo, 'r')
numero_linea = 1
fh.close()
self.resultado['tokens_leidos'] = len(self.tokens)
except IOError as e:
print "\n*** Error al intentar leer el archivo fuente ***"
generar_tokens
El mtodo generar_tokens es donde se analiza la entrada para dividir los lexemas y analizar si
corresponden a tokens vlidos. Lo primero que hace es descartar los espacios en blanco previos a un
carcter y descarta los comentarios de lnea. Luego debe controlar si est leyendo el inicio de una cadena,
lo cual implicara que todo lo ledo sea aceptado y sin descarte hasta recibir la comilla de finalizacin de la
misma.
def __generar_tokens(self,entrada,linea):
tokens_leidos = False
posicion = 0
pos_maxima = len(entrada)
while (posicion < pos_maxima and not self.resultado['error_lexico']):
# Avanza en la entrada descartando espacios
while entrada[posicion].isspace():
posicion += 1
# Descartando comentarios
if (posicion < pos_maxima-1 and entrada[posicion] == '/' and entrada[posicion+1] == '/'):
posicion = pos_maxima
else:
# Si el caracter inicial de este token es una comilla => Cadena
if entrada[posicion_inicial] == self.comilla:
posicion += 1
while (posicion < pos_maxima and entrada[posicion] != self.comilla):
posicion += 1
if not ( posicion < pos_maxima and entrada[posicion] == self.comilla and \
self.__agregar_token(entrada[posicion_inicial:posicion+1],linea,posicion) ):
self.resultado['error_lexico'] = True
self.resultado['detalle_error'] =
self.__error_lexer(entrada[posicion_inicial:posicion],linea,posicion)
else:
tokens_leidos = True
# Avanza en la entrada hasta encontrar un espacio en blanco, un terminador o un
separador (token)
while (posicion < pos_maxima and entrada[posicion] not in self.delimitadores and
not entrada[posicion].isspace()):
posicion += 1
self.resultado['error_lexico'] = True
self.resultado['detalle_error'] =
self.__error_lexer(entrada[posicion_inicial:posicion],linea,posicion)
posicion += 1
Caso contrario, se comienza a leer de a un caracter hasta encontrar algn delimitador, los cuales se
encuentran en la lista delimitadores para su fcil deteccin en Python. Por eso mismo se utilizan
tambin otras variables de apoyo para la deteccin de tokens.
Estas variables de apoyo slo son tiles en la lectura de caracteres para detectar tokens o delimitadores,
por lo cual no es necesario el smbolo terminal asociado, sino slo su valor como caracter. Por eso mismo
se ve que slo se concatenan los primeros elementos de las tuplas de tokens que corresponden al valor
literal del mismo.
Para cada token delimitado es tambin importante detectar si se ley ms de un caracter, ya que de ser
as se debe considerar el ltimo ledo para saber si corresponde a otro token, tal como se vio
previamente.
Otro punto a considerar es el lookahead selectivo. Aqu se controla la posibilidad de que existan tokens
compuestos por dos o ms tokens individuales. Esto se valida con el mtodo es_token_compuesto.
def __es_token_compuesto(self,entrada):
# Para analizar si se lee un 2do token (lookahead)
return entrada[0:2] in self.tokens_compuestos
De ser verdadero su retorno, se lee el siguiente caracter y se agrega un nico token conteniendo el
caracter actual y prximo. Es por eso que el lookahead es slo de 1 caracter siguiente por el momento.
Finalmente, al finalizar el anlisis de todos los elementos de la lnea y si el ltimo token registrado no
corresponde a uno de los terminadores de lnea posibles, entonces el lexer agrega un terminador ; para
resguardar el correcto anlisis sintctico que continuar.
PARSER
Lookahead
selectivo
La primera operacin responde a la transicin de shift o desplazamiento. Se lee un token, por lo tanto se
apila en la pila del autmata. Por ejemplo, luego de leer dos tokens la pila queda de esta forma:
VAR ID ...
ID Iteraciones: 2
VAR
Z0
En este caso se carg slo un token por vez, ya que el lookahead no determin carga del prximo, pero
hubiera sido posible que s ocurriera. Los casos de carga de token siguiente son dependiendo del token
ledo actual. Es por ello que es llamado lookahead selectivo, debido a que no siempre se consulta el
siguiente. La tabla a continuacin define los casos donde se cargan dos tokens aplicando el lookahead:
Estas situaciones son importantes detectarlas con lookahead porque permiten ahorrar tiempo de
ejecucin en pruebas de derivaciones que no son tiles. Teniendo el siguiente token, se puede gracias a
una adaptacin en la gramtica detectar con cierto determinismo qu regla aplicar. Por eso mismo, la
primera reduccin se realiza sin intentar reducir el ltimo token ledo de forma individual. Esto permite
entonces validar primero si existe una produccin que aplique utilizando el token como smbolo terminal
en conjunto con el resto de los smbolos que estaban cargados en la pila.
Lookahead selectivo
Tokens
ID =
= Iteraciones: 1
ID
Z0
analizar_sintaxis
La implementacin est segmentada en los pasos expuestos previamente, de forma que sea de fcil
interpretacin. El mtodo analizar_sintaxis primero toma el primer token de la lista devuelta por el lexer y
lo agrega a la pila del autmata, luego el proceso realiza las dos acciones de reduccin para luego volver a
leer el prximo token.
def analizar_sintaxis(self):
# Analiza los tokens generados por el lexer
actual = 0
pila = Pila()
# Se agrega el token leido actual (como nodo 'hoja' para armar el arbol sintactico)
produccion_candidata = [ Arbol(self.tokens[actual]) ]
if self.mostrar_analisis:
print "\n- Tokens Leidos: '" + self.tokens[actual]['valor'] + "': " +
self.tokens[actual]['simbolo'],
if self.mostrar_analisis:
print ". Resultado del paso 1:", [x.dato['simbolo'] for x in
produccion_candidata]
# Segunda Reduccin (si el simbolo arriba en la pila es reducible, se hace una nueva
pasada)
if produccion_candidata[-1].dato['simbolo'] != self.S_PROGRAMA:
produccion_candidata2 = []
while produccion_candidata:
produccion_candidata2.insert(0,produccion_candidata.pop())
produccion_candidata2 = self.__reducir_tokens(produccion_candidata2)
else:
produccion_candidata2 = produccion_candidata
if self.mostrar_analisis:
print ". Resultado del paso 2:", [x.dato['simbolo'] for x in
produccion_candidata2]
actual += 1
# Si la pila del autmata slo contiene el smbolo inicial <programa> => El lenguaje es
aceptado
if not pila.esVacia():
elemento = pila.desapilar()
if elemento.dato['simbolo'] == self.S_PROGRAMA and pila.esVacia():
self.resultado['aceptado'] = True
# Si el resultado es afirmativo => se asigna el elemento inicial como nodo padre
el arbol sintactico
self.arbol_sintactico = elemento
else:
terminales = []
self.__obtener_terminales(elemento,terminales)
self.resultado['detalle_error'] = terminales
while not pila.esVacia():
elemento = pila.desapilar()
if elemento.dato['simbolo'] != self.S_PROGRAMA:
terminales = []
self.__obtener_terminales(elemento,terminales)
self.resultado['detalle_error'] = terminales
reducir_tokens
El proceso de reduccin entonces se realiza comparando el elemento superior de la pila con las partes
derechas de las producciones, luego se vuelve a sacar el elemento superior de la pila, pero esta vez se
comparan las partes derechas con el elemento previo y el actual. En caso de coincidir alguna regla, se
inserta en la pila el no terminal de la parte izquierda. Esto ocurre hasta que la pila est vaca, donde se
volvern a cargar toda la concatenacin de smbolos resultantes nuevamente en la pila.
Reduccin
=
ID ASIG1
VAR VAR DV1
Z0 Z0 Z0
El segundo proceso de reduccin vuelve a realizar lo mismo pero primero empezando con el elemento
superior de la pila nicamente. La diferencia con el primer paso es que el smbolo ledo no es reducido
individualmente, es decir, si se lee un IDENTIFICADOR no se compara con las reglas si existe una parte
derecha para IDENTIFICADOR, porque inmediatamente lo reducira a TRMINO -> EXPRESIN, lo cual no
siempre es aplicable. Para manejar este indeterminismo, se intenta reducir primero IDENTIFICADOR junto
con el primer smbolo que estaba en la pila. Por eso mismo, existe una regla con parte derecha VAR
IDENTIFICADOR que reduce a un smbolo no terminal llamado DECLARACIN VARIABLE. De esa manera,
se garantiza que al leer el identificador, en la primera reduccin quedar como tal y no como EXPRESIN.
El mtodo que realiza la iteracin de reducciones de una lista de smbolos hasta que no existan
reducciones posibles se llama reducir_tokens.
def __reducir_tokens(self,produccion):
# Se intenta reducir la produccin de smbolos a su smbolo No Terminal del lado
izquierdo
reduccion = True
while reduccion:
try:
# Se reduce al no_terminal
encontrado = next(x for x in self.producciones if x[1:len(x)] ==
tuple([n.dato['simbolo'] for n in produccion]))[0]
if self.mostrar_analisis:
print " ** Reduccion: " + str([x.dato['simbolo'] for x in produccion]) + " -
> " + encontrado
# Se encontro una regla que coincide en su parte derecha, se reemplaza por el no
terminal a la izquierda
produccion = [ self.__nuevo_no_terminal(encontrado,produccion) ]
except StopIteration:
# No se encontro produccion que coincida
#pass
reduccion = False
# Para registrar el costo estimado del parser
self.resultado['iteraciones'] += 1
return produccion
En este caso fue muy prctica la funcin nativa de Python llamada next que permite hacer bsqueda por
elementos de una lista. Para esta situacin, la bsqueda se basa en la comparacin de la lista de smbolos
actual (produccin) con cada una de las tuplas de producciones, pero slo los elementos menos el
primero (slo los smbolos de la parte derecha). Si la produccin coincide con alguna de las tuplas, se
reemplaza por el primer elemento de dicha tupla ([0], la parte izquierda de la regla).
Si bien se ha hecho uso de una pila para almacenar los smbolos resultantes de cada iteracin, como as
tambin de otra pila para utilizar en la comparacin de cada lista de smbolos contra las producciones, en
realidad el modelo terico sigue siendo un autmata de pila, tal como se demostr al principio. La
implementacin con listas o pilas de soporte, se realiz para mejorar la eficiencia y permitir una mejor
expresin en el algoritmo.
Este mtodo tiene una funcin adicional muy importante. A medida que reduce un conjunto de smbolos,
el nuevo que representa la parte izquierda de la produccin coincidente es generado como un nodo de
rbol n-ario. A este nuevo nodo se le agregan como hijos los smbolos reducidos que pertenecen a la
parte derecha de la produccin, lo cual permite tener la estructura de rbol de derivacin para dicha
regla particular con los smbolos especficos. Entonces, en realidad lo que se ir apilando son estructuras
de rboles que representan a los nodos superiores de las derivaciones ya realizadas. Por lo tanto, al
finalizar la ltima reduccin al smbolo inicial, se dispondr de un rbol sintctico completo que incluir
todas las derivaciones realizadas para llegar a dicho smbolo. Es as que luego es posible devolver dicha
estructura para futuros anlisis.
Existen 3 mtodos para representar el rbol sintctico resultante que slo es generado si el lenguaje es
aceptado.
EJEMPLO DE EJECUCIN
# Includes
execfile("analizador.py")
## Instanciar Analizador ##
analizador = Analizador(archivo)
analizador.mostrar_analisis = True
## Ejecutar el anlisis ##
resultados = analizador.ejecutar()
#analizador.mostrar_tokens()
#analizador.mostrar_arbol()
#analizador.mostrar_elementos_arbol()
analizador.guardar_arbol_png()
El cdigo presentado muestra un ejemplo de ejecucin del analizador. Para exponer el resultado, se
utilizar el siguiente contenido en el archivo ledo cdigo.js.
Una vez ejecutado el ejemplo propuesto, la salida de la consola con el anlisis sintctico es la siguiente:
En ese caso, el token ledo es el parntesis de cierre, y se identifica el smbolo terminal asociado
(PARENTESIS_FIN). Luego se realizan dos pasos de reduccin, es por eso que se muestran resultados de
paso 1 y 2. Como se ver ms adelante, el paso 1 corresponde a un intento de reduccin sin considerar el
token ledo por separado. En este ejemplo, existen dos reducciones durante el primer paso, ya que
aplican las reglas de produccin para aquellos smbolos.
Las reglas asociadas son las definidas por las siguientes tuplas en la lista de producciones:
(S_EXPRESION,T_PAREN_INICIO,S_EXPRESION,T_PAREN_FIN)
(S_ASIG,S_ASIG,T_OP_ARITMETICO,S_EXPRESION)
Puede observarse cmo se reemplazan los smbolos que coinciden a la derecha de la produccin con su
no terminal a la izquierda.
El resultado devuelve la aceptacin del lenguaje, junto con la cantidad de iteraciones necesarias, las
cuales son dependientes de la cantidad de tokens ledos. En este caso, dados 17 tokens de entrada, se
debieron realizar 102 iteraciones, lo cual determina el coste lineal del parser para esta situacin.
El rbol sintctico generado se almacen en una imagen ya que se llam al mtodo guardar_arbol_png().
Los smbolos terminales se representan en color azul y su contenido hace referencia al id, smbolo,
token, mientras que los no terminales se representan en color rojo con su id y smbolo. El archivo del
ejemplo se muestra a continuacin.
EFICIENCIA
Como se ha mencionado en puntos previos, siempre se intent tener control sobre el rendimiento del
analizador. Quiz la forma ms natural de buscar la solucin se basa en el anlisis recursivo intentando
diferentes derivaciones hasta encontrar una aceptada o devolver el error. En cambio, se intent elaborar
una implementacin que no superara el orden cuadrtico en costos de tiempo.
Entonces, analizando simplemente de forma muy bsica las iteraciones necesarias para los casos
extremos, podemos ver que no se supera dicho orden. Estos casos corresponden a situaciones donde los
smbolos ledos no son reducidos por ninguna regla de produccin, teniendo as un incremento en la
longitud de la pila del autmata.
Analizando cada llamada de reduccin que evala una comparacin para cada smbolo de la pila, se
puede ver que se tendrn dos iteraciones para cada token. Es decir, la variable independiente ser el
token. Entonces, habiendo ledo 1 token, slo se produce una comparacin, la de la reduccin del paso 2.
Con la lectura de 2 tokens, se realizan 4 comparaciones: 1 correspondiente a la primera iteracin del
primer token, 1 correspondiente al paso 1 del segundo y 2 correspondientes al paso 2 del ltimo tambin.
Como se ve, el patrn siempre es el mismo para el caso extremo. El clculo de iteraciones resulta en X2.
1 0 1 1 1
2 1 2 3 4
3 2 3 5 9
4 3 4 7 16
5 4 5 9 25
X X-1 X 2.X - 1 X2
Obviamente, slo se contempl la iteracin para los casos de reduccin que son los que pueden llegar a
crecer de forma importante respecto al resto de las operaciones del algoritmo. Afortunadamente, para la
mayora de los casos probados, los tiempos siempre han sido del orden lineal y slo en casos
expresamente forzados se logr observar el comportamiento extremo en costos de tiempo cercanos al
orden cuadrtico.
CONCLUSIONES
En primer lugar quisiera expresar algunas experiencias obtenidas en la confeccin de este trabajo. El
objetivo fijado previamente para reconocer el lenguaje de programacin JavaScript, parti de una
curiosidad y necesidad profesional. Ante la posibilidad de interpretar una gramtica libre de contexto a
travs de autmatas, pareci inicialmente un desafo interesante pero no muy alejado de lo posible. Esta
consideracin inicial, luego result no ser completamente cierta.
Al comenzar la investigacin sobre cmo se puede hacer un anlisis sintctico de un lenguaje de
programacin, me encontr con el primer problema. Los lenguajes de programacin en general no son
generados por una gramtica libre de contexto, porque en realidad s requieren de conocer el contexto.
Sea el caso de saber si una variable fue definida con antelacin, o si un identificador ya fue utilizado como
declaracin previamente bajo el mismo scope. Estos casos hacen que sea imposible generar un lenguaje
con gramticas de tipo 2. Fue aqu donde se comenz con la bsqueda por los mtodos que utilizan los
compiladores. Al diferenciar los pasos en anlisis lxico, sintctico y semntico, el compilador puede
realizar validaciones en distintos niveles del lenguaje de entrada.
Esta separacin conceptual result de gran importancia para interpretar la implementacin del analizador
sintctico, porque para resolver la aceptacin del lenguaje se estableci como lmite exclusivamente a su
estructura sintctica, sin contemplar la semntica. Por lo tanto, lenguaje que genera la gramtica libre de
contexto propuesta, podra decirse que tiene propiedades sintcticas similares a Javascript, pero carece
de algunas consideraciones semnticas en su estructura, lo cual lo hace un lenguaje distinto.
De todas maneras, an ha sido muy complejo definir la gramtica apropiada para hacer el anlisis. Al
principio, supuse que utilizar una gramtica bien expresiva, de fcil interpretacin, con sus smbolos
reutilizables, intentando evitar la ambigedad y definiendo un enfoque descendente, me permitira
implementar un parser sencillo y rpido de ejecucin. La conclusin, luego de varios intentos de definir la
gramtica y encontrarme con situaciones donde el indeterminismo se converta en un problema
irresoluble, fue que deba antes incorporar conocimiento sobre las tcnicas conocidas de anlisis lxico y
sintctico.
Las diversas lecturas y comprensin de algunos textos me llevaron a entender que no todas las
gramticas pueden analizarse con una tcnica universal. Cada una de las tcnicas de anlisis, tiene sus
restricciones en cmo est escrita la gramtica. Es por ello que en varios casos se deban modificar las
producciones para adecuarlas al tipo de tcnica escogida. Por ejemplo, las gramticas que son analizadas
con parsers LL, requieren no ser ambiguas y recursivas a izquierda, lo cual genera un problema para las
gramticas inherentemente ambiguas que no podrn ser analizadas con dicho parser. Este tipo de
restricciones dan pauta para definir una gramtica, que no necesariamente debe ser la ms expresiva,
sino que estar ligada al tipo de tcnica utilizada para analizar el lenguaje.
Ha cobrado tambin importante relevancia la implementacin del autmata. La idea original se basaba en
replicar el procesamiento del autmata de pila con todas sus transiciones explcitas, resolviendo
indeterminaciones con recursividad. Este tipo de implementacin s era vlida para el AFD que acepta los
lenguajes regulares, pero para el caso de una gramtica compleja resultaba muy difcil de construir y,
posiblemente, ni siquiera era factible. Por lo cual, interiorizando el concepto de implementar autmatas
de pila que conceptualmente se construan con 4 estados, fue ms accesible llegar a disear el actual. De
esa manera, no era necesario plasmar cada produccin como una transicin para determinar la
aceptacin, sino que poda utilizarse un modelo que aplique para todas las producciones posibles y as
implementarlo de forma general. Este enfoque ha sido un diferencial importante al momento del diseo
final de la gramtica y el autmata.
Si bien el objetivo logrado ha sido satisfactorio, debe destacarse que el resultado del analizador sintctico
posee una desventaja frente a otros analizadores estudiados. El problema radica en la modificacin o
adaptacin de la gramtica que permite la implementacin del autmata propuesto. Al incorporar nuevas
producciones que reemplazaban algunas otras que provocaban ciertos indeterminismos durante la
ejecucin del autmata, se debi resignar expresividad en la gramtica. Esto significa que debieron
utilizarse smbolos no terminales que representaban de forma parcial un concepto semntico o una
estructura sintctica de la gramtica formal. El resultado de dicha decisin permiti obtener tiempos de
anlisis lineales, pero el rbol de derivacin no siempre expresa en cada nodo un concepto sintctico
completo. Esta desventaja impactar en el anlisis posterior semntico. La solucin estar en reemplazar
dichos nodos que representan los smbolos no terminales incompletos o de soporte, por aquellos que s
lo son.
Entonces logrando sumar las herramientas adquiridas sobre la teora de gramticas, autmatas y
lenguajes, junto con el anlisis ascendente, descendente, LL, LR, etc, se ha encontrado una solucin al
problema propuesto. Probablemente no sea la mejor solucin posible para esta situacin, pero asumo
que es una opcin vlida, como tantas otras, de evaluar mediante un autmata de pila la aceptacin de
un lenguaje generado por una gramtica libre de contexto.
Me llevo as un interesante y atractivo desafo para continuar en el futuro. Quiz extender la limitacin
del lenguaje actual, o bien adaptar el rbol de anlisis sintctico resultante para incorporarlo al siguiente
paso de anlisis semntico. Seguramente sern actividades de gran valor para la adquisicin de nuevos
conceptos tericos y prcticos.
BIBLIOGRAFA