Você está na página 1de 36

LENGUAJES FORMALES

TRABAJO DE INVESTIGACIN

Anlisis Sintctico del lenguaje JavaScript utilizando teora de autmatas

Autor Aprea, Mariano

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

Lenguajes Formales | Universidad Caece 2


Anlisis Sintctico del lenguaje JavaScript utilizando teora de autmatas

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

La implementacin a desarrollar considerar los primeros pasos en la compilacin de un lenguaje de


programacin, donde slo se resolver el anlisis lxico y sintctico de un cdigo fuente dado. A su vez, se
reducir el dominio de la especificacin del lenguaje para hacer foco en el objetivo pautado y
cumplimentarlo en tiempo.
Se intentar evitar el uso de tcnicas predefinidas para el anlisis sintctico.
Las consideraciones semnticas no sern parte del alcance inicial.

Lenguajes Formales | Universidad Caece 3


Anlisis Sintctico del lenguaje JavaScript utilizando teora de autmatas

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

Imperativo y Estructurado: El programa se compone de una sucesin de instrucciones o sentencias, las


cuales se ejecutan en orden y cambian el estado del mismo; son terminadas con el carcter ;. Dicho
terminador es posible obviarlo ya que JavaScript puede detectar el final de lnea y as insertar el
terminador, si fuera necesario. Utiliza estructuras similares a C, que pueden encontrarse encadenadas.

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

No se consideran los espacios en blanco y las lneas vacas


Es case-sensitive, por lo cual se diferencian las maysculas y minsculas
No se define el tipo de variables
Se pueden incluir comentarios

Palabras reservadas

El lenguaje especifica un conjunto de palabras reservadas que no pueden utilizarse como identificadores
de variables dentro del programa. Las mismas son:

Lenguajes Formales | Universidad Caece 4


Anlisis Sintctico del lenguaje JavaScript utilizando teora de autmatas

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

Tambin se tendrn en cuenta las siguientes consideraciones:

- Los bloques de sentencias sern delimitados por { y }


- Las estructuras deben siempre delimitar sus bloques con llaves
- Los tipos de variables aceptados estn limitados por los literales definidos, no se aceptan otros
tipos (vectores, json, objetos, etc)
- Los comentarios aceptados slo son los de 1 lnea definidos a partir de //
- La declaracin de variables puede ser mltiple (separadas por ,) y con asignacin inicial

Lenguajes Formales | Universidad Caece 5


Anlisis Sintctico del lenguaje JavaScript utilizando teora de autmatas

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.

Por ejemplo, analizamos el caso de la asignacin

<asignacin> ::= <identificador> <operador_asignacin> <expresin>

Luego, si consideramos una expresin que simplemente devuelve el contenido de la variable

<expresin> ::= <identificador>

Aqu encontramos un problema de indeterminacin ya que el autmata no podra definir si el


identificador debe ser tratado como una expresin o como un identificador en s. En realidad, el
autmata no puede determinar qu regla de produccin aplicar. En ciertos casos es posible ajustar la
gramtica para permitir que el autmata pila la resuelva, es decir, manteniendo el grado de la misma y el
indeterminismo, pero logrando en la implementacin misma evitar la recursividad mediante el agregado
de condiciones particulares para cada caso de indeterminismo. En otros casos, es necesario cambiar las
producciones para adecuar la restriccin del contexto a una gramtica libre del mismo. Y finalmente
existen situaciones donde simplemente no es necesario modificarla, haciendo posible implementar el
autmata correspondiente con una tcnica de prueba y error recursiva que intente todas las alternativas
ante casos de indeterminacin.

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

Lenguajes Formales | Universidad Caece 6


Anlisis Sintctico del lenguaje JavaScript utilizando teora de autmatas

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:

<asignacin1> ::= <identificador> <operador_asignacin>

<asignacin> ::= < asignacin1> <expresin>

<expresin> ::= <identificador>

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:

<expresin> ::= <expresin> <operador_aritmtico> <trmino>

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.

Lenguajes Formales | Universidad Caece 7


Anlisis Sintctico del lenguaje JavaScript utilizando teora de autmatas

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

La gramtica escrita para generar el lenguaje propuesto mediante el algoritmo a implementar es la


siguiente:

G: Gramtica de tipo 2
G = { Vn, Vt, P, S }

S - Smbolo inicial: <programa>

Vocabulario No Terminal

Vn = { <programa>, <asignacin >, <asignacin1 >, <bloque >, <condicin>, <comparacin>,


<expresin>, <estructura>, <estructura_if>, <estructura_if1>, <estructura_while>,
<estructura_while1>, <declaracin_var>, <declaracin_var1>, <declaracin_var2>,
<declaracin_funcin>, <declaracin_funcin1>, <llamada>, <llamada1>, <sentencia_return>,
<sentencia>, <trmino>, <literal> }

