#! /bin/bash
# Last edited on 2008-02-04 20:36:39 by stolfi

# Script para:
#   dado um texto de entrada escrito em portugues 
#   gerar como saida a analise sintatica desse texto

# path para executaveis (gawk, etc.)
export PATH="${PATH}:/usr/local/bin:/usr/bin:/bin"

# Diretorio do projeto
top="/home/msc2000/ra880518/parser/"

# caminho completo do dicionario
# lex="/home-ext/msc2000/ra880518/parser/dic/lex.dic"
lex="${STOLFIHOME}/projects/port-syntax/TMP/lucien/lex.dic"

# caminho completo do analisador
parser="${top}analisador/parser.plp"

# path da gramatica
# gramdir="${top}gramatica/"
gramdir="${STOLFIHOME}/projects/port-syntax/gram/sheila/"

# gramatica
gramusada="gramatica01.plg"
# gramusada="gramatica02.plg"

# caminho completo da gramatica
gram="${gramdir}${gramusada}"

# path das ferramentas
toolsdir="${top}prog-texto/"

# path das tabelas
tablesdir="${top}tabelas/"

# path do dicionario complementar
extradicdir="${PWD}/"

# caminho completo do dicionario complementar
extralex="${extradicdir}lex-extra.dic"

# path dos arquivos de entrada
indir="${PWD}/"

# path dos arquivos de saida
outdir="${PWD}/"

# path dos arquivos intermediarios gerados
tmpdir="${PWD}/"


# arquivos de ferramentas utilizados:
#   check-lex-format
#   text2word
#   word2arc
#   deduce-codes
#   dedice-clises
#   exclude-wrong-class
#   deduce-others
#   expand-contractions

# dicionario utilizado:
#   lex.dic

# tabelas de conversao utilizadas
#   conv-categ-names.tbn
#   categories.tbc

# dicionario complementar (entrada e saida):
#   lex-extra.dic

# arquivos de entrada:
#   text.txt

# arquivos de saida:
#   tree.txt
#   err.txt

# arquivos intermediarios gerados ou possivel de ser gerado:
#   wrong-lex-extra.err
#       words-pnt.lst (possivel)
#   arcs.tb1
#       sorted-by-end-node-arcs.tb1 (possivel)
#   last-node.pln
#       words-seq.lst (possivel)
#   corpus.lst
#   corpus-delimited.lst
#       found-in-lex-delimited.dic (possivel)
#   found-in-lex.dic
#   deduced-codes.dic
#   deduced-clises.dic
#	lex-extra-cr-rm.dic (possivel)
#       lex-extra-delimited.dic (possivel)
#       found-in-lex-extra-delimited.dic (possivel)
#   found-in-lex-extra.dic
#   found.dic
#       sorted-by-word-arcs.tb1 (possivel)
#       augmented.tb2 (possivel)
#	sorted-to-exclude.tb2 (possivel)
#   compacted.tb2
#       notclassified-lines.lst (possivel)
#   missing-words.lst
#       compacted-sorted-by-source.tb2 (possivel)
#       joined-by-source-word.dic (possivel)
#       possible-noun-adj-names.dic (possivel)
#       noun-adj-names.dic (possivel)
#       sorted-by-class-names.dic (possivel)
#   deduced-names.dic
#       deduced-names.lst (possivel)
#       missing-words2.lst (possivel)
#       deduced-others.dic (possivel)
#   lex-extra.dic
#       expanded.tb3 (possivel)
#	unique-expanded.tb3 (possivel)
#       sorted-by-class-expanded.tb3 (possivel)
#       graph.tb4 (possivel)
#   graph.pla
#   program.pl

