#!/bin/bash
##############################################################################
#
# Script d'évaluation automatique du compilateur.
# Compile le programme source et compare la sortie avec la référence.
# Pour le code MIPS, compare la SORTIE de l'exécution sur Mars
# Comprend 
#   * analyse lexicale, 
#   * analyse syntaxique, 
#   * arbre abstrait, 
#   * table des symboles et
#   * code MIPS.
#
# ATTENTION : pour que le script marche, votre compilateur doit returner 0 em
# cas de succès, autre valeur en cas d'erreur (entrée ne compile pas 
# correctement) et doit permettre d'afficher les sorties sur stdout, sauf 
# éventuels messages d'erreur, sur stderr. On doit pouvoir, à tout moment,
# changer le type de sortie (lex, synt, asynt, tab, mips) avec des options.
#
##############################################################################
###     LLL      IIIIIII    RRRRRRR     EEEEEEEEE     ||||
###     LLL        III      RRR  RRR    EEEEEEEEE     ||||
###     LLL        III      RRR    RR   EEE           ||||
###     LLL        III      RRR  RRR    EEEEEEEE      ||||
###     LLL        III      RRRRRRR     EEEEEEEE    \\\\//// 
###     LLL        III      RRR RRR     EEE          \\\/// 
###     LLLLLLLL   III      RRR   RRR   EEEEEEEE      \\// 
###     LLLLLLLL IIIIIII    RRR   RRR   EEEEEEEEE      \/  
##############################################################################

# 1) MODIFIEZ LA VARIABLE CI-DESSOUS AVEC LE CHEMIN VERS VOTRE COMPILATEUR

MYCOMPILO="../compilateur-l"

# 2) DÉCOMMENTEZ + MODIFIEZ LES COMMANDES POUR GÉNÉRER LES DIFFÉRENTES SORTIES

MYCOMPILODEFAULT="${MYCOMPILO}"  # utilisé pour test reconnaissance et erreur
MYCOMPILOLEX="${MYCOMPILO} -l"   # exécuter l'analyseur lexical
#MYCOMPILOSYNT="${MYCOMPILO} -s"  # exécuter l'analyseur syntaxique
MYCOMPILOASYNT="${MYCOMPILO} -a" # afficher l'arbre abstrait
MYCOMPILOTAB="${MYCOMPILO} -t"   # afficher les tables des symboles
MYCOMPILOMIPS="${MYCOMPILO} -m"  # générer code MIPS
EXITONFAIL=1                     # mettre à zéro pour continuer après erreurs
MARS="./Mars4_5.jar"             # utilisez autre version de Mars si besoin
##############################################################################
# NE PLUS LIRE À PARTIR D'ICI ;-)
##############################################################################

declare -A testname
testname["lex"]="Analyse lexicale"
testname["synt"]="Analyse syntaxique"
testname["asynt"]="Arbre abstrait"
testname["tab"]="Table des symboles"
testname["mips"]="Code machine MIPS"

##############################################################################
# Fonction pour faire le diff textuel régulier (lex et tab).
# Cette fonction peut être passée en paramètre à la fonction diff_prog.
# @param $1 Fichier 1
# @param $2 Fichier 2
# @return 0 si pas de différence, autre sinon

function REGDIFF() {
  diff -q -w $1 $2
}

##############################################################################
# Fonction pour faire le diff entre deux fichiers XML (syn et asyn).
# Cette fonction peut être passée en paramètre à la fonction diff_prog.
# @param $1 Fichier XML 1
# @param $2 Fichier XML 2
# @return 0 si pas de différence, autre sinon

function XMLDIFF() {
  ./compare_arbres_xml $1 $2
}

##############################################################################
# Fonction pour faire le diff entre deux sorties d'exécution Mars (mips).
# Cette fonction peut être passée en paramètre à la fonction diff_prog.
# @param $1 Fichier mips 1
# @param $2 Fichier mips 2
# @param $3 Entrée à donner à l'exécution du programme (optionnel)
# @return 0 si pas de différence, autre sinon

function MARSOUTDIFF() {
  diff <(echo -e "$3" | java -jar $MARS $1 | tail -n +2 | sed ':a;N;$!ba;s/[ \n]//g') <(echo -e "$3" | java -jar $MARS $2 | tail -n +2 | sed ':a;N;$!ba;s/[ \n]//g')
}

