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

C# Discussion :

Parser un langage structuré pour en faire des objets


Sujet :

C#

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Expert confirmé
    Avatar de StringBuilder
    Homme Profil pro
    Chef de projets
    Inscrit en
    Février 2010
    Messages
    4 197
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Chef de projets
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2010
    Messages : 4 197
    Billets dans le blog
    1
    Par défaut Parser un langage structuré pour en faire des objets
    Bonjour,

    Je travaille avec une application qui utilise en interne un langage proche du SQL afin d'interroger ses données.

    J'ai de mon côté un programme qui reproduit la structure de la base afin d'interroger un module de cette application qui utilise une autre syntaxe.

    Sans trop entrer dans les détails, d'un côté, je peux interroger l'application avec cette syntaxe :
    Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    select (CoGrp, CoNo, Company, Country, FreeN1, KP.Sex, KP.LastName, KP.FirstName, KP.FreeN1)
    from (FI)
    where (CoGrp='1' AND CoNo='123')
    with (KP)
    where (PeGrp='1' AND PeNo='45')

    Et de l'autre, avec ce type de requête (qui fait la même chose) :
    Code xml : 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
     
    <?xml version='1.0'?>
    <request>
      <query>
        <tables>
          <table tablename="FI">
            <table tablename="KP"/>
          </table>
        </tables>
        <fields tablename="FI" fields="CoGrp,CoNo,Company,Country,FreeN1"/>
        <fields tablename="KP" fields="Sex,LastName,FirstName,FreeN1"/>
        <condition>
          <cond tablename="FI" fieldname="CoGrp" op="=" value="1"/>
          <cond tablename="FI" fieldname="CoNo" op="=" value="123"/>
        </condition>
        <condition>
          <cond tablename="KP" fieldname="PeGrp" op="=" value="1"/>
          <cond tablename="KP" fieldname="PeNo" op="=" value="45"/>
        </condition>
      </query>
    </request>

    Actuellement, j'ai écrit du code qui me permet de passer d'objets à la syntaxe XML :
    Code csharp : 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
     
    Request request = new(client);
    Query query = new(client);
    request.RequestElements.Add(query);
    Table fi = new Table(client, "FI");
    Table kp = new Table(client, "KP");
    fi.Tables.Add(kp);
    query.Tables.Add(fi);
    query.Fields.Add(new Fields(fi, "CoGrp,CoNo,Company,Country,FreeN1"));
    query.Fields.Add(new Fields(kp, "Sex,LastName,FirstName,FreeN1"));
    Condition conditionfi = new(fi);
    query.Conditions.Add(conditionfi);
    conditionfi.AddCondition("CoGrp", "=", "1");
    conditionfi.AddCondition("CoNo", "=", "123");
    Condition conditionkp = new(kp);
    query.Conditions.Add(conditionkp);
    conditionkp.AddCondition("PeGrp", "=", "1");
    conditionkp.AddCondition("PeNo", "=", "197874");
    Response response = client.SendRequest(request);

    En revanche, je ne sais pas trop comment m'y prendre pour parser la première syntaxe pour initialiser les objets.

    Je sais que c'est le genre de travaux qui se fait en cours d'école d'ingé (enfin, à une époque en tout cas c'était le cas) donc j'imagine que ce n'est pas ultra compliqué, mais pour ça faut une méthode...
    Si je cherche sur internet comment parser du code, je ne trouve que des librairies spécialisées (SQL, Python, etc.) sauf que moi je veux parser un langage spécifique pour lequel je ne trouverai pas de librairie toute faite...

    Le but recherché étant de pouvoir faire produire simplement des requêtes de la première syntaxe par les utilisateurs (d'autant qu'il y a un assistant pour les écrire) et qu'ensuite ils puissent les soumettre telles qu'elles à mon programme pour qu'il interroge les données en automatique.

    Des suggestions ?

  2. #2
    Expert confirmé
    Avatar de StringBuilder
    Homme Profil pro
    Chef de projets
    Inscrit en
    Février 2010
    Messages
    4 197
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Chef de projets
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2010
    Messages : 4 197
    Billets dans le blog
    1
    Par défaut
    Bon, finalement ce n'est peut-être pas aussi "dans les règles de l'art" que ça pourrait être, mais pour le moment ça me donne satisfaction

    Code csharp : 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
     
    using System.Text.RegularExpressions;
     
    namespace CRMInterface
    {
        public class AQLParser
        {
            private readonly CRMInterfaceClient Client;
            private readonly Request Request;
     
            public AQLParser(CRMInterfaceClient client, Request request)
            {
                Client = client;
                Request = request;
            }
     
            public Query Parse(string aql)
            {
                List<Table> tables = new();
     
                Query query = new(Client);
                string pattern = @"([^\(\)]*) \(([^\(\)]*)\)"; // Marche pas avec les conditions complexes (contenant des parenthèses)
                MatchCollection matches = Regex.Matches(aql, pattern);
     
                Match matchFrom = matches.First(a => a.Groups[1].Value.Trim() == "from");
                Table from = new(Client, matchFrom.Groups[2].Value.Trim());
                query.Tables.Add(from);
                tables.Add(from);
     
                for (int i = 0; i < matches.Count; i++)
                {
                    if (matches[i].Groups[1].Value.Trim() == "with")
                    {
                        Table with = new(Client, matches[i].Groups[2].Value.Trim());
                        from.Tables.Add(with);
                        tables.Add(with);
                        if (matches[i + 1].Groups[1].Value.Trim() == "where")
                        {
                            Condition condition = new(with);
                            query.Conditions.Add(condition);
                            string[] conds = matches[i + 1].Groups[2].Value.Split("AND", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
                            foreach (string cond in conds)
                            {
                                string pat = @"([^=<>]*)([=<>]*)([^=<>]*)";
                                MatchCollection ms = Regex.Matches(cond, pat);
                                string fieldName = ms[0].Groups[1].Value.Trim().Replace("\"", "").Replace("'", "");
                                if (!int.TryParse(fieldName, out _))
                                {
                                    fieldName = Client.GetFieldId(with, fieldName);
                                }
                                condition.AddCondition(fieldName, ms[0].Groups[2].Value.Trim(), ms[0].Groups[3].Value.Trim().Replace("\"", "").Replace("'", ""));
                            }
                        }
                    }
                    else if (matches[i].Groups[1].Value.Trim() == "from")
                    {
                        if (matches[i + 1].Groups[1].Value.Trim() == "where")
                        {
                            Condition condition = new(from);
                            query.Conditions.Add(condition);
                            string[] conds = matches[i + 1].Groups[2].Value.Split("AND", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
                            foreach (string cond in conds)
                            {
                                string pat = @"([^=<>]*)([=<>]*)([^=<>]*)";
                                MatchCollection ms = Regex.Matches(cond, pat);
                                string fieldName = ms[0].Groups[1].Value.Trim().Replace("\"", "").Replace("'", "");
                                if (!int.TryParse(fieldName, out _))
                                {
                                    fieldName = Client.GetFieldId(from, fieldName);
                                }
                                condition.AddCondition(fieldName, ms[0].Groups[2].Value.Trim(), ms[0].Groups[3].Value.Trim().Replace("\"", "").Replace("'", ""));
                            }
                        }
                    }
                }
     
                Match matchSelect = matches.First(a => a.Groups[1].Value.Trim() == "select");
                string[] fields = matchSelect.Groups[2].Value.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
                foreach (Table table in tables)
                {
                    if (table == from)
                    {
                        string[] fs = fields.Where(a => !a.Contains('.')).ToArray();
                        for (int i = 0; i < fs.Length; i++)
                        {
                            if (!int.TryParse(fs[i], out _))
                            {
                                fs[i] = Client.GetFieldId(table, fs[i]);
                            }
                        }
                        query.Fields.Add(new Fields(table, string.Join(',', fs)));
                    }
                    else
                    {
                        string[] fs = fields.Where(a => a.StartsWith(string.Concat(table.Code, '.'))).Select(a => a.Substring(a.IndexOf('.') + 1)).ToArray();
                        for (int i = 0; i < fs.Length; i++)
                        {
                            if (!int.TryParse(fs[i], out _))
                            {
                                fs[i] = Client.GetFieldId(table, fs[i]);
                            }
                        }
                        query.Fields.Add(new Fields(table, string.Join(',', fs)));
                    }
                }
     
                return query;
            }
        }
    }

    Utilisation :
    Code csharp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    Request request = new(client);
     
    AQLParser parser = new(client, request);
    Query query = parser.Parse("select (CoGrp, CoNo, Company, Country, FreeN1, KP.Sex, KP.LastName, KP.FirstName, KP.FreeN1) from (FI)\r\nwhere (CoGrp='1' AND CoNo='123')\r\nwith (KP)\r\nwhere (PeGrp='1' AND PeNo='197874')");
    request.RequestElements.Add(query);
     
    Response response = client.SendRequest(request);

    Reste plus qu'à nettoyer un peu le code et gérer les cas un peu plus complexes (imbrications de jointures, conditions plus évoluées, etc.)

  3. #3
    Expert confirmé
    Avatar de popo
    Homme Profil pro
    Analyste programmeur Delphi / C#
    Inscrit en
    Mars 2005
    Messages
    2 972
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Analyste programmeur Delphi / C#
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2005
    Messages : 2 972
    Par défaut
    Ta problématique ressemble à du parsing et à de l'interprétation de ce parsing.
    J'ai vu que tu as résolu, ton problème sans utiliser l'artillerie lourde.

    Mais si ça t'intéresse, j'ai écrit cet article qui parle du sujet :
    https://jeremy-laurent.developpez.co...s/interpreter/

  4. #4
    Expert confirmé
    Avatar de StringBuilder
    Homme Profil pro
    Chef de projets
    Inscrit en
    Février 2010
    Messages
    4 197
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Chef de projets
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2010
    Messages : 4 197
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par popo Voir le message
    Ta problématique ressemble à du parsing et à de l'interprétation de ce parsing.
    J'ai vu que tu as résolu, ton problème sans utiliser l'artillerie lourde.

    Mais si ça t'intéresse, j'ai écrit cet article qui parle du sujet :
    https://jeremy-laurent.developpez.co...s/interpreter/
    Merci pour ton lien (et ton intérêt )

    Je vais regarder ça de plus près, les expressions régulières ayant leurs limites, je pourrais être amené à changer de fusil d'épaule pour pouvoir gérer les requêtes plus complexes

Discussions similaires

  1. Réponses: 4
    Dernier message: 03/02/2011, 14h15
  2. [MySQL] extraire des mots concaténés ensemble pour en faire des liens
    Par oim dans le forum PHP & Base de données
    Réponses: 1
    Dernier message: 15/11/2007, 04h32
  3. [JDOM] Parser plusieurs fichiers xml pour en faire 1
    Par Mygush dans le forum Format d'échange (XML, JSON...)
    Réponses: 11
    Dernier message: 05/07/2007, 16h15
  4. Traiter lignes pour en faire des colonnes
    Par killapig dans le forum Shell et commandes GNU
    Réponses: 2
    Dernier message: 13/04/2007, 09h21
  5. Reformatter un doc word pour en faire des copies d'écran
    Par titouille dans le forum VBA Word
    Réponses: 4
    Dernier message: 22/09/2005, 18h21

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