# Formatacao dos arquivos pela extensao, especificando seus campos:
#
# txt:
#   texto da lingua portuguesa
#
# err:
#   1. mensagem de erro
#
# lst:
#   1. palavra
#
# dic:
#   1. palavra
#   2. classe codificada
#   3. canonica mista (palavra ou contracao)
#      sendo a contracao no formato (dic;dic) ou (dic;dic;dic)
#      onde dic e' composto dos seguintes campos:
#        1. palavra
#        2. classe codificada
#        3. canonica pura (palavra)
#
# tbc:
#   1. classe codificada
#   2. nome da classe para o prolog
#   3. tracos da classe separados por virgula (ex.: masc,pl)
#
# tbn:
#   1. classe codificada de substantivo ou adjetivo
#   2. classe codificada de substantivo nome proprio
#
# tb1:
#   1. no' inicial
#   2. no' final
#   3. palavra convertida
#   4. palavra original (Maiuscula se vinda no inicio de uma frase)
#   5. marcacao da palavra convertida, da palavra original e do seu posicionamento na frase,
#      sendo da forma abc onde:
#        a - palavra convertida e' minuscula (m) ou maiuscula (M)
#        b - palavra original e' minuscula (m) ou maiuscula (M)
#        c - posicionamento na frase e' inicio (i) ou meio (m)
#
# tb2:
#   1. no' inicial
#   2. no' final
#   3. palavra convertida
#   4. palavra original (Maiuscula se vinda no inicio de uma frase)
#   5. marcacao da palavra convertida, da palavra original e do seu posicionamento na frase,
#   6. classe codificada
#   7. canonica mista (palavra ou contracao)
#
# tb3:
#   1. no' inicial
#   2. no' final
#   3. palavra convertida
#   4. classe codificada
#   5. canonica pura (palavra)
#
# tb4:
#   1. nome da classe para o prolog
#   2. no' inicial
#   3. no' final
#   4. palavra convertida
#   5. canonica pura (palavra)
#   6. tracos da classe separados por virgula (ex.: masc,pl)
#
# pla:
#   1. arcos do grafo no formato predicado do prolog da seguinte forma:
#      nome_classe(no_inicial,no_final,palavra_convertida,canonica_pura,tracos_sep_virg).
#
# plb:
#   1. arcos do grafo no formato predicado do prolog da seguinte forma:
#      arco(no_inicial,no_final,palavra_original,nivel).
#
# pln:
#   último no' do grafo no formato predicado do prolog da seguinte forma: last_node(no_final).
#
# plg:
#   gramatica no formato predicados do prolog.
#
# plp:
#   parser no formato predicados do prolog.


# PROGRAMA DO SCRIPT # 

echo "etapa 01 - cria arquivo de dicionario extra"
# gera o arquivo do dicionario extra se inexistente
if [ ! -e ${extralex} ]; then
  touch ${extralex}
fi

echo "etapa 02 - verifica permissao de escrita para dicionario extra"
# se o arquivo do dicionario extra nao tem permissao de escrita, sai do script
if [ ! -w ${extralex} ]; then
  echo "Dicionario complementar 'lex-extra' sem permissao de escrita. Programa abortado" \
    > "/dev/stderr"
  exit 1
fi

echo "etapa 03 - checa formato do dicionario extra"
# renomeia dicionario extra ->lex-extra.dic~~
# verifica se o formato do dicionario extra esta correto ->wrong-lex-extra.err
# remove antigo dicionario extra ->lex-extra.dic
mv ${extralex} ${extralex}~~
gawk -f ${toolsdir}check-lex-format \
  ${tablesdir}categories.tbc ${extralex}~~ \
  > ${extralex} \
  2> ${tmpdir}wrong-lex-extra.err
rm ${extralex}~~

echo "etapa 04 - verifica se houve erros no formato do dicionario extra"
# se o formato nao esta correto, sai do script
if [ -s ${tmpdir}wrong-lex-extra.err ]; then
  echo "Dicionario complementar 'lex-extra' em formato incorreto. Programa abortado" \
    > "/dev/stderr"
  exit 1
fi

echo "etapa 05 - converte um texto em uma lista de arcos"
# converte um texto em uma lista sequencial das palavras do texto e ->words-pnt.lst
# converte a lista de palavras em uma lista de arcos ->arcs.tb1
#   onde pode haver mais de uma linha (palavra convertida) para a mesma palavra original
gawk -f ${toolsdir}text2word < ${indir}text.txt \
  | gawk -f ${toolsdir}word2arc -v arqarcos="${tmpdir}big-arcs.plb" > ${tmpdir}arcs.tb1

echo "etapa 06 - obtem o numero do ultimo no'"
# ordena os arcos pelo no' final ->sorted-by-end-node-arcs.tb1
# obtem o numero do ultimo no' ->last-node.pln
sort -k 2,2n ${tmpdir}arcs.tb1 \
  | awk '{last_node = $2} END {printf("no(%s).\n",last_node)}' > ${tmpdir}last-node.pln

echo "etapa 07 - extrai lista de palavras dos arcos"
# extrai a lista de palavras dos arcos e ->words-seq.lst
# ordena a lista de palavras excluindo repeticoes ->corpus.lst
awk '{printf("%s\n", $3)}' < ${tmpdir}arcs.tb1 \
  | sort -u > ${tmpdir}corpus.lst

echo "etapa 08 - delimita palavras do corpus com asterisco"
# delimita as palavras do corpus com asterisco ->corpus-delimited.lst
awk '{printf("*%s*\n", $0)}' < ${tmpdir}corpus.lst > ${tmpdir}corpus-delimited.lst

echo "etapa 09 - obtem o dicionario das palavras encontradas"
# procura as palavras do corpus no dicionario e ->found-in-lex-delimited.dic
# retira a delimitacao de asterisco das palavras encontradas ->found-in-lex.dic
fgrep -f ${tmpdir}corpus-delimited.lst ${lex} \
  | sed -e 's/[*]//g' \
  > ${tmpdir}found-in-lex.dic