##############################################################################
# Fonction pour faire le diff entre deux fichiers, vérifier et afficher le 
# résultat avec de belles couleurs.
# @param $1 Nom de la fonction utilisée pour faire le diff (parmi 3 ci-dessus)
# @param $2 Nom du fichier d'entrée à tester sans extension (p. ex. affect)
# @param $3 Extension du fichier à tester (p. ex. synt)
# @param $4 Entrée à donner à l'exécution du programme (optionnel)

function diff_prog() {
  diffprog=$1
  input=$2
  suffix=$3
  values=$4
  refoutput="ref-${suffix}/${input}.${suffix}"
  sysoutput="output/${input}.${suffix}"
  echo -e "\033[35m > ${testname[${suffix}]} (.${suffix})\033[0m"
  echo -e "`wc -l output/${input}.${suffix} | awk '{print $1}'` lignes"
  if [ -f ref-${suffix}/$input.${suffix} ]; then
    ${diffprog} output/${input}.${suffix} ref-${suffix}/${input}.${suffix} ${values} 2>&1 2> /dev/null
    if [ $? != 0 ]; then 
      echo -e "\033[31mTEST ${testname[${suffix}]} ÉCHOUÉ\033[0m"
      echo -e "Différences entre output/${input}.${suffix} ref-${suffix}/${input}.${suffix} en utilisant $diffprog:"
      ${diffprog} output/${input}.${suffix} ref-${suffix}/${input}.${suffix} ${values}
      if [ $EXITONFAIL = 1 ]; then exit 1; fi
    else
      echo -e "\033[32mTEST ${testname[${suffix}]} OK\033[0m"
    fi
  else
    echo -e "\033[34mRéférence ref-${suffix}/${input}.${suffix} absente\033[0m"
  fi
}

##############################################################################
# Exécute tous les tests (reco, lex, synt, asynt, tab, mips) pour un exemple
# donné qui doit être correctement compilé à tous les niveaux.
# @param $1 Nom du fichier d'entrée à tester sans extension (p. ex. affect)
# @param $2 Entrée à donner à l'exécution du programme (optionnel)

function test_fichier_ok() {
    input=$1
    echo -e "\n\033[4m ---- Test input/$input.l ----\033[0m"
    if [ -n "$2" ]; then
      echo "Input : $2"
    fi
    if [ -f input/$input.l ]; then
      # Reconnaissance : programme correct doit être accepté
      if [ -n "${MYCOMPILODEFAULT}" ]; then
        echo -e "\033[35m > Reconnaissance (accepte l'entrée)\033[0m"        
        ${MYCOMPILODEFAULT} input/$input.l > output/$input.synt
        if [ $? != 0 ]; then 
          echo -e "\033[31mTEST Reconnaissance ÉCHOUÉ\033[0m"
          echo -e "Le programme $input.l n'a pas été compilé correctement"	
          if [ $EXITONFAIL = 1 ]; then exit 1; fi
        else
          echo -e "\033[32mTEST Reconnaissance OK\033[0m"
        fi
      fi     
      # Teste analyseur lexical
      if [ -n "${MYCOMPILOLEX}" ]; then
        ${MYCOMPILOLEX} input/$input.l > output/$input.lex
        diff_prog REGDIFF $input lex
      fi 
      # Teste analyseur syntaxique
      if [ -n "${MYCOMPILOSYNT}" ]; then
        ${MYCOMPILOSYNT} input/$input.l > output/$input.synt          
        diff_prog XMLDIFF $input synt
      fi
      # Teste création de l'arbre abstrait
      if [ -n "${MYCOMPILOASYNT}" ]; then
        ${MYCOMPILOASYNT} input/$input.l > output/$input.asynt
        diff_prog XMLDIFF $input asynt
      fi
      # Teste replissage de la table des symboles
      if [ -n "${MYCOMPILOTAB}" ]; then
        ${MYCOMPILOTAB} input/$input.l > output/$input.tab
        diff_prog REGDIFF $input tab
      fi
      # Teste génération de code MIPS
      if [ -n "${MYCOMPILOMIPS}" ]; then
        ${MYCOMPILOMIPS} input/$input.l > output/$input.mips
        diff_prog MARSOUTDIFF $input mips "$2"
      fi
    else
      echo -e "\033[31minput/$input.l non trouvé\033[0m"
      echo -e "\033[31mTest impossible\033[0m"        
    fi
}

##############################################################################
# Vérifie qu'un programme contenant des erreurs n'est pas reconnu/compilé.
# @param $1 Nom du fichier d'entrée à tester sans extension (p. ex. affect)

