IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Voir le flux RSS

matser

unix2dos

Noter ce billet
par , 31/03/2021 à 02h13 (16574 Affichages)
Un convertisseur unix2dos
Les fichiers texte unix ont les fin de lignes indiqué avec le caractère de contrôle LF (line feed), tandis que ceux de windows sont les deux caractère
de contrôle CR (carriage return) et LF (line feed).
Pour convertir un fichier unix en fichier dos, rien de plus simple, il exste
des lignes de commandes pour cela. par exemple :

unix2dos -n fichier_entrée fichier_sortie

ou bien :

sed -i 's/\n/\n\r/g' fichier

Je vais vous proposer ici de faire la même chose, mais en un peu plus
compliqué, pour le fun. Il s'agit d'écrire un convertisseur unix2dos à la
manière d'un compilateur prar descente récursive prédictive suivant une
grammaire LL.
Ce convertisseur est constitué de 3 modules:
* Le scanneur, qui fournis à l'analyseur lexicale les caratère du fichier
d'entrée.
* L'analyseur lexical, qui rassemble ces caractères en lexemes et forunit
les unité lexicales pour l'analyseur syntaxique.
* L'analyseur syntaxique qui vérifie la bonne syntaxe du flot d'unité
lexical, puis emmet une traduction.

le scanneur et l'analyse lexicale

je ne m'attarderais pas sur le scanneur et l'analyseur lexical, car ce n'est
pas le but du sujet.
Sachez seulement que le scanneur lit les caractères du fichier source, et
les transmet,un par un, à l'analyseur lexical. Quant à ce dernier, il
regroupe ces caractères, en fait un lexemes et retourne l'unité lexicale
correspondant à ce lexeme, à l'analyseur syntaxique.

commun.hpp
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
#ifndef COMMUN_HPP
#define COMMUN_HPP

enum terminal{rc,car,dolar}; /*dolar est le terminal qui n'apparait
     			     dans  aucune grammaire. Il est utilisé pour
			     signiifer la fin du fichier d'entrée*/
#endif
uniLex.hpp
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#ifndef UNILEX_HPP
#define UNILEX_HPP

#include <string>
#include "commun.hpp"

class uniLex{
public:
  terminal getLex();
  void setLex(terminal const &x);
  std::string getLexeme();
  void setLexeme(std::string const &l);
private:
  terminal unitLex;
  std::string lexeme;
};

#endif
uniLex.cpp
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <string>
#include "uniLex.hpp"
#include "commun.hpp"


terminal uniLex::getLex(){
  return unitLex;
}
void uniLex::setLex(terminal const &x){
  unitLex=x;
}
std::string uniLex::getLexeme(){
  return lexeme;
}
void uniLex::setLexeme(std::string const &l){
  lexeme=l;
}
scanneur.hpp
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#ifndef SCANNEUR_HPP
#define SCANNEUR_HPP

#include <fstream>
#include <string>

constexpr int taille=4096;

class scanneur{
public:
  scanneur(std::string const& nomFichier);
  char carSuiv();//donne le caractère suivant à l'analyseur lexical
  int getTailleFichier();
private:
  std::ifstream fichier;
  char tampon[taille+1];// +1 car sentinelle EOF à la fin du tampon
  int Ntampon;
  int enAvant;
  int derniers;
  int tailleFichier;
};

#endif
scanneur.cpp
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
scanneur::scanneur(std::string const& nomFichier):Ntampon(0),enAvant(0),
						  derniers(0){
  fichier.open(nomFichier.c_str(),std::ios::binary|std::ios::in);
  fichier.seekg(0,std::ios_base::end);
  tailleFichier=fichier.tellg();
  fichier.seekg(0,std::ios_base::beg);
  if(taille<=tailleFichier){
    fichier.read(tampon,taille);
    Ntampon=1;
    enAvant=0;
    tampon[taille]=EOF;
  }
  else{
    derniers=tailleFichier;
    fichier.read(tampon,derniers);
    Ntampon=1;
    tampon[derniers]=EOF;
    enAvant=0;
  }
}
lexical.hpp
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#ifndef LEXICAL_HPP
#define LEXICAL_HPP
#include <string>
#include "uniLex.hpp"
#include "scanneur.hpp"

