alexp.py 5.04 KB
#! /usr/bin/env python2.6
# -*- coding: utf-8 -*-

#---------------------------------

# Editado:

#Autor: Erickson Silva 
#Email: <erickson.silva@lavid.ufpb.br> <ericksonsilva@live.com>

#LAViD - Laboratório de Aplicações de Vídeo Digital

#---------------------------------


# Donatus Brazilian Portuguese Parser
#
# Copyright (C) 2010-2013 Leonel F. de Alencar
#
# Author: Leonel F. de Alencar <leonel.de.alencar@ufc.br>
# Homepage: <http://www.leonel.profusehost.net/>
#
# Project's URL: <http://sourceforge.net/projects/donatus/>
# 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,os
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="/vlibras-translate/data/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 na pasta vlibras-translate.
	"""
	home = os.path.expanduser("~")
	path = os.path.realpath(home+caminho)
	return path

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.parse_one(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.CFG.fromstring(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):
	tokens=toqueniza(sentenca)
	trees=analisaSentenca(tokens)
	return trees