function test_fichier_fail() {
	input=$1
    echo -e "\n\033[4m ---- Test input/$input.l ----\033[0m"
    ${MYCOMPILODEFAULT} input/$input.l > output/$input.synt.xml
    if [ $? = 0 ]; then 
    echo -e "\033[31mTEST REJET ÉCHOUÉ\033[0m"
      echo -e "Le programme $input.l est accepté mais il devrait être rejeté"
      if [ $EXITONFAIL = 1 ]; then exit 1; fi
    else
      echo -e "\033[32mTEST REJET OK\033[0m"
    fi
}

##############################################################################
#######  #####  ######   ######    ###  #####  ###############################
#######   ###   #####  #  ######  ####    ###  ###############################
#######    #    ####  ###  #####  ####  ##  #  ###############################
#######  #   #  ###         ####  ####  ###    ###############################
#######  ## ##  ##  #######  ##    ###  ####   ###############################
##############################################################################

mkdir -p output
make clean
make

################################################################################
# Vérifications initiales : MYCOMPILO et MARS bien configurés?

echo -e "Votre compilateur : ${MYCOMPILO}"
if [ ! -f  ${MYCOMPILO} ]; then
  echo -e "\033[31mCompilateur introuvable"
  echo -e "Modifiez la variable MYCOMPILO avant de lancer l'éval\033[0m"
  exit 1
fi
if [ -n "${MYCOMPILOMIPS}" -a ! -f  ${MARS} ]; then
  echo -e "\033[31mMARS introuvable"
  echo -e "Modifiez la variable MARS avant de lancer l'éval\033[0m"
  exit 1
fi

################################################################################

echo -e "\033[1m\n>> 1.1) Tests connus OK\033[0m"

test_fichier_ok eval1
test_fichier_ok eval2
test_fichier_ok eval3
# 3 cas de figure selon les entrées tapées
test_fichier_ok eval4 "3\n10\n"
test_fichier_ok eval4 "10\n10\n"
test_fichier_ok eval4 "-3\n-10\n"
test_fichier_ok eval5

################################################################################

echo -e "\033[1m\n>> 1.2) Tests connus FAIL\033[0m"

test_fichier_fail eval1-err

################################################################################

echo -e "\033[1m\n>> 2.1) Tests nouveaux OK\033[0m"

test_fichier_ok eval6
test_fichier_ok eval7
test_fichier_ok eval8
test_fichier_ok eval9
test_fichier_ok eval10
test_fichier_ok eval11
test_fichier_ok eval12
test_fichier_ok eval13
test_fichier_ok eval14
test_fichier_ok eval15
test_fichier_ok eval16
test_fichier_ok eval17
test_fichier_ok eval18
test_fichier_ok eval19
test_fichier_ok eval20 "0"
test_fichier_ok eval20 "1"
test_fichier_ok eval20 "10"
test_fichier_ok eval20 "16"
test_fichier_ok eval21 "-1"
test_fichier_ok eval21 "1"
test_fichier_ok eval21 "10"
test_fichier_ok eval21 "16"
test_fichier_ok eval22 "10\n5\n"
test_fichier_ok eval22 "31\n175\n"
test_fichier_ok eval22 "2\n2\n"
test_fichier_ok eval23 "1"
test_fichier_ok eval23 "0"
test_fichier_ok eval24 "7\n7\n7\n7\n"
test_fichier_ok eval24 "0\n7\n0\n7\n"
test_fichier_ok eval24 "7\n0\n7\n0\n"
test_fichier_ok eval24 "0\n0\n0\n0\n"

################################################################################

echo -e "\033[1m\n>> 2.2) Tests nouveaux FAIL\033[0m"

test_fichier_fail eval2-err
test_fichier_fail eval3-err
test_fichier_fail eval4-err
test_fichier_fail eval5-err
test_fichier_fail eval6-err
test_fichier_fail eval7-err
test_fichier_fail eval8-err
test_fichier_fail eval9-err
test_fichier_fail eval10-err
test_fichier_fail eval11-err
test_fichier_fail eval12-err

################################################################################

echo -e "\033[1m\n>> 3.1) Tests nouvelle fonctionnalité OK\033[0m"

test_fichier_ok eval25 "9"
test_fichier_ok eval25 "10"
test_fichier_ok eval25 "11"
test_fichier_ok eval26 "-50"
test_fichier_ok eval26 "50"
test_fichier_ok eval26 "150"
test_fichier_ok eval27

################################################################################
echo -e "\033[1m\n>> 3.2) Tests nouvelle fonctionnalité FAIL\033[0m"

test_fichier_fail eval13-err