class lexical{
public:
  lexical(std::string const &nomFichier);
  uniLex anaLex();
private:
  char c;
  uniLex obtenirProchain();
  scanneur scan;
  int etat;
  std::string lexeme;
};

#endif
lexical.cpp
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <iostream>
#include <map>
#include <string>
#include "uniLex.hpp"
#include "scanneur.hpp"
#include "lexical.hpp"

lexical::lexical(std::string const &nomFichier):scan(nomFichier) { }

uniLex lexical::anaLex(){
  uniLex retour;
  retour=obtenirProchain();
  return retour;
}

uniLex lexical::obtenirProchain(){
  uniLex uniLexTrouve;
  bool trouve=false;
  std::string carUL;
  etat=0;
  carUL="";
  while(true){
    switch(etat){
    case 0:
      c=scan.carSuiv();
      if(c=='\n')
	etat=1;
      else if(c==EOF)
	etat=2;
      else
	etat=3;
      break;
    case 1:
      uniLexTrouve.setLex(rc);
      uniLexTrouve.setLexeme("\n");
      trouve=true;
      break;
    case 2:
      uniLexTrouve.setLex(dolar);
      trouve=true;
      break;
    case 3:
      uniLexTrouve.setLex(car);
      carUL+=c;
      uniLexTrouve.setLexeme(carUL);
      trouve=true;
      break;
    }
    if(trouve)
      break;
  }
  return uniLexTrouve;
}
syntaxique.hpp:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#ifndef SYNTAXIQUE_HPP
#define SYNTAXIQUE_HPP

#include <string>
#include "lexical.hpp"
#include "uniLex.hpp"
#include "commun.hpp"

class syntaxique{
public:
  std::string F();
  syntaxique(std::string const &nomFichier);
private:
  std::string RF(std::string const &h);
  std::string P(std::string const &h);
  void consommer(terminal s);
  lexical pe;
  uniLex a;
};
#endif
le fichier princ.cpp:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
#include <iostream>
#include "syntaxique.hpp"

int main(int argc,char *argv[]){
  syntaxique G(argv[1]);
  std::cout<<G.F();
}
la grammaire hors contexte

la grammaire de ce compilateur est la suivante:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
fichier      -> paragraphe resteFichier
resteFichier -> retourChariot paragraphe resteFichier
	     -> epsilon (epsilon est la production vide)
paragraphe   -> caractère paragraphe
	     -> epsilon
"caractères" et "retourChariot" représentent les terminaux.

"fichier" et "resteFichier" constituent une liste associative à gauche, et
éventuelllement vide, de paragraphes séparés par un retour chariot

"paragraphe" est une liste associative à droite, et éventuellement vide, de
caractères, sans séparateur.



le STDS (schéma de traduction dirigé par la syntaxe)

nous allons d'abord simplifier les noms:
F = fichier
P = paragraphe
RF = resteFichier
car = caractère
rc = retourChariot

le stds résultant est le suivant:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
F  -> {P.h = ""} P {RF.h = P.s} RF {F.s = RF.s}
RF -> rc {P.h = ""} P {RF1.h = RF.h + "\n\r" + P.s } RF1 {RF.s = RF1.s}
   -> epsilon {RF.s = RF.h}
P  -> car {P1.h = P.h + car} P1 {P.s = P1.s}
   -> epsilon {P.s = P.h}