Vocabulario Terminal

Vt = { <terminador>, <comparador>, <op_lgico>, <op_aritmtico>, <op_asignacin>,


<identificador>, <nmero>, <cadena>, <var>, <function>, <if>, <else>, <while>, <return>,
<llave_inicio>, <llave_fin>, <paren_inicio>, <paren_fin>, <coma> }

<terminador> ::= ;
<coma> ::= ,
<comparador> ::= > | < | <operador_asignacin> <operador_asignacin> | >= | <=
<operador_lgico> ::= && | ||
<operador_aritmtico> ::= + | - | * | /
<operador_asignacin> ::= =
<llave_inicio> ::= {
<llave_fin> ::= }
<parntesis_inicio> ::= (
< parntesis _fin> ::= )

Lenguajes Formales | Universidad Caece 8


Anlisis Sintctico del lenguaje JavaScript utilizando teora de autmatas

<var> ::= var


<return> ::= return
<if> ::= if
<else> ::= else
<while> ::= while
<function> ::= function

Producciones

<programa> ::= <programa> <sentencia>


<programa> <programa>
<sentencia>
<sentencia> ::= <declaracin_funcion>
<declaracin_var> <terminador>
<declaracin_var2> <terminador>
<asignacin> <terminador>
<expresin> <terminador>
<setencia_return> <terminador>
<estructura>
<declaracin_var> ::= <var> <identificador>
<declaracin_var> <coma> <identificador>
<declaracin_var2> <coma> <identificador>
<declaracin_var1> ::= <var> <asignacin1>
<declaracin_var> <coma> <asignacin1>
<declaracin_var2> <coma> <asignacin1>
<declaracin_var2> ::= <declaracin_var1> <expresin>
<declaracin_var2> <op_aritmtico> <expresin>
<asignacin> ::= <asignacin1> <expresin>
<asignacin> <op_aritmtico> <expresin>
<asignacin1> ::= <identificador> <op_asignacin>
<declaracin_funcin> ::= <function> <identificador> <paren_inicio> <paren_fin> <bloque>
<declaracin_funcin1> <paren_fin > <bloque>
<declaracin_funcin1> ::= <function> <identificador> <paren_inicio> <identificador>
<declaracin_funcin1> <declaracin_funcin1> <coma> <identificador>
<bloque> ::= <llave_inicio> <programa> <llave_fin>
<llave_inicio> <llave_fin>
<expresin> ::= <paren_inicio> <paren_fin>

Lenguajes Formales | Universidad Caece 9


Anlisis Sintctico del lenguaje JavaScript utilizando teora de autmatas

<expresin> <op_aritmtico> <expresin>


<trmino>
<trmino> ::= <identificador>
<llamada>
<condicin>
<conparacin>
<literal>
<condicin> ::= <expresin> <op_lgico> <expresin>
<comparacin> ::= <expresin> <comparador > <expresin>
<sentencia_return> ::= <return> <expresin>
<sentencia_return> <op_aritmtico> <expresin>
<llamada> ::= <identificador> <paren_inicio> <paren_fin>
<llamada1> <paren_fin>
<llamada1> ::= <identificador> <paren_inicio> <expresin>
<llamada1> <op_aritmtico> <expresin>
<llamada1> <coma> <expresin>
<estructura> ::= <estructura_if>
<estructura_while>
<estructura_if> ::= <estructura_if1> <llave_fin>
<estructura_if1> <llave_fin> <else> <bloque>
<estructura_if1> ::= <if> <expresin> <llave_inicio> <programa>
<estructura_if1> <programa>
<estructura_while> ::= <estructura_while1> <llave_fin>
<estructura_while1> ::= <while> <expresin> <llave_inicio> <programa>
<estructura_while1> <programa>
<literal> ::= <entero>
<flotante>
<cadena>

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.

Lenguajes Formales | Universidad Caece 10


Anlisis Sintctico del lenguaje JavaScript utilizando teora de autmatas

<identificador> = (<letra>)+ (<letra>|<numero>|_)*


<entero> ::= (<nmero>)+
<flotante> ::= <entero> . <entero>
<cadena> ::= . (<letra> | <nmero> | <smbolo>)* .
<letra> ::= a | A | b | B | | z | Z
<nmero> ::= 1| 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0
<smbolo> ::= - | _ | . | : | , | ; | + | = | # | $ | % | & | / | ( | ) | > | <

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

