diff --git a/src/new/AplicaRegras.py b/src/new/AplicaRegras.py index 97ccd5b..f08c2a5 100644 --- a/src/new/AplicaRegras.py +++ b/src/new/AplicaRegras.py @@ -7,6 +7,7 @@ #LAViD - Laboratório de Aplicações de Vídeo Digital import platform +import re import xml.etree.ElementTree as ET from os.path import expanduser from collections import deque @@ -19,114 +20,110 @@ from nltk import ParentedTree, Tree, draw class AplicaRegras(object): - # inicializa todos as variaveis def __init__(self): self.__root = self.get_root() self.dicionarios = LerDicionarios() + # Dicionário de funcões do campo specific do arquivo de regras self.__especificos = {"advt":self.verificar_adv_tempo, "v":self.verificar_vb_infinitivo, "x":self.verificar_preposicao, "c":self.verificar_subst_genero, "a":self.verificar_artigo, "l":self.verificar_vb_ligacao, "i":self.verificar_adv_intensidade, "vbi":"zero", "n":self.verificar_vb_muda_negacao, "abmn":"zero", "adji":"zero","adjn":"zero", "advi":"zero"} + self.__acoes = {"change_vb":self.get_vb_infinitivo, "concate_intens":self.get_token_intensidade} + + # Gera arvore a partir do arquivos regras.xml def get_root(self): if platform.system() == 'Windows': return ET.parse(expanduser("~")+'\\vlibras-translate\data\\regras.xml').getroot() return ET.parse(expanduser("~")+'/vlibras-translate/data/regras.xml').getroot() + # Aplica regras morfológicas apartir do arquivo regras.xml def aplicar_regras_morfo(self, lista, sint=False): - self.lista = list(lista) - self.quantidade_iter_pular = 0 - self.lista_corrigida = [] + self.lista = list(lista) # Nova cópia da lista morfológica + self.lista_corrigida =[] # Lista morfológica após a aplicação das regras it = Iterator() it.load(self.lista) - while(it.has_next()): + while(it.has_next()): for morpho in self.__root.findall('morphological'): - self.has_rule = False - for rule in morpho.findall('rule'): # procura a tag rule + self.has_rule = False # Boolean caso encontre encontre regra compativel + for rule in morpho.findall('rule'): + # Verifica se a regra está ativa e se o nome da regra, ou o ínicio, é igual a tag de classificação do token atual if rule.find('active').text == "true" and rule.get('name').split("_")[0] == it.get_ticket(): count = int(rule.find('count').text) - self.lista_iteracoes = [] - if count == 1: - self.lista_iteracoes = [it.get_token()] - else: - try: - self.lista_iteracoes = it.get_interval(count) - it.skip(count-1) - except: - continue + self.lista_iteracoes = [] # Lista que conterá todas tuplas referentes a quantidade de classes da regra + + # Obtém o intervalo de tuplas de acordo com o número de classes da regra + try: + self.lista_iteracoes = it.get_interval(count) + print '# LISTA DA ITERAÇÂO: ' + print self.lista_iteracoes + except: + continue + # Gera o nome da regra do intervalo de tuplas e verifica se é igual ao nome da regra em si self.nome_regra = self.gerar_nome_regra(self.lista_iteracoes) - if rule.get('name') == self.nome_regra: # verifica se a regra é aplicavel e a mesma esta ativa + if rule.get('name') == self.nome_regra: self.has_rule = True - self.lista_iteracao_regra = count * [None] self.count_iteracao_regra = -1 - print "REGRA MORFOLÓGICA: " + rule.get('name') + print "REGRA MORFOLÓGICA ENCONTRADA: " + rule.get('name') + self.lista_iteracao_regra = [] # Lista temporária | Insere tokens após a aplicação das regras + for classe in rule.iter('class'): # for nas tags class - - title = classe.find('title') - newpos = classe.find('newpos') + action = classe.find('action') newprop = classe.find('newprop') newtoken = classe.find('newtoken') newtokenpos = classe.find('newtokenpos') - self.specific = classe.find('specific') + specific = classe.find('specific') self.count_iteracao_regra += 1 tupla = self.lista_iteracoes[self.count_iteracao_regra] - if self.specific is not None: - self.specific = self.__especificos[self.specific.text](tupla[0]) - if self.specific == False: + if specific is not None: + result_specific = self.__especificos[specific.text](tupla[0]) + if result_specific is False: + print "REGRA MORFOLÓGICA " + rule.get('name') + " INVÁLIDA. PROCURANDO OUTRA..." self.has_rule = False - self.quantidade_iter_pular = 0 break - if newprop is not None and newtoken is not None: - if newtokenpos is not None and newtokenpos.text == "0": - self.lista_iteracao_regra[self.count_iteracao_regra] = (newtoken.text + "_" + self.specific, newprop.text) - else: - self.lista_iteracao_regra[self.count_iteracao_regra] = (self.specific + "_" + newtoken.text, newprop.text) - continue - - elif newprop is not None: - self.lista_iteracao_regra.append([self.specific,newprop.text]) - - if newtoken is not None and newpos is not None: - if newtokenpos is not None: - lista_merge = count * [None] - lista_merge[int(newpos.text)] = tupla[0] - lista_merge[int(newtokenpos.text)] = newtoken.text - merge_tokens = "_".join(lista_merge) - self.lista_iteracao_regra.append([merge_tokens, title.text]) - else: - self.lista_iteracao_regra.append([tupla[0] + "_" + newtoken.text, title.text]) - - elif newpos is not None: - if newpos.text == "-1": - self.lista_corrigida.append(None) + if action is not None: + action_text = action.text + if action_text == "remove": + self.lista_iteracao_regra.append(None) continue - else: - self.lista_iteracao_regra[int(newpos.text)] = tupla - - elif newtoken is not None: - token_anterior = self.lista_iteracao_regra[self.count_iteracao_regra][0] - ticket_anterior = self.lista_iteracao_regra[self.count_iteracao_regra][1] - if newtokenpos is not None and newtokenpos.text == "0": - self.lista_iteracao_regra[self.count_iteracao_regra] = [newtoken.text + "_" + token_anterior, ticket_anterior] - else: - self.lista_iteracao_regra[self.count_iteracao_regra] = [token_anterior + "_" + newtoken.text, ticket_anterior] - - if self.has_rule: - self.lista_corrigida.append(filter(None, self.lista_iteracao_regra)[0]) - break - - if (self.has_rule == False): + elif action_text == "invert": + self.lista_iteracao_regra.reverse() + elif self.__acoes.has_key(action_text): + result_action = self.__acoes[action_text](tupla[0]).lower() + self.lista_iteracao_regra.append([result_action, tupla[1]]) + else: + self.lista_iteracao_regra.append(tupla) + + if newprop is not None: + self.lista_iteracao_regra[self.count_iteracao_regra][1] = newprop.text + + if newtoken is not None: + if newtokenpos.text == "next": + self.lista_iteracao_regra.append([newtoken.text.lower(), "NTK"]) + elif newtokenpos.text == "previous": + self.lista_iteracao_regra.append(self.lista_iteracao_regra[-1]) + self.lista_iteracao_regra[-2] = [newtoken.text.lower(), "NTK"] + elif newtokenpos.text == "end": + print "TODO" + + if self.has_rule: + it.skip(count-1) + self.lista_corrigida.extend(self.lista_iteracao_regra) + break + + if self.has_rule is False: + print 'NÃO ACHOU REGRA - ' + it.get_word().encode('utf-8') self.lista_corrigida.append(it.get_token()) #se nao achou regra, entao adiciona a tupla original if sint: - return self.lista_corrigida + return self.lista_corrigida return filter(None, self.lista_corrigida) @@ -135,159 +132,185 @@ class AplicaRegras(object): self.adaptar_regras_morfo_arvore(lista, p_arvore) for morpho in self.__root.findall('syntactic'): for rule in morpho.findall('rule'): # procura a tag rule - nome_regra = self.corrigir_anotacao(rule.get('name')) + nome_regra = self.corrigir_nome_regra(rule.get('name')) regra = self.separar_regra(nome_regra) node_pai = tgrep_nodes(p_arvore, regra[0], search_leaves=False) - if node_pai: - node_regra = tgrep_nodes(node_pai[0], regra[1].replace('$', '>'), search_leaves=False) + if node_pai and rule.find('active').text == "true": + node_pai = node_pai[0] + node_regra = tgrep_nodes(node_pai, regra[1].replace('$', '..'), search_leaves=False) if node_regra: - node_esq = tgrep_nodes(node_pai[0], regra[1], search_leaves=False) - node_esq_pos = tgrep_positions(node_pai[0], regra[1], search_leaves=False) - node_dir = tgrep_nodes(node_pai[0], regra[2], search_leaves=False) - node_dir_pos = tgrep_positions(node_pai[0], regra[2], search_leaves=False) - if node_esq and node_dir: - print "REGRA SINTÁTICA: " + rule.get('name') - subnodes = node_esq + node_dir - for subnode in subnodes: - self.has_rule = True - self.alteracoes_nao_implementadas = [None,None] - self.count_iteracao_regra = -1 - self.specific = None - - for classe in rule.findall('class'): - self.specific = classe.find('specific') - if self.specific is not None: - self.specific = self.__especificos[self.specific.text](subnode.leaves()[0]) - if self.specific is False: - self.has_rule = False - break - - # modelo: [['node_esq', ['token', 'ticket']],['node_dir', ['token', 'ticket']]] - for classe in rule.iter('class'): - title = classe.find('title') - if subnode.label() == title.text: - newpos = classe.find('newpos') - newprop = classe.find('newprop') - newtoken = classe.find('newtoken') - newtokenpos = classe.find('newtokenpos') - - - self.count_iteracao_regra += 1 - - if self.specific is not None: - self.specific = self.__especificos[self.specific.text](subnode.leaves()[0]) - if self.specific is False: - self.has_rule = False - break - - if newprop is not None and newtoken is not None: - if newtokenpos is not None and newtokenpos.text == "0": - self.alteracoes_nao_implementadas[self.count_iteracao_regra].append([newtoken.text + "_" + self.specific, newprop.text]) - else: - self.alteracoes_nao_implementadas[self.count_iteracao_regra].append([self.specific + "_" + newtoken.text, newprop.text]) - continue - - elif newprop is not None: - self.alteracoes_nao_implementadas[self.count_iteracao_regra].append([self.specific,newprop.text]) - - if newtoken is not None and newpos is not None: - if newtokenpos is not None: - lista_merge = count * [None] - lista_merge[int(newpos.text)] = subnode.leaves()[0] - lista_merge[int(newtokenpos.text)] = newtoken.text - merge_tokens = "_".join(lista_merge) - self.alteracoes_nao_implementadas[self.count_iteracao_regra].append([merge_tokens, title.text]) - else: - self.alteracoes_nao_implementadas[self.count_iteracao_regra].append([subnode.leaves()[0] + "_" + newtoken.text, title.text]) + node_esq_pos = tgrep_positions(node_pai, regra[1], search_leaves=False) + node_dir_pos = tgrep_positions(node_pai, regra[2], search_leaves=False) + if node_esq_pos and node_dir_pos: + print "REGRA SINTÁTICA ENCONTRADA: " + rule.get('name') + nodes_positions = node_esq_pos + node_dir_pos + self.count = -1 + self.has_rule = True + + count_temp = -1 + for classe in rule.findall('class'): + count_temp += 1 + leaves = node_pai[nodes_positions[count_temp]].leaves() + token = filter(None, leaves)[0] + specific = classe.find('specific') + if specific is not None: + result_specific = self.__especificos[specific.text](token) + if result_specific is False: + self.has_rule = False + + if self.has_rule is False: + print "REGRA SINTÁTICA " + rule.get('name') + " INVÁLIDA. PROCURANDO OUTRA..." + break - elif newpos is not None: - if newpos.text == "-1": - alteracoes_nao_implementadas[self.count_iteracao_regra].append(None) - continue - else: - #TODO - if int(newpos.text) == 0: - self.alteracoes_nao_implementadas[int(newpos.text)] = subnode - - elif newtoken is not None: - token_anterior = self.lista_iteracao_regra[self.count_iteracao_regra][0] - ticket_anterior = self.lista_iteracao_regra[self.count_iteracao_regra][1] - if newtokenpos is not None and newtokenpos.text == "0": - self.lista_iteracao_regra[self.count_iteracao_regra] = [newtoken.text + " " + token_anterior, ticket_anterior] + nodes_deleted = [] + + for classe in rule.iter('class'): + action = classe.find('action') + newprop = classe.find('newprop') + + self.count += 1 + + if action is not None: + action_text = action.text + + if action_text == "remove": + pos_del = nodes_positions[self.count] + nodes_deleted.append(node_pai[pos_del]) + node_pai[pos_del] = None + continue + + elif action_text == "invert": + aux1 = node_pai[nodes_positions[self.count]] + aux2 = node_pai[nodes_positions[self.count+1]] + node_pai[nodes_positions[self.count]] = None + node_pai[nodes_positions[self.count+1]] = None + node_pai[nodes_positions[self.count]] = aux2 + node_pai[nodes_positions[self.count+1]] = aux1 + + ''' + elif self.__acoes.has_key(action_text): + leaves = node_pai[nodes_positions[self.count]].leaves() + token = filter(None, leaves)[0] + result_action = self.__acoes[action_text](token) + + #self.lista_iteracao_regra.append([result_action, tupla[1]]) + nodes_positions[self.count] = 'TODO' + + if action_text == "concate_intens": + leaves_prev = node_pai[nodes_positions[self.count-1]].leaves() + token_prev = filter(None, leaves_prev)[0] + title_text = classe.find('title').text + if title_text == "ADV-R": + node_prev = nodes_deleted.pop() + label_prev = node_prev[0][0].label() + token_prev = filter(None, node_pai[nodes_positions[count_temp-1]].leaves())[0] + token = filter(None, node_pai[nodes_positions[count_temp]].leaves())[0] + token_concate = token_prev + "_" + token + node_pai[nodes_positions[count_temp-1]][0][0][0] = token_concate + newprop = "" + if label_prev[:-2] == "VB": + newprop = "VBi" + elif label_prev[:-3] == "ADJ": + newprop = "ADJi" + + # TODO: Verifica qual newprop adicionada e remove o nó corrente + node_pai[nodes_positions[count_temp-1]][0][0].set_label(newprop) + pos_del = nodes_positions[self.count] + node_pai[pos_del] = None + else: - self.lista_iteracao_regra[self.count_iteracao_regra] = [token_anterior + " " + newtoken.text, ticket_anterior] + token_prev = filter(None, nodes_deleted.pop().leaves())[0] + token = filter(None, node_pai[nodes_positions[count_temp]].leaves())[0] + token_concate = token + "_" + token_prev + node_pai[nodes_positions[count_temp]][0][0][0] = token_concate - if self.has_rule: - print self.alteracoes_nao_implementadas[0] - self.lista_corrigida.append(filter(None, self.lista_iteracao_regra)[0]) - break + elif action_text == "concate_neg": + print "TODO" + ''' + if newprop is not None: + node_pai[nodes_positions[self.count]].set_label(newprop.text) + + break + return self.converter_arv_para_lista(p_arvore) + + # Aplica regras morfológicas na árvore sintática def adaptar_regras_morfo_arvore(self, lista, arvore): lista_pos_arv = [] + # Pega as posições das classificações morfológicas dos tokens na arvore sintática for tupla in lista: - string_grep = self.corrigir_anotacao(tupla[1]) + " < " + tupla[0].lower() + string_grep = self.corrigir_nome_regra(tupla[1]) + " < " + tupla[0].lower() node = tgrep_positions(arvore, string_grep) + if not node: + string_grep = self.corrigir_nome_regra(tupla[1]) + " < " + self.remover_acento(tupla[0].lower()) + node = tgrep_positions(arvore, string_grep) if node[0] in lista_pos_arv: node.reverse() lista_pos_arv.append(node[0]) + + # Aplica regras morfológicas na lista morfo = self.aplicar_regras_morfo(lista, sint=True) + + # Corrige arvore de acordo com a lista após aplicar as regras morfológicas for i in range(0, len(morfo)): + if morfo[i] is not None and morfo[i][1] == "NTK": + new_node = self.gerar_no(morfo[i]) + arvore[lista_pos_arv[i-1][:-2]].insert(2, new_node) + try: + lista_pos_arv.insert(i,lista_pos_arv[i]) + except: + continue + arv_ticket = arvore[lista_pos_arv[i]].label() arv_token = arvore[lista_pos_arv[i]][0] + if morfo[i] is None: - arvore[lista_pos_arv[i][:-1]] = None + arvore[lista_pos_arv[i]] = None + elif arv_token != morfo[i][0] and arv_ticket != morfo[i][1]: arvore[lista_pos_arv[i]][0] = morfo[i][0] - arvore[lista_pos_arv[i]].set_label(self.corrigir_anotacao(morfo[i][1])) + arvore[lista_pos_arv[i]].set_label(morfo[i][1]) + elif arv_token != morfo[i][0]: arvore[lista_pos_arv[i]][0] = morfo[i][0] + elif arv_ticket != morfo[i][1]: - arvore[lista_pos_arv[i]].set_label(self.corrigir_anotacao(morfo[i][1])) - else: - continue - - nodes_none = tgrep_positions(arvore, 'None') - for node in nodes_none: - arvore[node[:-1]].remove(None) + arvore[lista_pos_arv[i]].set_label(morfo[i][1]) + # Converte árvore sintática para uma lista de tuplas (igual a lista morfológica) def converter_arv_para_lista(self, arvore): - folhas = arvore.leaves() + folhas = filter(None, arvore.leaves()) lista_nodes = [] for folha in folhas: pos = tgrep_positions(arvore, folha) node = arvore[pos[0][:-1]] #decode node[0] - lista_nodes.append([node[0], self.corrigir_anotacao(node.label())]) + lista_nodes.append([node[0], self.corrigir_nome_regra(node.label())]) return lista_nodes + def remover_acento(self, texto): + return normalize('NFKD', texto.encode('utf-8').decode('utf-8')).encode('ascii', 'ignore') - def criar_ptree(self, s): + # Gera um ParentedTree do NLTK apartir da string recebida + def gerar_no(self, s): all_ptrees = [] - ptree = ParentedTree.convert(Tree.fromstring(s)) + t_string = '('+s[1] + ' ' + s[0]+')' + ptree = ParentedTree.convert(Tree.fromstring(t_string)) all_ptrees.extend(t for t in ptree.subtrees() if isinstance(t, Tree)) return ptree - def separar_new_tokens(self, lista): - for index, tupla in enumerate(lista): - if '_' in tupla[0]: - if 'VB' in tupla[1]: - token_split = tupla[0].split('_') - tupla[0] = token_split[0] - lista.append([token_split[1],'NTK']) - else: - token_split = tupla[0].split('_') - tupla[0] = token_split[0] - lista.insert(index+1, [token_split[1],'NTK']) - - def corrigir_anotacao(self, anotacao): + # Corrige nome da regra descrita no arquivo de regras para como está na árvore sintática + def corrigir_nome_regra(self, anotacao): split = anotacao.split('_') for i in range(0, len(split)): - split[i] = split[i].replace('-','_') + split[i] = re.sub(r"[-+]","_", split[i]) + split[i] = re.sub(r"\$","_S",split[i]) return "-".join(split).encode('utf-8') + # Separa a regra por nó pai e seus filhos def separar_regra(self, regra): split = regra.split("(") split[1] = split[1].replace(")","").split("-") @@ -298,22 +321,24 @@ class AplicaRegras(object): split[2] = ' $ '.join(split[2]) return split + # Gera nome de regra apartir de uma lista def gerar_nome_regra(self, lista): - self.__nomeRegra = [] + nome_regra = [] for t in lista: - self.__nomeRegra.append(t[1]) - return "_".join(self.__nomeRegra) + nome_regra.append(t[1]) + return "_".join(nome_regra) def verificar_adv_tempo(self, token): for tupla in self.lista: if self.dicionarios.has_adverbio_tempo(tupla[0]): - return self.verificar_vb_infinitivo(token) + return True return False + def verificar_excecao_plural(self, token): + return self.dicionarios.has_excecao_plural(token) + def verificar_vb_infinitivo(self, token): - if self.dicionarios.has_verbo_infinitivo(token): - return self.dicionarios.get_verbo_infinitivo(token) - return False + return self.dicionarios.has_verbo_infinitivo(token) def verificar_preposicao(self, token): return self.dicionarios.has_preposicao(token) @@ -331,29 +356,38 @@ class AplicaRegras(object): return self.dicionarios.has_adverbio_intensidade(token) def verificar_vb_muda_negacao(self, token): - if self.dicionarios.has_verbo_muda_negacao(token): - return self.dicionarios.get_verbo_muda_negacao(token) - return False + return self.dicionarios.has_verbo_muda_negacao(token) + + def get_vb_infinitivo(self, token): + if self.dicionarios.has_verbo_infinitivo(token): + return self.dicionarios.get_verbo_infinitivo(token) + return token + + def get_token_intensidade(self, token): + print 'TODO' + + # Simplifica a sentença para que possa evitar a ditalogia def simplificar_sentenca(self, lista): + lista_simplificada = list(lista) it = Iterator() - it.load(lista) + it.load(lista_simplificada) num = False - nova_lista = [] while(it.has_next()): - token = it.get_word() tag = it.get_ticket() if tag == "NUM": num = True - if tag[-2:] == "-P": - singular = self.analisar_plural(token) - lista[it.get_count()][0] = singular + if tag[-2:] == "-P" and self.verificar_excecao_plural(it.get_word()): + singular = self.analisar_plural(it.get_word()) + lista_simplificada[it.get_count()][0] = singular - if num: return self.converter_extenso(nova_lista) - return nova_lista + if num: + return self.converter_extenso(lista_simplificada) + return lista_simplificada + # Alterar uma palavra do plural para o singular def analisar_plural(self, token): if(token[-3:] == "OES" or token[-2:] == "AES" or token[-2:] == "AOS"): return token[0:-3]+"AO" @@ -376,32 +410,37 @@ class AplicaRegras(object): else: return token + # Converter número por extenso para numeral def converter_extenso(self, lista): - lista_aux = [] - index_deleted = [] + lista_extensos = [] + indices_deletar = [] count = 0 - is_running = False + is_sequence = False for i in range(0, len(lista)): token = lista[i][0] tag = lista[i][1] if tag == "NUM": - if (is_running is False and len(lista_aux) == count): - lista_aux.append([i,[token]]) - is_running = True + # Verifico se não há sequência de obtenção de extenso em andamento para começar a obter um nova sequência + if (is_sequence is False): # and len(lista_extensos) == count (???) + lista_extensos.append([i,[token]]) # i = Posição do primeiro extenso encontrado, token = número por extenso + is_sequence = True else: - lista_aux[count][1].append(token) - index_deleted.append(i) - elif (is_running): + lista_extensos[count][1].append(token) # Pego número por extenso que está na sequência e adiciona na lista + indices_deletar.append(i) # Insiro indice na lista para ser removido depois + elif (is_sequence): + # Se o token anterior e o próximo foram classificados como número, e o token atual como conjunção, significa que podemos remove-lo if ((lista[i-1][1] == "NUM") and (lista[i+1][1] == "NUM") and (tag == "CONJ")): - index_deleted.append(i) + indices_deletar.append(i) else: - is_running = False + # A sequência foi quebrada, o que significa que selecionamos o extenso do número por completo + # Podemos agora procurar por outra sequencia de número por extenso na lista + is_sequence = False count += 1 - for i in lista_aux: - ext = extenso(' '.join(i[1])) - lista[i[0]] = [ext, "NUM"] + for extenso in lista_extensos: + ext = convert_extenso(' '.join(extenso[1])) + lista[extenso[0]] = [ext, "NUM"] - deque((list.pop(lista, i) for i in sorted(index_deleted, reverse=True)), maxlen=0) + deque((list.pop(lista, i) for i in sorted(indices_deletar, reverse=True)), maxlen=0) return lista \ No newline at end of file -- libgit2 0.21.2