echo "etapa 10 - deduz dicionario dos codigos"
# deduz lexico de numeros e codigos presentes no corpus ->deduced-codes.dic
gawk -f ${toolsdir}deduce-codes < ${tmpdir}corpus.lst > ${tmpdir}deduced-codes.dic

echo "etapa 11 - deduz dicionario de enclises e mesoclises"
# deduz lexico de enclises e mesoclises presentes no corpus -> deduced-clises.dic
# gawk -f ${toolsdir}deduce-clises < ${tmpdir}corpus.lst > ${tmpdir}deduced-clises.dic

echo "etapa 12 - obtem dicionario de palavras encontradas no dicionario extra"
# remove carriage return, linhas em branco e comentarios do dicionario extra, ->lex-extra-cr-rm.dic
# delimita as palavras com classificacao do dicionario extra com asterisco, ->lex-extra-delimited.dic
# procura as palavras do corpus no dicionario extra ->found-in-lex-extra-delimited.dic
# retira a delimitacao de asterisco das palavras encontradas ->found-in-lex-extra.dic
gawk -f ${toolsdir}remove-comments < ${extralex} \
  | awk '(NF == 3) {printf("*%s* %s %s\n", $1,$2,$3)}' \
  | fgrep -f ${tmpdir}corpus-delimited.lst \
  | sed 's/[*]//g' > ${tmpdir}found-in-lex-extra.dic

echo "etapa 13 - une os dicionarios obtidos "
# une os corpus encontrados, ordenando e eliminando ->found.dic
#   duplicacoes (caso o dicionario extra contenha lexico presente no dicionario normal)
sort -u \
  ${tmpdir}found-in-lex.dic \
  ${tmpdir}deduced-codes.dic \
  ${tmpdir}found-in-lex-extra.dic \
  > ${tmpdir}found.dic
# sort -u ${tmpdir}found-in-lex.dic \
#   ${tmpdir}deduced-codes.dic \
#   ${tmpdir}deduced-clises.dic \
#   ${tmpdir}found-in-lex-extra.dic \
#   > ${tmpdir}found.dic

echo "etapa 14 - obtem os arcos com classificacao de dicionario "
# ordena a lista de arcos pelo campo de palavra (convertida) ->sorted-by-word-arcs.tb1
# faz a juncao dos arcos contendo as palavras com a classificacao lexical encontrada ->augmented.tb2
# ordena inversamente para os arcos que ligam o mesmo no ficarem juntos ->sorted-to-exclude.tb2
#   e para as nao capitalizadas virem antes permitindo ao programa awk 
#   tratar as capitalizadas de acordo com o lexico encontrado para as nao capitalizadas para o mesmo no'
# exclui classificacoes improprias como: ->compacted.tb2
#   Pronomes, verbos e outros (nao subst nem adj) classificados como nome proprio quando iniciando uma frase
#   Palavras descapitalizadas sem classificacao derivadas de nomes proprios
#   Nomes proprios classificados como pronomes, verbos e outros (nao subst nem adj) quando nao iniciando uma frase
sort -k 3,3 ${tmpdir}arcs.tb1 \
  | join -a 1 -1 3 -2 1 -o 1.1,1.2,0,1.4,1.5,2.2,2.3 - ${tmpdir}found.dic \
  | sort -r \
  | gawk -f ${toolsdir}exclude-wrong-class > ${tmpdir}compacted.tb2

echo "etapa 15 - obtem a lista de palavras nao encontradas "
# seleciona as palavras para as quais nao foram encontradas classificacoes ->notclassified-lines.lst
# ordena e retira duplicacoes das palavras ->missing-words.lst
awk '(NF == 5) {printf("%s\n",$3)}' < ${tmpdir}compacted.tb2 \
  | sort -u > ${tmpdir}missing-words.lst