Anlisis Lxico Anlisis Sintctico Anlisis Semntico

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:

<comentario > ::= // <cadena>

Lenguajes Formales | Universidad Caece 11


Anlisis Sintctico del lenguaje JavaScript utilizando teora de autmatas

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:

Id Valor Smbolo Tipo Lnea Columna


233 id_prueba1 IDENTIFICADOR terminal 3 10

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;

IDENTIFICADOR No es token IDENTIFICADOR TERMINADOR

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.

AFD: Identificador AFD: Nmero AFD: Cadena


letra dgito
dgito dgito dgito letra
smbolo
letra dgito . comilla comilla
q0 q1 q0 q1 q2 q0 q1 q2

Lenguajes Formales | Universidad Caece 12


Anlisis Sintctico del lenguaje JavaScript utilizando teora de autmatas

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.

El proceso de derivacin entonces sera como est representado en el siguiente ejemplo.

Cadena a derivar: var id_prueba1 = prueba ;

Anlisis Lxico -> Tokens

VAR IDENTIFICADOR OP_ASIGNACIN CADENA TERMINADOR

Anlisis Sintctico

SENTENCIA

DECLARACIN
VARIABLE2

EXPRESIN
DECLARACIN
VARIABLE1
TRMINO

ASIGNACIN1
LITERAL

VAR IDENTIFICADOR OP_ASIGNACIN CADENA TERMINADOR


(lookahead)

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

Lenguajes Formales | Universidad Caece 13


Anlisis Sintctico del lenguaje JavaScript utilizando teora de autmatas

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.

Por qu es necesario el lookahead? En este ejemplo se puede ver la justificacin. Si al leer el


IDENTIFICADOR no se leyera el siguiente, entonces se reducira el IDENTIFICADOR hasta EXPRESIN
porque existen producciones que lo permiten, de forma anloga a cmo ocurri con el token CADENA.
Entonces, quedaran dos smbolos en la pila: VAR EXPRESIN. Esta combinacin de smbolos no se
contempla, ni debera hacerse, en la gramtica. Por lo tanto, es necesario antes de reducir el
IDENTIFICADOR, detectar si se debe leer como tal, como una llamada a funcin o expresin. Es por ello
que se utiliza lookahead. No es la nica forma de resolverlo, ya que podra utilizarse recursividad y
backtracking para probar diferentes derivaciones, pero de esta forma se logra mejorar el rendimiento del
algoritmo en tiempo y espacio.

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.

Representacin del autmata de Pila del Parser

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

La transicin t, | t es la correspondiente a la accin de desplazamiento (shift) donde al leer un token, se lo


agrega a la pila. Las transiciones , pd | y , | pi representan de forma genrica a cmo se reemplaza un
conjunto de smbolos que se encuentran en la parte superior de la pila por el smbolo de la parte
izquierda de la regla de produccin de la gramtica que coincide con la parte derecha, lo que sera la
operacin de reduccin (reduce). Esta transicin en realidad es una representacin de una sucesin de
transiciones y estados para cada produccin definida en la gramtica. Por lo tanto, en el grfico siguiente
se realizar a modo de ejemplo para demostrar cmo sera la construccin del autmata formalmente
para algunas producciones ya vistas.

Lenguajes Formales | Universidad Caece 14


Anlisis Sintctico del lenguaje JavaScript utilizando teora de autmatas

AP: Declaracin de variable con asignacin (DV2)


Cadena a analizar: var id1 = 1

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

, O | , | A1 Smbolos terminales Smbolos no terminales


q5 q6 I: Identificador A1: Asignacin1
, DV1 | O: Operador Asignacin DV1: Declaracin Variable1
q2 N: Nmero DV2: Declaracin Variable2
q1 V: var E: Expresin
, I | , | DV1
, A1 | T: Trmino
L: Literal
q3 q4 S: Programa
, V |

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.

Lenguajes Formales | Universidad Caece 15


Anlisis Sintctico del lenguaje JavaScript utilizando teora de autmatas

IMPLEMENTACIN

El lenguaje de programacin utilizado para implementar la solucin analizada es Python. La decisin de


utilizar el mismo se basa en la simplicidad para operar con colecciones de datos como los diccionarios y
listas que facilitan la codificacin del autmata. A su vez, es un lenguaje de fcil lectura y simple sintaxis; y
no requiere de un framework importante que pueda impactar de forma relevante en la performance de
ejecucin.

AUTMATA FINITO DETERMINISTA

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

Lenguajes Formales | Universidad Caece 16


Anlisis Sintctico del lenguaje JavaScript utilizando teora de autmatas

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:

# Configuracin de Autmatas para las ER


# AFD para identificador de literales, variables y funciones
comilla = '\''
afd_identificador = AFD()
afd_identificador.agregarTransicion(1,string.ascii_letters,2)
afd_identificador.agregarTransicion(2,string.ascii_letters + string.digits,2)
afd_identificador.agregarAceptado(2)
afd_numero = AFD()
afd_numero.agregarTransicion(1,string.digits,2)
afd_numero.agregarTransicion(2,string.digits,2)
afd_numero.agregarTransicion(2,'.',3)
afd_numero.agregarTransicion(3,string.digits,3)
afd_numero.agregarAceptado(2)
afd_numero.agregarAceptado(3)
afd_cadena = AFD()
afd_cadena.agregarTransicion(1,comilla,2)
afd_cadena.agregarTransicion(2,string.printable,2)
afd_cadena.agregarTransicion(2,comilla,3)
afd_cadena.agregarAceptado(3)

tokens_expresiones = [ (afd_identificador, T_ID), (afd_numero, T_NUMERO), (afd_cadena,


T_CADENA) ]

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'

Lenguajes Formales | Universidad Caece 17


Anlisis Sintctico del lenguaje JavaScript utilizando teora de autmatas

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'

# Simbolos No Terminales de las producciones


S_PROGRAMA = 'PROGRAMA'
S_ASIG = 'ASIGNACION'
S_ASIG1 = 'ASIGNACION1'
S_BLOQUE = 'BLOQUE'
S_CONDICION = 'CONDICION'
S_COMPARACION = 'COMPARACION'
S_EXPRESION = 'EXPRESION'
S_ESTRUCTURA = 'ESTRUCTURA'
S_ESTR_IF = 'ESTRUCTURA_IF'
S_ESTR_IF1 = 'ESTRUCTURA_IF1'
S_ESTR_WHILE = 'ESTRUCTURA_WHILE'
S_ESTR_WHILE1 = 'ESTRUCTURA_WHILE1'
S_DV = 'DECLARACION_VARIABLE'
S_DV1 = 'DECLARACION_VARIABLE_1'
S_DV2 = 'DECLARACION_VARIABLE_2'
S_DF = 'DECLARACION_FUNCION'
S_DF1 = 'DECLARACION_FUNCION1'
S_CF = 'LLAMADA_FUNCION'
S_CF1 = 'LLAMADA_FUNCION1'
S_RETURN = 'SETENCIA_RETURN'
S_SENTENCIA = 'SENTENCIA'
S_TERMINO = 'TERMINO'
S_LITERAL = 'LITERAL'

Las producciones se implementaron en una lista de tuplas, donde cada una de ellas tiene la siguiente
representacin:

SENTENCIA ::= EXPRESIN TERMINADOR

(SENTENCIA, EXPRESIN, TERMINADOR)

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),

Lenguajes Formales | Universidad Caece 18


Anlisis Sintctico del lenguaje JavaScript utilizando teora de autmatas

(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.

El resultado devuelto por el analizador tiene la siguiente estructura:

self.resultado = { 'aceptado': False,


'iteraciones': 0,
'tokens_leidos': 0,
'error_lexico': False,
'detalle_error': {}
}

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

Lenguajes Formales | Universidad Caece 19


Anlisis Sintctico del lenguaje JavaScript utilizando teora de autmatas

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.

Existen algunas variables auxiliares que actan en la configuracin del analizador:

- 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

El mtodo principal a ejecutar es justamente el llamado ejecutar(), el cual simplemente llama al


analizador lxico y sintctico para devolver la estructura de resultado.

### Ejecucion general ###

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.

# Definicin de Tokens y su smbolo en la gramtica: ( valor, smbolo_terminal )


terminadores = ([ ';' ], T_TERMINADOR)
operadores_asignacion = ([ '=' ], T_OP_ASIGNACION)
comparadores = ([ '>', '<', '==', '>=', '<=' ], T_COMPARADOR)
operadores_aritmeticos = ([ '+', '-', '/', '*', '%' ], T_OP_ARITMETICO)
operadores_logicos = ([ '&', '|', '&&', '||' ], T_OP_LOGICO)
coma = ([ ',' ], T_COMA)
llave_inicio = ([ '{' ], T_LLAVE_INICIO)
llave_fin = ([ '}' ], T_LLAVE_FIN)
paren_inicio = ([ '(' ], T_PAREN_INICIO)
paren_fin = ([ ')' ], T_PAREN_FIN)
res_if = ([ 'if' ], T_IF)
res_else = ([ 'else' ], T_ELSE)
res_function = ([ 'function' ], T_FUNCTION)

Lenguajes Formales | Universidad Caece 20


Anlisis Sintctico del lenguaje JavaScript utilizando teora de autmatas

res_return = ([ 'return' ], T_RETURN)


res_var = ([ 'var' ], T_VAR)
res_while = ([ 'while' ],T_WHILE)

tokens_fijos = [ terminadores, operadores_asignacion, comparadores, operadores_aritmeticos,


operadores_logicos, coma, llave_inicio, llave_fin, paren_inicio, paren_fin, res_if, res_else,
res_function, res_return, res_var, res_while ]

tokens_expresiones = [ (afd_identificador, T_ID), (afd_numero, T_NUMERO), (afd_cadena,


T_CADENA) ]

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

# Validar si es token prefijado


i = 0
while (not token_valido and i < len(self.tokens_fijos)):
if valor_token in self.tokens_fijos[i][0]:
token_valido = True
self.tokens.append(
self.__nuevo_token(valor_token,self.tokens_fijos[i][1],linea,columna) )
i += 1

# Validar si es token de una Expresin Regular


i = 0
while (not token_valido and i < len(self.tokens_expresiones)):
self.tokens_expresiones[i][0].setEntrada(valor_token)
if (self.tokens_expresiones[i][0].evaluar()):
token_valido = True
self.tokens.append(
self.__nuevo_token(valor_token,self.tokens_expresiones[i][1],linea,columna) )
i += 1

# Devuelve si el token es valido o no


return token_valido

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

Lenguajes Formales | Universidad Caece 21


Anlisis Sintctico del lenguaje JavaScript utilizando teora de autmatas

# Se lee por lnea. De esta forma se elimina verificar la terminacin de sentencia


con \n
for linea in fh:
linea_limpia = linea.strip()
# Se quitan lneas vacas
if (len(linea_limpia) > 0):
# Se guardan los tokens encontrados en la linea
self.__generar_tokens(linea_limpia,numero_linea)
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

# Se marca inicio del token


posicion_inicial = posicion

# 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

# Si se avanzo desde la posicion inicial, es porque se leyo 1 token y 1


delimitador => son 2 tokens
if (posicion != posicion_inicial):
# Se agrega el token previo al delimitador
if not
self.__agregar_token(entrada[posicion_inicial:posicion],linea,posicion):

Lenguajes Formales | Universidad Caece 22


Anlisis Sintctico del lenguaje JavaScript utilizando teora de autmatas

self.resultado['error_lexico'] = True
self.resultado['detalle_error'] =
self.__error_lexer(entrada[posicion_inicial:posicion],linea,posicion)

# Se agrega el token de terminacion/separacion (si no era un espacio en blanco o


fin de linea)
if posicion < pos_maxima and not entrada[posicion].isspace():
# Lookahead para detectar tokens compuestos
if posicion+1 < pos_maxima and
self.__es_token_compuesto(entrada[posicion:posicion+2]):
res = self.__agregar_token(entrada[posicion:posicion+2],linea,posicion)
posicion += 1
else:
res = self.__agregar_token(entrada[posicion],linea,posicion)
if not res:
self.resultado['error_lexico'] = True
self.resultado['detalle_error'] =
self.__error_lexer(entrada[posicion],linea,posicion)

posicion += 1

# Si la linea termina sin ';', se agrega


if tokens_leidos and not self.resultado['error_lexico'] and self.tokens[-1]['valor'] not in
self.simbolos_terminadores:
self.__agregar_token(';',linea,posicion)

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.

# Variables de soporte para analizar tokens


operadores = operadores_asignacion[0] + comparadores[0] + operadores_aritmeticos[0] +
operadores_logicos[0]
simbolos = coma[0] + llave_inicio[0] + llave_fin[0] + paren_inicio[0] + paren_fin[0]
reservadas = res_if[0] + res_else[0] + res_function[0] + res_return[0] + res_var[0] +
res_while[0]
delimitadores = terminadores[0] + comparadores[0] + operadores + simbolos + reservadas
simbolos_terminadores = [ ';', ')', '{', '}' ]
tokens_compuestos = [ '==', '>=', '<=', '&&', '||' ]

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.

Lenguajes Formales | Universidad Caece 23


Anlisis Sintctico del lenguaje JavaScript utilizando teora de autmatas

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

El analizador sintctico se implement en dos mtodos. El principal es llamado analizar_sintaxis y su


funcin es iterar para cada token ledo que detect el lexer y realizar las siguientes acciones:

Iteracin para cada Token

Agregar Token Reduccin 1 Reduccin 2


a la Pila

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:

Agregar Token a la Pila (shift)


Tokens

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:

Token actual Token siguiente


} else
IDENTIFICADOR ( o =

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

Lenguajes Formales | Universidad Caece 24


Anlisis Sintctico del lenguaje JavaScript utilizando teora de autmatas

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()

while (not self.resultado['error_lexico'] and actual < len(self.tokens)):

# 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'],

# Lookahead (selectivo): Se analizan ciertos casos para detectar si es necesario


cargar 2 tokens en vez de 1 #
if actual+1 < len(self.tokens) and (
(self.tokens[actual]['simbolo'] == self.T_LLAVE_FIN and
self.tokens[actual+1]['simbolo'] == self.T_ELSE) or
(self.tokens[actual]['simbolo'] == self.T_ID and
self.tokens[actual+1]['simbolo'] in [ self.T_PAREN_INICIO, self.T_OP_ASIGNACION
])):
# Si el token es una '}', carga el prximo si es un 'else'
# Si el token es un ID, carga el prximo si es id() o id= (declaracin de funcin
o asignacin)
produccion_candidata.append(Arbol(self.tokens[actual+1]))
if self.mostrar_analisis:
print ", '" + self.tokens[actual+1]['valor'] + "': " +
self.tokens[actual+1]['simbolo']
actual += 1
if self.mostrar_analisis:
print "\n. Reduccion a simbolos no terminales"

# Primera Reduccin (sin reducir el token actual ledo)


while not pila.esVacia():
produccion_candidata.insert(0,pila.desapilar())
produccion_candidata = self.__reducir_tokens(produccion_candidata)

Lenguajes Formales | Universidad Caece 25


Anlisis Sintctico del lenguaje JavaScript utilizando teora de autmatas

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]

# Se devuelven a la pila los simbolos


for simb in produccion_candidata2:
pila.apilar(simb)

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.

Lenguajes Formales | Universidad Caece 26


Anlisis Sintctico del lenguaje JavaScript utilizando teora de autmatas

Reduccin

Reglas aplicadas: ASIG1 -> ID =


DV1 -> VAR ASIG1

=
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

Lenguajes Formales | Universidad Caece 27


Anlisis Sintctico del lenguaje JavaScript utilizando teora de autmatas

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.

mostrar_arbol Presenta en pantalla el rbol sintctico de forma vertical


mostrar_elementos_arbol Lista en pantalla todos los nodos del rbol, con sus datos ms relevantes
e hijos inmediatos
guardar_arbol_png Genera un archivo de imagen png con la estructura del rbol sintctico

EJEMPLO DE EJECUCIN

# -*- coding: utf8 -*-


# Script para probar el Analizador de cdigo JS
# INSTRUCCIONES:
# - Archivos necesarios a incluir: analizador.py
# - Seteo de archivo fuente al inicializar Analizador
# - Convocar mtodo ejecutar
#
# OPCIONALES:
# - Si se setea mostrar_analisis en True, imprime en shell el proceso
# - El mtodo mostrar_tokens devuelve el listado de tokens ledos
#

from pprint import pprint

# Includes
execfile("analizador.py")

## Archivo fuente a analizar ##


archivo = 'codigo.js'

## 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()

print "\n\n### RESULTADO DEL ANLISIS ###"


pprint(resultados)

Lenguajes Formales | Universidad Caece 28


Anlisis Sintctico del lenguaje JavaScript utilizando teora de autmatas

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.

var test1 = 1, test2;

test2 = test1 * (3 + test1);

Una vez ejecutado el ejemplo propuesto, la salida de la consola con el anlisis sintctico es la siguiente:

### Analisis Sintactico ###

- Tokens Leidos: 'var': VAR


. Reduccion a simbolos no terminales
. Resultado del paso 1: ['VAR']
. Resultado del paso 2: ['VAR']

- Tokens Leidos: 'test1': IDENTIFICADOR , '=': OP_ASIGNACION


. Reduccion a simbolos no terminales
. Resultado del paso 1: ['VAR', 'IDENTIFICADOR', 'OP_ASIGNACION']
** Reduccion: ['IDENTIFICADOR', 'OP_ASIGNACION'] -> ASIGNACION1
** Reduccion: ['VAR', 'ASIGNACION1'] -> DECLARACION_VARIABLE_1
. Resultado del paso 2: ['DECLARACION_VARIABLE_1']

- Tokens Leidos: '1': NUMERO


