#!/usr/bin/env python # -*- coding: latin-1 -*- # ############################################################################## # # metacomp 2.5beta3: a metacompiler for RLL(1) grammars # Copyright (C) 2008 Juan Miguel Vilar and Andrés Marzal # Universitat Jaume I, Castelló (Spain) # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # Any questions regarding this software should be directed to: # # Juan Miguel Vilar # Departament de Llenguatges i Sistemes Informàtics # Universitat Jaume I # E12071 Castellón (SPAIN) # # email: jvilar@lsi.uji.es # ############################################################################## # # Fichero: analex.py # from string import * import errores import re ############################################################################## # Clases auxiliares # # Conjuntos de elementos # class Conjunto: def __init__(self): self.c = [] def insert(self, x): if x not in self.c: self.c.append(x) def __getitem__(self, i): return self.c[i] def __repr__(self): self.c.sort() return "·"+`self.c`+"·" def __add__(self, other): aux = Conjunto() aux.c = self.c[:] for i in other.c: if i not in self.c: aux.c.append(i) return aux def __sub__(self, other): aux = Conjunto() aux.c= filter(lambda x,y= other.c: x not in y, self.c) return aux def __len__(self): return len(self.c) # # ############################################################################## ############################################################################## # Analizador Lexico # ### # Clase ComponenteLexico # class ComponenteLexico: def __init__(self, cat=None): self.cat = cat def __str__(self): s = [] for k, v in self.__dict__.items(): if k != 'cat': s.append(`k`+': '+`v`) if s: return '%s (%s)' % (self.cat,', '.join(s)) else: return self.cat def __cmp__(self, other): if other and self.cat == other.cat and self.id == other.id: return 0 else: return 1 ### # Clase Analizador Lexico # try: from keyword import iskeyword except: _python_kwds = split("and del for is raise assert elif from lambda return " + "break else global not try class except if or while " + "continue exec import pass yield def finally in print") def iskeyword(s): return s in _python_kwds class AnalizadorLexico: def __init__(self, entrada): self.esprimera= 1 self.entrada= entrada self.actual= ComponenteLexico() self.nlactual= 0 self.nlfichero= 1 self.finfichero= 0 self.buffer=[] def lee_especificacion_lexica(self): # Desde el comienzo hasta el primer \n% self.actual= ComponenteLexico("especificacion_lexica") self.actual.esp= [] erterminal= re.compile("[a-zA-Z_]*[a-zA-Z]$") dentro= 0 self.terminales= Conjunto() while 1: l= self.entrada.readline() if l=="": self.finfichero= 1 errores.errores.append(self.nlactual, "Se ha terminado el fichero mientras leía la especificación léxica.") break self.nlactual= self.nlfichero self.nlfichero= self.nlfichero+1 l= lstrip(l) if l=="" or l[0]=="#": continue if l[0]=="%": self.devuelve(l) self.nlfichero= self.nlfichero-1 break l= rstrip(l) ee= re.split(r"[ \t]+", l) if len(ee)==0 or ee[0]=='': continue if not erterminal.match(ee[0]): errores.errores.append(self.nlactual, ("Las categorías léxicas están compuestas de letras y del símbolo _ y no terminan en _")) continue if len(ee)<= 2: errores.errores.append(self.nlactual, ("Cada línea de la especificación léxica debe constar de:"+ " identificador de categoría léxica, función de tratamiento del componente" " léxico y expresión regular.")) continue else: er= join(ee[2:], " ") try: re.compile(er) except re.error, msg: errores.errores.append(self.nlactual, ("No puedo compilar la expresión '%s'.\n" % er) + (" El error devuelto por Python es '%s'." % msg[0])) continue if iskeyword(ee[0]): errores.errores.append(self.nlactual, ("El identificador de categoría léxica '%s' " % ee[0] + ("coincide con una palabra reservada de Python."))) continue if ee[0]=="error": errores.errores.append(self.nlactual, "La categoría error está reservada") continue if ee[0][:3]=="mc_": errores.errores.append(self.nlactual, "El prefijo mc_ está reservado") continue if ee[0][-1]=="_": errores.errores.append(self.nlactual, "Las categorías léxicas no pueden terminar en _") continue self.actual.esp.append((ee[0], ee[1], er)) self.terminales.insert(ee[0]) def linea(self): return self.nlactual def nuevo_caracter(self): if self.buffer==[]: return self.entrada.read(1) else: c= self.buffer[-1] del self.buffer[-1] return c def nuevo_caracter_avanzando(self): c= self.nuevo_caracter() if c=='\n': self.nlfichero= self.nlfichero+1 return c def devuelve(self,cad): for i in range(len(cad)): self.buffer.append(cad[-i-1]) def sincroniza(self, sincr, enEOF): while self.actual.cat not in sincr and self.actual.cat!= "mc_EOF": self.avanza() if self.actual.cat=="mc_EOF" and not "mc_EOF" in sincr: enEOF() def avanza(self): if self.finfichero: self.actual= ComponenteLexico("mc_EOF") self.actual.nlinea= self.nlactual return if self.esprimera: self.esprimera= 0 self.lee_especificacion_lexica() return while 1: c= self.nuevo_caracter() if c=="": # Fin de fichero self.finfichero= 1 self.nlactual= self.nlfichero self.actual= ComponenteLexico("mc_EOF") self.actual.nlinea= self.nlactual return elif c==' ' or c=='\t' or c=='\r': # Nos cargamos el espacio en blanco pass elif c=='#': # Comentario while c!='\n' and c!="": c= self.nuevo_caracter() self.devuelve(c) elif c=='\n': # Nueva línea self.nlfichero= self.nlfichero+1 elif c=='<': # Posible no terminal lexema='' self.nlactual= self.nlfichero while 1: c= self.nuevo_caracter() if c in letters or c=="_": lexema= lexema+c elif c=='>': self.actual= ComponenteLexico("noterminal") self.actual.nlinea= self.nlactual self.actual.id= lexema if iskeyword(lexema): errores.errores.append(self.nlactual, "El no terminal %s coincide con una palabra reservada de Python.") if lexema[-1]== "_": errores.errores.append(self.nlactual, "Un no terminal no puede terminar en _") return else: self.devuelve(c) if lexema=='<': errores.errores.append(self.nlactual, "El carácter < sólo puede utilizarse para no terminales.") break errores.errores.append(self.nlactual, "El no terminal %s no tiene el símbolo > al final" % lexema) self.actual= ComponenteLexico("noterminal") self.actual.nlinea= self.nlactual self.actual.id= lexema return elif c in letters or c=="_": # Terminal lexema= c self.nlactual= self.nlfichero self.actual= ComponenteLexico("terminal") self.actual.directo= 0 while 1: c= self.nuevo_caracter() if c in letters or c=="_": lexema= lexema+c else: self.devuelve(c) if lexema== "error": self.actual= ComponenteLexico("tokenerror") elif iskeyword(lexema): errores.errores.append(self.nlactual, "Vaya, el identificador %s ya está reservado por Python." % lexema) if lexema[-1]=="_": errores.errores.append(self.nlactual, "Un identificador de categoría léxica no puede terminar en _") self.actual.id= lexema self.actual.nlinea= self.nlactual return elif c=='"': # Terminal directo lexema= "!" self.nlactual= self.nlfichero self.actual= ComponenteLexico("terminal") self.actual.nlinea= self.nlactual self.actual.directo= 1 while 1: c= self.nuevo_caracter() if c=='"': self.actual.id= lexema return elif c in [" ","\t","\n"]: errores.errores.append(self.nlactual, "He encontrado un terminal directo no terminado, asumire que es %s" % lexema) self.devuelve(c) self.actual.id= lexema return lexema= lexema+c elif c=="@": # Posible acción self.nlactual= self.nlfichero lexema="" self.actual= ComponenteLexico("accion") self.actual.nlinea= self.nlactual escape= 0 while 1: c= self.nuevo_caracter() lexema= lexema+c if c=="@": if escape: escape= 0 else: self.actual.id= lexema[:-1] return elif c=="\\": escape= 1 elif c=="" or c=="\n": errores.errores.append(self.nlactual, "La acción semántica que empieza en esta línea no está terminada.") self.actual.id= lexema if c=="\n": self.devuelve(c) return else: escape= 0 elif c=="$": # Error dos self.nlactual= self.nlfichero lexema="" self.actual= ComponenteLexico("errordos") self.actual.nlinea= self.nlactual escape= 0 while 1: c= self.nuevo_caracter_avanzando() lexema= lexema+c if c=="$": if escape: escape= 0 else: self.actual.id= lexema[:-1] return elif c=="\\": escape= 1 elif c=="" or c=="\n": errores.errores.append(self.nlactual, "El tratamiento de error de tipo dos de esta línea no está terminado.") self.actual.id= lexema if c=="\n": self.devuelve(c) return else: escape= 0 elif c=="%": # Código self.nlactual= self.nlfichero lexema="" self.actual= ComponenteLexico("codigo") self.actual.nlinea= self.nlactual finlinea= 0 while 1: c= self.nuevo_caracter_avanzando() lexema= lexema+c if (c=="%" and finlinea) or c=="": if c== "%": lexema= lexema[:-1] # Quitamos el último % self.actual.cod= lexema return if c=="\n": finlinea= 1 else: finlinea= 0 elif c=="-": # Posible flecha c= self.nuevo_caracter() if c==">": self.nlactual= self.nlfichero self.actual= ComponenteLexico("flecha") self.actual.nlinea= self.nlactual return else: errores.errores.append(self.nlfichero, "He encontrado un triste y solitario guión.") self.devuelve(c) elif c==";": # Punto y coma self.nlactual= self.nlfichero self.actual= ComponenteLexico("pyc") self.actual.nlinea= self.nlactual return elif c=="*": # Asterisco self.nlactual= self.nlfichero self.actual= ComponenteLexico("asterisco") self.actual.nlinea= self.nlactual return elif c=="+": # Cruz self.nlactual= self.nlfichero self.actual= ComponenteLexico("cruz") self.actual.nlinea= self.nlactual return elif c=="?": # Interrogante self.nlactual= self.nlfichero self.actual= ComponenteLexico("interrogante") self.actual.nlinea= self.nlactual return elif c=="|": # Barra self.nlactual= self.nlfichero self.actual= ComponenteLexico("barra") self.actual.nlinea= self.nlactual return elif c=="(": # Abre paréntesis self.nlactual= self.nlfichero self.actual= ComponenteLexico("abre") self.actual.nlinea= self.nlactual return elif c==")": # Cierra paréntesis self.nlactual= self.nlfichero self.actual= ComponenteLexico("cierra") self.actual.nlinea= self.nlactual return else: errores.errores.append(self.nlfichero, "He encontrado el carácter %s y no sé que hacer con él." % c) # # ############################################################################## if __name__=="__main__": import sys f= sys.stdin a= AnalizadorLexico(f) a.avanza() try: while a.actual.cat!= "mc_EOF": print a.linea(),"---",a.actual a.avanza() print a.linea(),"---",a.actual except errores.DemasiadosErrores: print "Demasiados errores" errores.muestra_errores_y_avisos()