les attributs se terminant par .s sont les attributs synthétisés et les
autres, se terminant par .h, sont les attributs hérités.
dans un arbre syntaxique décoré (voir un exemple dans la figure suivante),
les attributs synthétisés d'un nœud se calculent avec les attributs venant
des nœuds frères de droite et des nœuds fils, du nœuds considéré.
Les attributs hérités d'un nœud, se calculent avec les attributs des nœud
frères de gauche et du nœud parent de ce nœud.
Nom : dos2unix.png
Affichages : 12
Taille : 5,5 Ko
Les trois première productions indiquent une traduction dirigée par la
syntaxe d'une liste de P séparés par "\n\r", qui est la fin de ligne pour
les fichiers texte brut Windows.
Les deux dernières productions décrivent le paragraphe, qui est une suite
de caractères, différents de CR, qui n'utilisent pas de séparateurs.

déscente récursive prédictive

pour implémenter un stds par la méthode de la descente récursive
prédictive, nous associerons les non terminaux à des fonctions.
Les terminaux seront représentés par un appel à la fonction "consommer" qui
vérifie que le lexème courant est bien celui attendu, pour fournit l'unité
lexicale suivante. Les attributs synthétisés d'un non terminal sont la
valeur de retour des fonctions qui représente ce non terminal. Les
attributs hérités sont les paramètres d'entrée de ces fonctions.
Pour écrire ces fonctions, on choisit dans cette fonction la séquence qui
correspond au premier symbole du non terminal. Ce symbole est indiqué par
la fonction (une fonction mathématique et non une fonction C++) appelé
"premier" dont la valeur dépend du non terminal courant. Voici ces
valeurs:
premier(F)={car;rc;epsilon}
premier(RF)={rc;epsilon}
premier(P)={car,epsilon}

la fonction F() devra s'écrire comme cela:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
std::string syntaxique::F(){
  std::string RFh,Ps,Fs,RFs;
  terminal x;
  x=a.getLex();
  Fs="";
  if(x==car||x==rc){ //x==rc car P peut être vide
    Ps=P("");
    RFh=Ps;
    RFs=RF(RFh);
    Fs=RFs;
  }
  return Fs;
}
voici le fichier syntaxique.cpp:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <string>
#include <iostream>
#include "syntaxique.hpp"

syntaxique::syntaxique(std::string nomFichier):pe(nomFichier){
  //pe est l'objet de la classe "lexical"
  a=pe.anaLex(); //ici, a est la première unité lexicale
}

std::string syntaxique::F(){
  std::string RFh,Ps,Fs,RFs;
  terminal x;
  Fs="";
  x=a.getLex();
  if(x==car||x==rc){ //x==rc car P peut être vide
    Ps=P("");
    RFh=Ps;
    RFs=RF(RFh);
    Fs=RFs;
  }
  return Fs;
}

std::string syntaxique::RF(std::string const &h){
  std::string Ps,RF1s,RFs,hprim;
 if(a.getLex()==rc){ //unité lexicale rc
    consommer(rc);
    Ps=P("");
    hprim=h+"\n\r"+Ps;
    RF1s=RF(hprim);
    RFs=RF1s;
  }
  else{
    RFs=h;
  }
  return RFs;
}

std::string syntaxique::P(std::string const &h){
  std::string aOld,P1h,Ps,P1s;
  if(a.getLex()==car){
    aOld=a.getLexeme();//getLexeme est le lexeme
    consommer(car);
    P1h=h+aOld;
    P1s=P(P1h);
    Ps=P1s;
  }
  else
    Ps=h;
  return Ps;
}

void syntaxique::consommer(terminal s){
  if(a.getLex()==s) // si l'unité lexicale lu est celle attendu
    a=pe.anaLex(); //unité lexicale suivante
  else
    std::cerr<<"erreur";
}

Envoyer le billet « unix2dos » dans le blog Viadeo Envoyer le billet « unix2dos » dans le blog Twitter Envoyer le billet « unix2dos » dans le blog Google Envoyer le billet « unix2dos » dans le blog Facebook Envoyer le billet « unix2dos » dans le blog Digg Envoyer le billet « unix2dos » dans le blog Delicious Envoyer le billet « unix2dos » dans le blog MySpace Envoyer le billet « unix2dos » dans le blog Yahoo

Mis à jour 03/04/2021 à 11h40 par matser

Tags: c++
Catégories
Sans catégorie

Commentaires