IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage Pascal Discussion :

Reconnaissance d'un code source en Pascal


Sujet :

Langage Pascal

  1. #1
    Rédacteur/Modérateur

    Avatar de Roland Chastain
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2011
    Messages
    4 087
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2011
    Messages : 4 087
    Points : 15 506
    Points
    15 506
    Billets dans le blog
    9
    Par défaut Reconnaissance d'un code source en Pascal
    Bonjour !

    Je m'étais amusé à faire un formateur (?) de code basé sur les expressions régulières. Ce formateur devait supprimer les commentaires et espacer régulièrement les caractères. Cependant j'ai très vite rencontré un problème avec les chaînes littérales : il fallait les épargner, ce qui paraissait impossible avec les moyens que j'avais choisi d'utiliser. La solution que j'ai trouvée est d'extraire les chaînes littérales, de les placer dans un tableau, de les remplacer provisoirement dans le code par un matricule, afin de les restituer après traitement du code.

    Voici le programme que j'ai imaginé pour faire cela.

    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
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
     
    program extractor2;
     
    {$APPTYPE CONSOLE}
     
    uses
      Classes, SysUtils, IOUtils;
     
    function SubString(const aSource: string; aStart: integer; aEnd: integer = MAXINT): string;
    begin
      result := Copy(aSource, aStart, aEnd - aStart + 1);
    end;
     
    var
      allText: string;
      i, j, k: integer;
      c: char; { Commentaire. }
      l: integer; { Chaîne littérale. }
      d: boolean; { Directive compilateur. }
      tc, tl, td: TStringList; { Tableau commentaires, tableau chaînes littérales, tableau directives. }
     
    begin
      tc := TStringList.Create;
      tl := TStringList.Create;
      td := TStringList.Create;
     
      if (ParamCount = 1) and FileExists(ParamStr(1))then
        allText := TFile.ReadAllText(ParamStr(1))
      else
        allText := TFile.ReadAllText('extractor2.pp');
     
      i := 1;
      j := 0;
      k := 0;
     
      c := #0; { Pas de commentaire en cours. }
      l := 0; { Pas de chaîne littérale en cours. }
      d := FALSE; { Pas de directive en cours. }
     
      while i <= Length(allText) do
      begin
        if c <> #0 then { On est en train de parcourir un commentaire et on en cherche la fin. }
        begin
          { La variable c contient le premier caractère trouvé, qui permet de savoir à quel type de
             commentaire on a affaire et donc comment il se termine. }
          if ((c = '{') and (allText[i] = '}'))
          or ((c = '(') and (allText[i] = ')') and (allText[i - 1] = '*'))
          or ((c = '/') and (i < Length(allText)) and (allText[i + 1] = #13)) then
          begin
            k := i;
            tc.Append(SubString(allText, j, k));
            { Les commentaires sont purement et simplement supprimés. }
            allText := Format(
              '%s%s',
              [SubString(allText, 1, j - 1), SubString(allText, k + 1)]
            );
            c := #0;
            i := j - 1;
          end;
        end else
          if l <> 0 then { On est en train de parcourir une chaîne littérale et on en cherche la fin. }
          begin
            if allText[i] = '''' then { Si le caractère courant est un guillemet simple... }
            begin
              Inc(l); { on le compte. }
              { Si le nombre de guillemets trouvés est pair et que le caractère suivant n'est pas un
                guillemet, on a trouvé la fin de la chaîne. }
              if (l mod 2 = 0) and (i < Length(allText)) and (allText[i + 1] <> '''') then
              begin
                k := i;
                { On place une copie de la chaîne, guillemets compris, dans un tableau. }
                tl.Append(SubString(allText, j, k));
                { On remplace la chaîne littérale par un motif contenant le numéro de la chaîne, son
                  index dans le tableau. }
                allText := Format(
                  '%s_litteral_%d_%s',
                  [SubString(allText, 1, j - 1), tl.Count, SubString(allText, k + 1)]
                );
                l := 0;
                i := j - 1;
              end;
            end;
          end else
            { Même procédure pour les directives. }
            if d then
            begin
              if allText[i] = '}' then
              begin
                k := i;
                td.Append(SubString(allText, j, k));
                allText := Format(
                  '%s_directive_%d_%s',
                  [SubString(allText, 1, j - 1), td.Count, SubString(allText, k + 1)]
                );
                d := FALSE;
                i := j - 1;
              end;
            end else
              { Autrement, on cherche le début d'un commentaire, }
              if ((allText[i] = '{') and (i < Length(allText)) and (allText[i + 1] <> '$'))
              or ((allText[i] = '(') and (i < Length(allText)) and (allText[i + 1] = '*'))
              or ((allText[i] = '/') and (i < Length(allText)) and (allText[i + 1] = '/')) then
              begin
                j := i;
                c := allText[i];
              end else
                { ou d'une chaîne littérale, }
                if allText[i] = '''' then
                begin
                  j := i;
                  l := 1;
                end else
                  { ou d'une directive. }
                  if ((allText[i] = '{') and (i < Length(allText)) and (allText[i + 1] = '$')) then
                  begin
                    j := i;
                    d := TRUE;
                  end;
     
        Inc(i);
      end;
     
      TFile.WriteAllText('1.txt', allText);
     
      with TStringList.Create do
      begin
        for i := 0 to tc.Count - 1 do Append(Format('comment=[%s]', [tc[i]]));
        for i := 0 to tl.Count - 1 do Append(Format('literal=[%s]', [tl[i]]));
        for i := 0 to td.Count - 1 do Append(Format('directive=[%s]', [td[i]]));
        SaveToFile('2.txt');
        Free;
      end;
     
      tc.Free;
      tl.Free;
      td.Free;
    end.
    Si aucun argument n'est passé au programme, il traite pour démonstration son propre code source.
    Fichiers attachés Fichiers attachés

  2. #2
    Responsable Pascal, Lazarus et Assembleur


    Avatar de Alcatîz
    Homme Profil pro
    Ressources humaines
    Inscrit en
    Mars 2003
    Messages
    7 968
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 58
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ressources humaines
    Secteur : Service public

    Informations forums :
    Inscription : Mars 2003
    Messages : 7 968
    Points : 59 748
    Points
    59 748
    Billets dans le blog
    2
    Par défaut
    Bonjour Roland,

    J'ai compilé et testé ton code source : si l'on met un commentaire de type double slash, le programme ne traite plus par la suite aucun commentaire ni aucune chaîne.

    Si je n'ajoute pas la directive {$MODE DELPHI}, le paramètre de la forme aEnd: integer = MAXINT déclenche une erreur de compilation (Free Pascal 2.6.4 sous Linux).

  3. #3
    Rédacteur/Modérateur

    Avatar de Roland Chastain
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2011
    Messages
    4 087
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2011
    Messages : 4 087
    Points : 15 506
    Points
    15 506
    Billets dans le blog
    9
    Par défaut
    @Alcatîz

    Merci pour l'essai et le rapport d'erreur.

    Pour les commentaires de type double slash qui sont mal gérés, je n'observe pas cette erreur chez moi mais c'est sans doute lié aux fins de ligne différentes sous Linux ? Je pense que la modification suivante devrait résoudre le problème :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
          //or ((c = '/') and (i < Length(allText)) and (allText[i + 1] = #13)) then
          or ((c = '/') and (i < Length(allText)) and (allText[i + 1] in [#10, #13])) then
    Pour le mode de compilation, oui c'est vrai. Donc à l'avenir je penserai à mettre une directive directement dans le code.

  4. #4
    Responsable Pascal, Lazarus et Assembleur


    Avatar de Alcatîz
    Homme Profil pro
    Ressources humaines
    Inscrit en
    Mars 2003
    Messages
    7 968
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 58
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ressources humaines
    Secteur : Service public

    Informations forums :
    Inscription : Mars 2003
    Messages : 7 968
    Points : 59 748
    Points
    59 748
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par Roland Chastain Voir le message
    c'est sans doute lié aux fins de ligne différentes sous Linux ? Je pense que la modification suivante devrait résoudre le problème
    Oui, ça marche !

  5. #5
    Rédacteur/Modérateur

    Avatar de Roland Chastain
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2011
    Messages
    4 087
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2011
    Messages : 4 087
    Points : 15 506
    Points
    15 506
    Billets dans le blog
    9
    Par défaut
    Citation Envoyé par Alcatîz Voir le message
    Oui, ça marche !
    Merci pour le retour.

    Voici une version plus complète du programme, qui restitue les chaînes extraites (au moyen des expressions régulières de la bibliothèque FLRE).
    Fichiers attachés Fichiers attachés

  6. #6
    Expert confirmé
    Avatar de anapurna
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2002
    Messages
    3 444
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Mai 2002
    Messages : 3 444
    Points : 5 864
    Points
    5 864
    Par défaut
    salut

    cela marche pas mal même en Delphi 7 après quelque modification

  7. #7
    Rédacteur/Modérateur

    Avatar de Roland Chastain
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2011
    Messages
    4 087
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2011
    Messages : 4 087
    Points : 15 506
    Points
    15 506
    Billets dans le blog
    9
    Par défaut
    Citation Envoyé par anapurna Voir le message
    cela marche pas mal même en Delphi 7 après quelque modification
    Merci pour le retour. Oui, il y a cette modification à faire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    unit IOUtils;
     
    ...
     
    type 
      TFile = class
        class function ReadAllText(const aFileName: ansistring): ansistring; {inline; static;}
        class procedure WriteAllText(const aFileName: ansistring; const aText: ansistring); {inline; static;}
      end;
    J'ai testé aussi avec XE2 (avec la vraie unité IOUtils) : j'ai quelques avertissements relatifs aux types de chaînes mais le programme donne un résultat correct.

    Quant à la bibliothèque FLRE, elle passe d'un compilateur à l'autre sans aucun problème. Chapeau !

  8. #8
    Expert confirmé
    Avatar de anapurna
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2002
    Messages
    3 444
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Mai 2002
    Messages : 3 444
    Points : 5 864
    Points
    5 864
    Par défaut
    salut

    oui je me suis permis deux modif en faites

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    unit IOUtils;
     
    ...
     
    type 
      TFile = class
        class function ReadAllText(const f: ansistring): ansistring;{$IFDEF FPC} inline; static;{$ENDIF}
        class procedure WriteAllText(const f: ansistring; const t: ansistring); {$IFDEF FPC}inline; static;{$ENDIF}  
     end;
    et

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    program extractor3;
    ...
     
     if (ParamCount = 1) and FileExists(ParamStr(1))then
        txt := TFile.ReadAllText(ParamStr(1))
      else
        txt := TFile.ReadAllText( {$IFDEF FPC}'extractor3.pp'{$ELSE}'extractor3.dpr'{$ENDIF});
    voila sinon effectivement le reste passe bien

  9. #9
    Rédacteur/Modérateur

    Avatar de Roland Chastain
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2011
    Messages
    4 087
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2011
    Messages : 4 087
    Points : 15 506
    Points
    15 506
    Billets dans le blog
    9
    Par défaut
    @anapurna

    Merci pour tes corrections, que j'ai adoptées. Voici une version un peu améliorée du code. Au lieu d'utiliser trois listes différentes et d'y accéder par index, cette version du programme utilise une seule liste et y accède par clé. Du coup le code est plus court et donc plus compréhensible j'espère.
    Fichiers attachés Fichiers attachés

  10. #10
    Rédacteur/Modérateur

    Avatar de Roland Chastain
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2011
    Messages
    4 087
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2011
    Messages : 4 087
    Points : 15 506
    Points
    15 506
    Billets dans le blog
    9
    Par défaut
    Je vous propose une version retravaillée du code. Toutes les fonctions sont maintenant regroupées dans une seule classe.

    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
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
     
    program extractor5;
     
    {$I DIRECTIVES}
     
    uses
      Classes, SysUtils, IOUtils, FLRE, FLREUnicode;
    { https://github.com/BeRo1985/flre }
     
    function SubStr(const aSource: string; aStart: integer; aEnd: integer = MAXINT): string;
    begin
      result := Copy(aSource, aStart, aEnd - aStart + 1);
    end;
     
    type
      TExtractor = class
        private
          fText: string;
          fData: TStringList;
        public
          constructor Create(const aFileName: string);
          destructor Destroy(); override;
          procedure Extract(var aCount: integer; const aType: string; const aStart, aEnd: integer);
          function Callback(const aInput: PFLRERawByteChar; const aCaptures: TFLRECaptures): TFLRERawByteString;
          procedure Restore(const aType: string);
          procedure ExtractAll();
          procedure SaveText(const aFileName: string);
          procedure SaveData(const aFileName: string);
          procedure RestoreAll();
          procedure Remove(const aType: string);
      end;
     
    constructor TExtractor.Create(const aFileName: string);
    begin
      inherited Create();
      fData := TStringList.Create;
      fText := TFile.ReadAllText(aFileName);
    end;
     
    destructor TExtractor.Destroy();
    begin
      fData.Free();
      inherited Destroy();
    end;
     
    procedure TExtractor.Extract(var aCount: integer; const aType: string; const aStart, aEnd: integer);
    begin
      Inc(aCount);
      fData.Append(Format('_%s_%d_=%s', [aType, aCount, SubStr(fText, aStart, aEnd)]));
      fText := Format('%s_%s_%d_%s', [SubStr(fText, 1, aStart - 1), aType, aCount, SubStr(fText, aEnd + 1)]);
    end;
     
    function TExtractor.Callback(const aInput: PFLRERawByteChar; const aCaptures: TFLRECaptures): TFLRERawByteString;
    begin
      with aCaptures[0] do result := fData.Values[FLREPtrCopy(aInput, Start, Length)];
    end;
     
    procedure TExtractor.Restore(const aType: string);
    begin
      if Assigned(fData) then
        with TFLRE.Create(Format('_%s_\d+_', [aType]), []) do
        begin
          fText := ReplaceCallback(fText, Callback);
          Free();
        end;
    end;
     
    procedure TExtractor.ExtractAll();
    var
      c: char;
      l: integer;
      d: boolean;
      cCount,
      lCount,
      dCount,
      i,
      iStart,
      iEnd: integer;
    begin
      c := #0;
      l := 0;
      d := FALSE;
     
      cCount := 0;
      lCount := 0;
      dCount := 0;
     
      i := 1;
      iStart := 0;
      iEnd := 0;
     
      while i <= Length(fText) do
      begin
        if c <> #0 then
        begin
          if ((c = '{') and (fText[i] = '}'))
          or ((c = '(') and (fText[i] = ')') and (fText[i - 1] = '*'))
          or ((c = '/') and (i < Length(fText)) and (fText[i + 1] in [#10, #13])) then
          begin
            iEnd := i;
            Extract(cCount, 'comment', iStart, iEnd);
            c := #0; // => comment = FALSE
            i := iStart - 1;
          end;
        end else
     
          if l <> 0 then
          begin
            if fText[i] = '''' then
            begin
              Inc(l);
              if (l mod 2 = 0) and (i < Length(fText)) and (fText[i + 1] <> '''') then
              begin
                iEnd := i;
                Extract(lCount, 'literal', iStart, iEnd);
                l := 0; // => literal = FALSE
                i := iStart - 1;
              end;
            end;
          end else
     
            if d then
            begin
              if fText[i] = '}' then
              begin
                iEnd := i;
                Extract(dCount, 'directive', iStart, iEnd);
                d := FALSE; // => directive = FALSE
                i := iStart - 1;
              end;
            end else
     
              if ((fText[i] = '{') and (i < Length(fText)) and (fText[i + 1] <> '$'))
              or ((fText[i] = '(') and (i < Length(fText)) and (fText[i + 1] = '*'))
              or ((fText[i] = '/') and (i < Length(fText)) and (fText[i + 1] = '/')) then
              begin
                iStart := i;
                c := fText[i]; // => comment = TRUE
              end else
     
                if fText[i] = '''' then
                begin
                  iStart := i;
                  l := 1; // => literal = TRUE
                end else
     
                  if ((fText[i] = '{') and (i < Length(fText)) and (fText[i + 1] = '$')) then
                  begin
                    iStart := i;
                    d := TRUE; // => directive = TRUE
                  end;
     
        Inc(i);
      end;
    end;
     
    procedure TExtractor.SaveText(const aFileName: string);
    begin
      TFile.WriteAllText(aFileName, fText);
    end;
     
    procedure TExtractor.SaveData(const aFileName: string);
    begin
      fData.SaveToFile(aFileName);
    end;
     
    procedure TExtractor.RestoreAll();
    begin
      Restore('[a-z]+');
    end;
     
    {$IFDEF VERSION_1}
    procedure TExtractor.Remove(const aType: string);
    var
      captures: TFLREMultiCaptures;
      i: integer;
    begin
      with TFLRE.Create(Format('_%s_\d+_', [aType]), []) do
      begin
        MaximalDFAStates := 65536;
        MatchAll(fText, captures);
        for i := High(captures) downto Low(captures) do with captures[i, 0] do Delete(fText, Start, Length);
        Free;
      end;
    end;
    {$ELSE}
    procedure TExtractor.Remove(const aType: string);
    begin
      with TFLRE.Create(Format('_%s_\d+_', [aType]), []) do
      begin
        MaximalDFAStates := 65536;
        fText := Replace(fText, '');
        Free;
      end;
    end;
    {$ENDIF}
     
    var
      filename: string;
     
    begin
      if (ParamCount = 1) then
        filename := ParamStr(1)
      else
        filename := {$IFDEF FPC}'extractor5.pp'{$ELSE}'extractor5.dpr'{$ENDIF};
     
      if FileExists(filename) then
        with TExtractor.Create(filename) do
        begin
          ExtractAll();
          SaveText('1.txt');
          SaveData('2.txt');
          Remove('comment');
          SaveText('3.txt');
          RestoreAll();
          SaveText('4.txt');
          Free();
        end;
    end.

Discussions similaires

  1. Codes sources Pascal : où les trouver, comment participer ?
    Par Alcatîz dans le forum Contribuez
    Réponses: 5
    Dernier message: 14/03/2024, 18h16
  2. PasDoc : Utilitaire de documentation de code source Pascal
    Par forum dans le forum Outils à télécharger
    Réponses: 0
    Dernier message: 23/03/2011, 22h20
  3. [Pascal] Comment nettoyer un code source ?
    Par ILP dans le forum Outils
    Réponses: 2
    Dernier message: 09/10/2009, 23h59
  4. Réponses: 4
    Dernier message: 14/12/2008, 21h13

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo