#! /usr/bin/env python2.6 # -*- coding: utf-8 -*- #--------------------------------- # EDIT: # Erickson Silva(erickson.silva@lavid.ufpb.br) # LAViD - Laboratório de Aplicações de Video Digital #--------------------------------- # Donatus Brazilian Portuguese Parser # # Copyright (C) 2010-2013 Leonel F. de Alencar # # Author: Leonel F. de Alencar # Homepage: # # Project's URL: # For license information, see LICENSE.TXT # # $Id: alexp.py $ """Este módulo contém funções que permitem utilizar o Aelius para etiquetar uma sentença, construindo entradas lexicais com base nas etiquetas atribuídas às palavras da sentença. Essas entradas lexicais são integradas em uma gramática CFG dada, que é transformada em um parser, utilizado para gerar uma árvore de estrutura sintagmática da sentença. """ import re, string,nltk, copy from os import path from Aelius.Extras import carrega from Aelius import AnotaCorpus # definição de algumas variáveis globais para # facilitar utilização das diferentes funções do módulo # sintaxe default em subpasta de nltk_data DIR="cfg.syn.nltk" # eventualmente será preciso incluir aqui outros sinais # de pontuação, como o travessão PUNCT=string.punctuation SENTENCA_ANOTADA="" def toqueniza(s): """Decodifica string utilizando utf-8, retornando uma lista de tokens em unicode. """ decodificada=s.decode("utf-8") return AnotaCorpus.TOK_PORT.tokenize(decodificada) def getAnaliseMorfologica(): return SENTENCA_ANOTADA def etiquetaSentenca(s): """Aplica um dos etiquetadores do Aelius na etiquetagem da sentença dada como lista de tokens. """ etiquetador = list((carrega("AeliusHunPos"),"nltk")) anotada = AnotaCorpus.anota_sentencas([s],etiquetador,"hunpos")[0] return anotada def geraEntradasLexicais(lista): """Gera entradas lexicais no formato CFG do NLTK a partir de lista de pares constituídos de tokens e suas etiquetas. """ entradas=[] for e in lista: # é necessário substituir símbolos como "-" e "+" do CHPTB # que não são aceitos pelo NLTK como símbolos não terminais c=re.sub(r"[-+]","_",e[1]) c=re.sub(r"\$","_S",c) entradas.append("%s -> '%s'" % (c, e[0].lower())) return entradas def corrigeAnotacao(lista): """Esta função deverá corrigir alguns dos erros de anotação mais comuns do Aelius. No momento, apenas é corrigida VB-AN depois de TR. """ i=1 while i < len(lista): if lista[i][1] == "VB-AN" and lista[i-1][1].startswith("TR"): lista[i]=(lista[i][0],"VB-PP") i+=1 # a função abaixo parece muito restritiva; talvez não seja necessário # que o arquivo esteja no diretório nltk_data def encontraArquivo(caminho=DIR): """Encontra arquivo em subpasta de nltk_data. """ for p in nltk.data.path: c=path.join(p,caminho) if path.isfile(c): return c return def extraiSintaxe(caminho=DIR): """Extrai gramática armazenada em arquivo cujo caminho é definido relativamente ao diretório nltk_data. """ arquivo=encontraArquivo(caminho) if arquivo: f=open(arquivo,"rU") sintaxe=f.read() f.close() return sintaxe else: print "Arquivo %s não encontrado em nenhum dos diretórios de dados do NLTK:\n%s" % (caminho,"\n".join(nltk.data.path)) def analisaSentenca(sentenca): """Retorna lista de árvores de estrutura sintagmática para a sentença dada sob a forma de uma lista de tokens, com base na gramática CFG cujo caminho é especificado como segundo argumento da função. Esse caminho é relativo à pasta nltk_data da instalação local do NLTK. A partir da etiquetagem morfossintática da sentença são geradas entradas lexicais que passam a integrar a gramática CFG. O caminho da gramática e o parser gerado são armazenados como tupla na variável ANALISADORES. """ parser=constroiAnalisador(sentenca) codificada=[w.encode("utf-8") for w in sentenca] trees=parser.nbest_parse(codificada) return trees def constroiAnalisador(s): """Constrói analisador a partir de uma única sentença não anotada, dada como lista de tokens, e uma lista de regras sintáticas no formato CFG, armazenadas em arquivo. Esta função tem um bug, causado pela maneira como o Aelius etiqueta sentenças usando o módulo ProcessaNomesProprios: quando a sentença se inicia por paravra com inicial minúscula, essa palavra não é incorporada ao léxico, mas a versão com inicial maiúscula. """ global SENTENCA_ANOTADA SENTENCA_ANOTADA=etiquetaSentenca(s) corrigeAnotacao(SENTENCA_ANOTADA) entradas=geraEntradasLexicais(SENTENCA_ANOTADA) lexico="\n".join(entradas) gramatica="%s\n%s" % (extraiSintaxe(DIR).strip(),lexico) cfg=nltk.parse_cfg(gramatica) return nltk.ChartParser(cfg) def exibeArvores(arvores): """Função 'wrapper' para a função de exibição de árvores do NLTK""" nltk.draw.draw_trees(*arvores) def run(sentenca): #SENTENCA_ANOTADA = [] tokens=toqueniza(sentenca) trees=analisaSentenca(tokens) return trees