echo "etapa 16 - verifica se existe alguma palavra nao encontrada nos dicionarios "
# verifica se ha alguma palavra sem classificacao encontrada
if [ -s ${tmpdir}missing-words.lst ]; then
  echo "Há palavras sem classificação encontrada." > "/dev/stderr";

  echo "etapa 17a - deduz nomes proprios de palavras nao encontradas a partir de substantivos e adjetivos"
  # ordena o grafo compactado pelo campo de palavra original para poder executar o join sobre esse campo ->compacted-sorted-by-source.tb2
  # deduz nomes proprios se encontrar seus respectivos nao capitalizados classificados como substantivo ou adjetivo ->joined-by-source-word.dic
  # retira duplicacoes de classificacao ->possible-noun-adj-names.dic
  # filtra o resultado selecionando aqueles que tem classificacao e que seja substantivo ou adjetivo ->noun-adj-names.dic
  # ordena os nomes pela classe ->sorted-by-class-names.dic
  # substitui a classe substantivo ou adjetivo pela respectiva classe de substantivo nome proprio ->deduced-names.dic
  sort -k 4,4 ${tmpdir}compacted.tb2 > ${tmpdir}compacted-sorted-by-source.tb2
  join -1 1 -2 4 -o 0,2.6,2.7 ${tmpdir}missing-words.lst ${tmpdir}compacted-sorted-by-source.tb2 > ${tmpdir}joined-by-source-word.dic
  cat ${tmpdir}joined-by-source-word.dic  | sort -u > ${tmpdir}possible-noun-adj-names.dic
  cat ${tmpdir}possible-noun-adj-names.dic  | egrep -e '[ ]((aj...)|(sb...))[ ]' > ${tmpdir}noun-adj-names.dic
  cat ${tmpdir}noun-adj-names.dic  | sort -k 2,2 > ${tmpdir}sorted-by-class-names.dic
  cat ${tmpdir}sorted-by-class-names.dic  | join -1 2 -2 1 -o 1.1,2.2,1.3 - ${tablesdir}conv-categ-names.tbn > ${tmpdir}deduced-names.dic 
#  sort -k 4,4 ${tmpdir}compacted.tb2 > ${tmpdir}compacted-sorted-by-source.tb2
#  join -1 1 -2 4 -o 0,2.6,2.7 ${tmpdir}missing-words.lst ${tmpdir}compacted-sorted-by-source.tb2 \
#    | sort -u \
#    | egrep -e '[ ]((aj...)|(sb...))[ ]' \
#    | sort -k 2,2 \
#    | join -1 2 -2 1 -o 1.1,2.2,1.3 - ${tablesdir}conv-categ-names.tbn > ${tmpdir}deduced-names.dic 

  echo "etapa 18a - deduz siglas e outros nomes proprios adicionando-os ao dicionario extra "
  # renomeia dicionario extra ->lex-extra.dic~~
  # seleciona os nomes deduzidos provenientes de adjetivo e substantivo ->deduced-names.lst
  # seleciona palavras faltantes sem a deducao dos nomes classificados como adjetivo ou substantivo ->missing-words2.lst
  # deduz nomes proprios faltantes e siglas ->deduced-others.dic
  # adiciona os nomes proprios deduzidos e os nomes proprios faltantes ao arquivo lex-extra.dic ->lex-extra.dic
  # remove antigo dicionario extra ->lex-extra.dic
  mv ${extralex} ${extralex}~~
  awk '{printf("%s\n",$1)}' < ${tmpdir}deduced-names.dic \
    | comm -23 ${tmpdir}missing-words.lst - \
    | gawk -f ${toolsdir}deduce-others \
    | sort -u ${tmpdir}deduced-names.dic - ${extralex}~~ \
    > ${extralex}
  rm ${extralex}~~

else

  echo "etapa 17 - obtem o grafo com predicados prolog"
  # expande as contracoes ->expanded.tb3
  # retira duplicidade gerada na expansao de contracoes (ex. verbo-lhe-os, verbo-lhes-os) ->unique-expanded.tb3
  # ordena pelo campo classe da palavra ->sorted-by-class-expanded.tb3
  # substitui o codigo da categoria pelo nome e seus parametros ->graph.tb4
  # formata cada arco como um predicado prolog colocando palavras entre aspas simples ->graph.pla
  gawk -f ${toolsdir}expand-contractions < ${tmpdir}compacted.tb2 > ${tmpdir}expanded.tb3 
  cat ${tmpdir}expanded.tb3 \
    | sort -u \
    | sort -k 4,4 \
    | join -1 4 -2 1 -o 2.2,1.1,1.2,1.3,1.5,2.3 - ${tablesdir}categories.tbc \
    | gawk ' \
        //{ \
          gsub(/['\'']/,"'\'''\''"); \
          gsub(/[*]$/,""); \
          printf("x_%s(%s,%s,'\''%s'\'','\''%s'\''%s).\n",$1,$2,$3,$4,$5,$6); \
        } \
      ' > ${tmpdir}graph.pla

  echo "etapa 18 - compoe todo o arquivo do programa prolog"
  # une os arquivos do analisador, da gramatica, do numero do ultimo no e do grafo ->program.pl
  cat ${parser} ${gram} ${tmpdir}last-node.pln ${tmpdir}graph.pla ${tmpdir}big-arcs.plb > ${tmpdir}program.pl

  echo "etapa 19 - gera as arvores sintaticas"
  # gera as arvores sintaticas ->tree.txt
  pl -g "consult(['${tmpdir}program.pl'])" -t gram > ${outdir}tree.txt 2> ${outdir}err.txt

fi

echo "fim da execucao"

