Tutoriel HsIndex

La Fonction principale

Pour que le programme soit exécutable, il est nécessaire de créer une fonction main comme dans tous les autres langages de programmation.

Cette fonction est une monade de type main :: IO () ne retournant aucune valeur (()). Pour appel, une monade est une fonction particulière permettant de revenir à une façon de programmer proche de celle que l’on utilise avec d’autres langages de programmation.

Dans cette fonction :

  • Les fonctions sont exécutées de façon séquentielle (les unes à la suite des autres).

  • Les effets de bords sont autorisés. Ce qui est indispensable si on veut faire interagir le programme avec le monde extérieur.

  • Les changements d’états sont autorisés.

Analyse des arguments en ligne des commandes

La première chose à faire est de lancer la fonction processArgs de la bibliothèque cmdargs avec le mode principal que l’on a définit précédemment avec la fonction modesCLI

main :: IO ()
main = do
  opts <- processArgs $ modesCLI lstModes
  outputScreen opts

On récupère dans la variable opts le mode MyModes qui a été choisi par l’utilisateur et on le passe en argument à la fonction outputScreen qui lance la génération de l’index et sa sortie dans un fichier texte.

Génération de l’index et exportation des données

On traite chaque mode avec une empreinte spécifique de la fonction outputScreen

Pour la génération des indexes Français, Anglais, Allemand et Russe, on lance la recherche et la lecture des fichiers d’entrée et de style avec la fonction readAllFile.

outputScreen opts@(IndexFrench  fin fou fsty    ) = readAllFile
  fin
  fsty
  (parseIndexFile Nothing)
  (\ent idx -> do
    let entc = concatPagesItems ent
        ents = sortItems charsFrench { lstLetters = ordFrenchUpperLower } (equivItems False charsFrench entc)
        entd = splitIndex idx ents

    writeIndex fou idx entd
  )
  
outputScreen opts@(IndexEnglish fin fou fsty    ) = readAllFile
  fin
  fsty
  (parseIndexFile Nothing)
  (\ent idx -> do
    let entc = concatPagesItems ent
        ents = sortItems charsEnglish { lstLetters = ordEnglishUpperLower } (equivItems False charsEnglish entc)
        entd = splitIndex idx ents

    writeIndex fou idx entd
  )

...

outputScreen opts@(IndexCustom fin fou fsty fdef) = readDefinitionFile
  fdef
  (\def -> readAllFile
    fin
    fsty
    (parseIndexFile Nothing)
    (\ent idx -> do
      let entc = concatPagesItems ent
          ents = sortItems def { lstLetters = upperLower (lstLetters def) } (equivItems False def entc)
          entd = splitIndex idx ents

      writeIndex fou idx entd
    )
  )

La fonction readAllFile permet de lire un fichier d’index, un possible fichier de style, d’utiliser un parser pour le fichier d’index et d’exécuter une série d’opérations avec ces différents éléments.

readAllFile :: FilePath                   -- ^ The path to the index file.
            -> Maybe FilePath             -- ^ The path to the style file.
            -> Parsec String () t         -- ^ The parser to apply on the index file.
            -> (t -> IndexStyle -> IO ()) -- ^ Operations to apply with the parsed index data and style.
            -> IO ()
readAllFile fidx mbfsty parsfun fun = readIndexFile fidx parsfun (readStyleFile mbfsty . fun)

Les fonctions readIndexFile et readStyleFile permettent de lire respectivement les fichiers d’indexes et de styles.

Tout d’abord, on teste l’éxistence du fichier avec doesFileExist:

  • Si le fichier n’existe pas, on affiche un message d’erreur.

  • Si le fichier existe, on le lit avec readFile et on lance le parser d’index parse.

    • Si le parser échoue en retournant Left err, on affiche les erreurs err détectées par le parser.

    • Si le parser réussi en retournant Right resu, on exécute la fonction fun avec le résultat du parser.

readIndexFile :: FilePath           -- ^ The path to the index file.
              -> Parsec String () t -- ^ The parser to apply on the file to extract datas.
              -> (t -> IO ())       -- ^ List of operations to perform on parsed datas.
              -> IO ()
readIndexFile fidx parsfun fun = do
    e <- doesFileExist fidx
    if e
        then do
            f <- readFile fidx
            case parse parsfun "(stdin)" f of
                Left  err  -> do
                    putStrLn $ "!!! ERROR reading Index file : " ++ fidx
                    print err
                Right resu -> fun resu
        else putStrLn $ "/!\\ ERROR the Index file : " ++ fidx ++ " does not exist !\n"

readStyleFile fonctionne de la même manière a ceci près qu si on ne donne pas de fichier de style à chercher, le style par défaut styleBasic est appliqué.

readStyleFile :: Maybe FilePath        -- ^ The path to the index file.
              -> (IndexStyle -> IO ()) -- ^ Operations to apply with the parsed style.
              -> IO ()
readStyleFile Nothing     fun = do
    putStrLn "Using the default style"
    fun styleBasic

readStyleFile (Just fsty) fun = do
    e <- doesFileExist fsty
    if e
        then do
            f <- readFile fsty
            case parse (parseStyleFile styleBasic) "(stdin)" f of
                Left  err  -> do
                    putStrLn $ "!!! ERROR reading Style file : " ++ fsty
                    print err
                Right resu -> fun resu
        else putStrLn $ "/!\\ ERROR the Style file : " ++ fsty ++ " does not exist !\n"

Conclusion

Développer HsIndex m’aura permis de gagner encore en maitrise de Haskell et de développer une application qui pourra être utilisée par tous. À ce jour, HsIndex est parfaitement fonctionnel et permet de répondre a mon besoin de créer des indexes dans différentes langues.

Ci-dessous un début de ce qui sera bientôt l’extension de mon lexique en 4 langues dont les indexes ont été générés avec HsIndex et qui paraitra … un jour !

Classement alphabétique<br>Version Multilingue
Classement alphabétique
Version Multilingue