. Reduccion a simbolos no terminales
. Resultado del paso 1: ['DECLARACION_VARIABLE_1', 'NUMERO']
** Reduccion: ['NUMERO'] -> LITERAL
** Reduccion: ['LITERAL'] -> TERMINO
** Reduccion: ['TERMINO'] -> EXPRESION
** Reduccion: ['DECLARACION_VARIABLE_1', 'EXPRESION'] -> DECLARACION_VARIABLE_2
. Resultado del paso 2: ['DECLARACION_VARIABLE_2']

- Tokens Leidos: ',': COMA


. Reduccion a simbolos no terminales
. Resultado del paso 1: ['DECLARACION_VARIABLE_2', 'COMA']
. Resultado del paso 2: ['DECLARACION_VARIABLE_2', 'COMA']

- Tokens Leidos: 'test2': IDENTIFICADOR


. Reduccion a simbolos no terminales
** Reduccion: ['DECLARACION_VARIABLE_2', 'COMA', 'IDENTIFICADOR'] -> DECLARACION_VARIABLE
. Resultado del paso 1: ['DECLARACION_VARIABLE']
. Resultado del paso 2: ['DECLARACION_VARIABLE']

- Tokens Leidos: ';': TERMINADOR


. Reduccion a simbolos no terminales
** Reduccion: ['DECLARACION_VARIABLE', 'TERMINADOR'] -> SENTENCIA
** Reduccion: ['SENTENCIA'] -> PROGRAMA
. Resultado del paso 1: ['PROGRAMA']
. Resultado del paso 2: ['PROGRAMA']

- Tokens Leidos: 'test2': IDENTIFICADOR , '=': OP_ASIGNACION


. Reduccion a simbolos no terminales
. Resultado del paso 1: ['PROGRAMA', 'IDENTIFICADOR', 'OP_ASIGNACION']
** Reduccion: ['IDENTIFICADOR', 'OP_ASIGNACION'] -> ASIGNACION1
. Resultado del paso 2: ['PROGRAMA', 'ASIGNACION1']

- Tokens Leidos: 'test1': IDENTIFICADOR


. Reduccion a simbolos no terminales
. Resultado del paso 1: ['PROGRAMA', 'ASIGNACION1', 'IDENTIFICADOR']
** Reduccion: ['IDENTIFICADOR'] -> TERMINO
** Reduccion: ['TERMINO'] -> EXPRESION
** Reduccion: ['ASIGNACION1', 'EXPRESION'] -> ASIGNACION
. Resultado del paso 2: ['PROGRAMA', 'ASIGNACION']

- Tokens Leidos: '*': OP_ARITMETICO


. Reduccion a simbolos no terminales
. Resultado del paso 1: ['PROGRAMA', 'ASIGNACION', 'OP_ARITMETICO']

Lenguajes Formales | Universidad Caece 29


Anlisis Sintctico del lenguaje JavaScript utilizando teora de autmatas

. Resultado del paso 2: ['PROGRAMA', 'ASIGNACION', 'OP_ARITMETICO']

- Tokens Leidos: '(': PARENTESIS_INICIO


. Reduccion a simbolos no terminales
. Resultado del paso 1: ['PROGRAMA', 'ASIGNACION', 'OP_ARITMETICO', 'PARENTESIS_INICIO']
. Resultado del paso 2: ['PROGRAMA', 'ASIGNACION', 'OP_ARITMETICO', 'PARENTESIS_INICIO']

- Tokens Leidos: '3': NUMERO


. Reduccion a simbolos no terminales
. Resultado del paso 1: ['PROGRAMA', 'ASIGNACION', 'OP_ARITMETICO', 'PARENTESIS_INICIO',
'NUMERO']
** Reduccion: ['NUMERO'] -> LITERAL
** Reduccion: ['LITERAL'] -> TERMINO
** Reduccion: ['TERMINO'] -> EXPRESION
. Resultado del paso 2: ['PROGRAMA', 'ASIGNACION', 'OP_ARITMETICO', 'PARENTESIS_INICIO',
'EXPRESION']

- Tokens Leidos: '+': OP_ARITMETICO


. Reduccion a simbolos no terminales
. Resultado del paso 1: ['PROGRAMA', 'ASIGNACION', 'OP_ARITMETICO', 'PARENTESIS_INICIO',
'EXPRESION', 'OP_ARITMETICO']
. Resultado del paso 2: ['PROGRAMA', 'ASIGNACION', 'OP_ARITMETICO', 'PARENTESIS_INICIO',
'EXPRESION', 'OP_ARITMETICO']

- Tokens Leidos: 'test1': IDENTIFICADOR


