#!/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: generador.py # from string import * def genera_analex(esplex): c= [] indent= [""] def l(x, y=c, i= indent): y.append(i[0]+x) def dentro(i= indent): i[0]=i[0]+" " def fuera(i= indent): i[0]=(i[0][:-2]) # # Clase ComponenteLexico # l("class ComponenteLexico:") dentro() # Método __init__: l("def __init__(self, cat, lexema, nlinea):") dentro() l("self.cat= cat") l("self.lexema= lexema") l("self.nlinea= nlinea") fuera() # Método __repr__: l("def __str__(self):") dentro() l("s = []") l("for k, v in self.__dict__.items():") l(" if k != 'cat': s.append(`k`+': '+`v`)") l("if s:") l(" return '%s (%s)' % (self.cat,', '.join(s))") l("else:") l(" return self.cat") fuera() fuera() # # Clase AnalizadorLexico: # l("class AnalizadorLexico:") dentro() # Método __init__ l("def __init__(self, entrada):") dentro() l("self.esp=[") coma="" dentro() for (cat,trat,er) in esplex: if cat!= "None": l('%s(%s, %s, re.compile(%s))' % (coma, `cat`, trat, `strip(er)`)) else: l('%s(%s, %s, re.compile(%s))' % (coma, cat, trat, `strip(er)`)) coma="," fuera() l("]") l("if type(entrada)==type(''):") l(" self.l= entrada") l("else:") dentro() l("ll= entrada.readlines()") l("self.l= string.join(ll,'')") l("entrada.close()") fuera() l("self.nlactual = 1") l("self.actual = ComponenteLexico(None,None,0)") l("try:") l(" self.error_lexico= error_lexico") l("except:") l(" self.error_lexico= self._error_lexico") fuera() # Tratamiento por defecto del error léxico: l("def _error_lexico(self, linea_error, cars):") l(' sys.stderr.write("Error léxico no tratado en línea %d: No esperaba %s.\\n" % (linea_error, repr(cars)))') l(' sys.exit(1)') # Método línea: l("""def linea(self): return self.actual.nlinea""") # Método sincroniza: l("""def sincroniza(self, sincr, enEOF= mc_abandonar): 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()""") # Método avanza: l("""def avanza(self): if self.l=="": self.actual = ComponenteLexico("mc_EOF", "", self.nlactual) return self.actual cars_inesperados, linea_error = [], 0 while 1: nl= self.nlactual max_lon, ff = 0, None for (cat, f, er) in self.esp: m = er.match(self.l) if m: if max_lon < m.end(): max_lon, cc, ff = m.end(), cat, f if max_lon > 0: if cars_inesperados: self.error_lexico(linea_error, string.join(cars_inesperados,"")) self.nlactual+= cars_inesperados.count("\\n") cars_inesperados, linea_error = [], 0 lexema= self.l[:max_lon] self.actual = ComponenteLexico(cc, lexema, nl) self.nlactual+= lexema.count("\\n") self.l = self.l[max_lon:] if ff: ff(self.actual) if self.actual.cat!= None: return self.actual continue else: if self.l=="": if cars_inesperados: self.error_lexico(linea_error, string.join(cars_inesperados,"")) self.actual = ComponenteLexico("mc_EOF","",self.nlactual) return self.actual if not linea_error: linea_error = self.nlactual cars_inesperados.append(self.l[0]) self.l= self.l[1:] """) return c def diccionarios(gramatica, w): # Diccionarios de primeros, siguientes, aceptables y anulables: w("""mc_primeros= {""") aux1='' for nt in gramatica.noterminales: w('%s"%s":[' % (aux1,nt)) aux1=",\n " aux2= '' for p in nt.primeros(): w(aux2) aux2="," w('"%s"' % p) w("]") w("}\n") w("""mc_siguientes= {""") aux1='' for nt in gramatica.noterminales: w('%s"%s":[' % (aux1,nt)) aux1=",\n " aux2= '' for s in nt.siguientes(): w(aux2) aux2="," w('"%s"' % s) w("]") w("}\n") w("""mc_aceptables= {""") aux1='' for nt in gramatica.noterminales: w('%s"%s":[' % (aux1,nt)) aux1=",\n " aux2= '' for s in nt.aceptables(): w(aux2) aux2="," w('"%s"' % s) w("]") w("}\n") w("""mc_anulables= {""") aux1='' for nt in gramatica.noterminales: w('%s"%s": ' % (aux1,nt)) aux1=",\n " w(str(nt.anulable())) w("}\n") def genera_excepciones(w): # Las excepciones y algunas funciones auxiliares: w("""class mc_error_sintaxis(Exception):\n""") w(""" pass\n""") w("""class mc_error_noEOF(Exception):\n""") w(""" pass\n""") w("""class mc_error_abandonar(Exception):\n""") w(""" pass\n""") w("""def mc_error(nt, esp):\n""") w(""" raise mc_error_sintaxis,(nt,esp)\n""") w("""def mc_abandonar():\n""") w(""" raise mc_error_abandonar\n""") def genera_sintactico(gram, w, traza): # La clase para los atributos: w("""class Atributos:\n""") w(""" pass\n""") # El analizador sintáctico: w("""class AnalizadorSintactico:\n""") w(""" def __init__(self, entrada, entorno= None):\n""") w(""" self.mc_entorno= entorno\n""") w(""" self.mc_al= AnalizadorLexico(entrada)\n""") w(""" self.mc_al.avanza()\n""") w(""" self.mc_reintento=[1]\n""") w(""" mc_reintentar= self.mc_reintentar\n""") w(""" while self.mc_reintento[-1]:\n""") w(""" self.mc_reintento[-1]= 0\n""") w(""" self.%s = Atributos()\n""" % gram.inicial.nombre) w(""" try:\n""") w(""" self.mc_analiza_%s(self.%s)\n""" % (gram.inicial.nombre,gram.inicial.nombre)) w(""" if self.mc_al.actual.cat!= "mc_EOF":\n""") w(""" raise mc_error_noEOF\n""") w(""" except mc_error_sintaxis, (nt,esp):\n""") w(""" sys.stderr.write("Error no tratado en línea %d:\\n"% self.mc_al.actual.nlinea)\n""") w(""" sys.stderr.write("Estaba analizando la expansión del no terminal %s y he encontrado\\n el terminal %s.\\n" % (nt, self.mc_al.actual.cat))\n""") w(""" if len(esp)==1:\n""") w(""" sys.stderr.write("Sólo valía un %s.\\n" % esp[0])\n""") w(""" else:\n""") w(""" sys.stderr.write("Tendría que haber sido uno de los siguientes: %s.\\n" % string.join(esp,","))\n""") w(""" sys.exit(1)\n""") w(""" except mc_error_noEOF:\n""") if gram.noEOF: # Si se trata el error noEOF w(""" mc_al= self.mc_al\n""") for l in gram.noEOF: w(""" %s\n""" % l) else: w(""" sys.stderr.write("Error no tratado en línea %d:\\n" % self.mc_al.actual.nlinea)\n""") w(""" sys.stderr.write("- He encontrado entrada donde esperaba ver el final del fichero\\n")\n""") w(""" sys.exit(1)\n""") w(""" except mc_error_abandonar:\n""") w(""" pass\n""") w(""" def mc_reintentar(self):\n""") w(""" self.mc_reintento[-1]= 1\n""") # Código de los no terminales: for nt in gram.noterminales: c= nt.genera_codigo(traza) for l in c: w(""" %s\n""" % l) def genera_puro(gram, w, traza): # El analizador sintáctico: w("""class AnalizadorSintactico:\n""") w(""" def __init__(self, entrada, entorno= None):\n""") w(""" self.mc_al= AnalizadorLexico(entrada)\n""") w(""" self.mc_al.avanza()\n""") w(""" try:\n""") w(""" self.mc_analiza_%s()\n""" % gram.inicial.nombre) w(""" if self.mc_al.actual.cat!= "mc_EOF":\n""") w(""" raise mc_error_noEOF\n""") w(""" except mc_error_sintaxis, (nt,esp):\n""") w(""" self.mc_error= 1\n""") w(""" self.mc_lineaError= self.mc_al.actual.nlinea\n""") w(""" self.mc_ntError= nt\n""") w(""" self.mc_tError= self.mc_al.actual\n""") w(""" self.mc_esperado= esp\n""") w(""" except mc_error_noEOF:\n""") w(""" self.mc_error= 1\n""") w(""" self.mc_lineaError= self.mc_al.actual.nlinea\n""") w(""" self.mc_ntError= None\n""") w(""" self.mc_tError= self.mc_al.actual\n""") w(""" self.mc_esperado= ["mc_EOF"]\n""") w(""" else:\n""") w(""" self.mc_error= 0\n""") # Código de los no terminales: for nt in gram.noterminales: c= nt.genera_codigo(traza, 1) for l in c: w(""" %s\n""" % l) def genera_analizador(gram, elexica, codusuario, traza, salida, analex, tipoAnalizador): w = salida.write # Preámbulo w("#!/usr/bin/env python\n") w("# -*- coding: latin-1 -*-\n") w("# Este código ha sido generado por metacomp, versión 2.5beta3\n") w("import sys, re, string\n") if traza=="t" or traza=="A": w("indentacion_traza = 0\n") w("def mc_traza(l):\n") w(" sys.stderr.write(indentacion_traza*' '+l)\n") genera_excepciones(w) # Analizador léxico: if not analex: w(join(genera_analex(elexica),"\n")) else: w("from %s import AnalizadorLexico" % analex) # El código del usuario: w("""\n# Código de usuario\n""") for i in codusuario: w("%s\n" % i) if tipoAnalizador== "normal": # Los primeros y siguientes: diccionarios(gram, w) # El sintáctico: genera_sintactico(gram, w, traza) # La llamada a main: w(""" if __name__=="__main__": try: mc_main= main except NameError: def mc_main(): AnalizadorSintactico(sys.stdin) mc_main() """) elif tipoAnalizador=="puro": # Los primeros y siguientes: diccionarios(gram, w) # El sintáctico: genera_puro(gram, w, traza) # La llamada a main: w(r""" if __name__=="__main__": try: mc_main= main except NameError: def mc_main(): A= AnalizadorSintactico(sys.stdin) if not A.mc_error: print "La entrada no tiene errores sintácticos." else: if A.mc_ntError!= None: print "Hay un error de sintaxis en la línea %d, provocado por el componente\n\t%s" % ( A.mc_lineaError, A.mc_tError) print "y detectado al intentar analizar en no terminal %s." % A.mc_ntError else: print "Hay un error de sintaxis en la línea %d, provocado por el componente\n\t%s" % ( A.mc_lineaError, A.mc_tError) print "y detectado cuando se esperaba el fin de la entrada." mc_main() """) elif tipoAnalizador=="lexico": # Si sólo generamos el léxico: w(""" if __name__=="__main__": try: mc_main= main except NameError: def mc_main(): A= AnalizadorLexico(sys.stdin) A.avanza() while A.actual.cat!= "mc_EOF": print A.actual A.avanza() mc_main() """) else: sys.stderr.write("Error interno.\n") sys.stderr.write("Se ha especificado un tipo de analizador distinto de normal, puro y léxico.\n") sys.exit(1) def _main(): l= [("Hola","procesa_hola","HOLA"),(None,None,"ninguna")] c= genera_analex(l) print join(c,"\n") if __name__=="__main__": _main()