. Reduccion a simbolos no terminales
. Resultado del paso 1: ['PROGRAMA', 'ASIGNACION', 'OP_ARITMETICO', 'PARENTESIS_INICIO',
'EXPRESION', 'OP_ARITMETICO', 'IDENTIFICADOR']
** Reduccion: ['IDENTIFICADOR'] -> TERMINO
** Reduccion: ['TERMINO'] -> EXPRESION
** Reduccion: ['EXPRESION', 'OP_ARITMETICO', 'EXPRESION'] -> EXPRESION
. Resultado del paso 2: ['PROGRAMA', 'ASIGNACION', 'OP_ARITMETICO', 'PARENTESIS_INICIO',
'EXPRESION']

- Tokens Leidos: ')': PARENTESIS_FIN


. Reduccion a simbolos no terminales
** Reduccion: ['PARENTESIS_INICIO', 'EXPRESION', 'PARENTESIS_FIN'] -> EXPRESION
** Reduccion: ['ASIGNACION', 'OP_ARITMETICO', 'EXPRESION'] -> ASIGNACION
. Resultado del paso 1: ['PROGRAMA', 'ASIGNACION']
. Resultado del paso 2: ['PROGRAMA', 'ASIGNACION']

- Tokens Leidos: ';': TERMINADOR


. Reduccion a simbolos no terminales
** Reduccion: ['ASIGNACION', 'TERMINADOR'] -> SENTENCIA
** Reduccion: ['SENTENCIA'] -> PROGRAMA
** Reduccion: ['PROGRAMA', 'PROGRAMA'] -> PROGRAMA
. Resultado del paso 1: ['PROGRAMA']
. Resultado del paso 2: ['PROGRAMA']

### RESULTADO DEL ANLISIS ###


{'aceptado': True,
'detalle_error': {},
'error_lexico': False,
'iteraciones': 102,
'tokens_leidos': 17}

Cada iteracin completa para cada token ledo se estructura as:

- Tokens Leidos: ')': PARENTESIS_FIN


. Reduccion a simbolos no terminales
** Reduccion: ['PARENTESIS_INICIO', 'EXPRESION', 'PARENTESIS_FIN'] -> EXPRESION
** Reduccion: ['ASIGNACION', 'OP_ARITMETICO', 'EXPRESION'] -> ASIGNACION
. Resultado del paso 1: ['PROGRAMA', 'ASIGNACION']
. Resultado del paso 2: ['PROGRAMA', 'ASIGNACION']

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

Lenguajes Formales | Universidad Caece 30


Anlisis Sintctico del lenguaje JavaScript utilizando teora de autmatas

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.

** Reduccion: ['PARENTESIS_INICIO', 'EXPRESION', 'PARENTESIS_FIN'] -> EXPRESION


** Reduccion: ['ASIGNACION', 'OP_ARITMETICO', 'EXPRESION'] -> ASIGNACION

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.

### RESULTADO DEL ANLISIS ###


{'aceptado': True,
'detalle_error': {},
'error_lexico': False,
'iteraciones': 102,
'tokens_leidos': 17}

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.

Lenguajes Formales | Universidad Caece 31


Anlisis Sintctico del lenguaje JavaScript utilizando teora de autmatas

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.

Lenguajes Formales | Universidad Caece 32


Anlisis Sintctico del lenguaje JavaScript utilizando teora de autmatas

Token Reduccin 1 Reduccin 2 Total Token Total Analizador

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.

Lenguajes Formales | Universidad Caece 33


Anlisis Sintctico del lenguaje JavaScript utilizando teora de autmatas

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.

Lenguajes Formales | Universidad Caece 34


Anlisis Sintctico del lenguaje JavaScript utilizando teora de autmatas

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.

Lenguajes Formales | Universidad Caece 35


Anlisis Sintctico del lenguaje JavaScript utilizando teora de autmatas

BIBLIOGRAFA

1. ECMAScript 2016 ECMA-262


http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf

2. On certain formal properties of grammars


Noam Chomsky, Information and Control 2, Massachusetts Institute of Technology, Cambridge, 1959, pp.
137-167.

3. Three models for the description of language


Noam Chomsky, IRE Transactions on Information Theory (2), pp. 113-124.

4. Teora de Autmatas, lenguajes y computacin


John Hopcroft, Rajeev Motwani, Jeffrey Ullman Addison Wesley

5. Compilers: Principles, Techniques and Tools


A.V. Aho, I. Sethi and J.D. Ullman, Addison-Wesley, Reading, MA, 1985.

6. Introduction to Formal Language Theory


M. A. Harrison, Addison-Wesley, Reading, MA, 1978.

7. Algorithms and Complexity


H. S. Wilf, Taylor & Francis, 2003

Lenguajes Formales | Universidad Caece 36

Você também pode gostar