Flex/Bison ou Boost.Spirit ?

a marqué ce sujet comme résolu.

Salut,

Dans le cadre du projet de création d'un langage de programmation de la communauté, je cherche un outil d'analyse lexicale et syntaxique. Comme l'indique le titre, j'hésite entre Boost.Spirit et Flex/Bison :

  • d'un côté, Boost.Spirit est une lib C++, donc vraisemblablement plus adaptée pour du C++, alors que Flex/Bison sont à l'origine faits pour le C.
  • de l'autre, je n'ai pas entendu dire que du bien de Boost.Spirit.

Qu'en pensez-vous ?

Merci d'avance ! :)

EDIT : Si vous connaissez une meilleure alternative, je suis tout ouï.

+0 -0

Plop,

Je n'ai pas vraiment d'avis sur la question, principalement parce que je n'ai pas essayé Bison et ai à peine joué avec Spirit. Mais attention à la date des critiques, Spirit 3 (Spirit.x3) est plutôt récent (l'année dernière) et simplifie un peu l'écriture par rapport aux anciennes versions. À voir quels sont les arguments.

Après, la syntaxe de x3 est moins élégante que du pur BNF (cela reste du C++), mais reste lisible. Concrètement, les opérateurs sont inversés (a* devient *a) et les espaces remplacés par des opérateurs >>/>. Il y aussi des opérateurs sympa comme %. On se chope par contre les problèmes des pré-déclarations, ce qui revient souvent à décrire la syntaxe à l'envers pour des raisons de commodité: les éléments de 'bas niveau' sont décrits en premier.

1
2
3
4
exp_list ::= exp ( ',' exp ) +
exp ::= int op int
int ::= [0-9]+
op ::= [+-/*]
1
2
3
4
auto int_ = x3::int_;
auto op = x3::char_("+-/*");
auto exp = int_ >> op >> int_;
auto exp_list = exp % ',';

Comme c'est du c++, on peut plus facilement faire des variations, grossièrement, des fonctions qui retournent des parseurs. Cela peut alléger l'écriture quand des actions entrent en jeux.

Pour une alternative, j'ai vu Ragel dans les paquets Ubuntu. Cela crée une machine à état finie à base de goto. À mon avis, ce n'est pas très différent de Bison -peut-être même syntaxiquement compatible-, mais tu peux toujours jeter un œil.

+2 -0

J'ai lu des critiques qui disaient :

  • que les temps de compilation étaient longs, mais à la limite, on s'en fiche pour un projet de ce type ;
  • que les messages d'erreur étaient cryptiques, mais vu que j'aime bien les templates, ça ne devrait pas me changer de mes habitudes ; :)
  • mais ce qui m'a le plus gêné, c'est que j'ai entendu que Spirit n'était pas très stable d'une version à l'autre.
+0 -0

Quid de Antlr et de Clang ?

Gawaboumga

Comment ça, clang ? Clang est une suite de compilateurs. Si tu veux parler de LLVM, c'est du backend, donc pas d'analyse sémantique et syntaxique.

Quant à antlr, j'irai regarder, merci. ;)

EDIT : Ça m'a tout l'air d'être pour du Java, non ?

EDIT 2 : Apparemment, il existe un portage pour le C++.

+0 -0

Le truc qui m'a gêné avec spirit, c'est que je peine à trouver des exemples pour lire des données fortement typées de façon désordonnée.

Lire un John Doe 24 +33642424242 Avenue du ZdS vers une structure/classe qui a 3 chaines, un age, et un numéro_de_tel est facile.

Mais lire depuis un truc plus structuré (json, or autre) vers une structure typée, je ne trouve pas. Je trouve juste comment lire vers des tables de clé-variant.

@lmghs: Avec un système de flags ?

 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
#include <boost/spirit/home/x3.hpp>

#include <iostream>
#include <iomanip>
#include <string>

int main()
{
  struct { int a, b; } data {};

  namespace x3 = boost::spirit::x3;


  unsigned init_flags = 0;
  unsigned current_flag = 1;

  auto m = [&init_flags, &current_flag](auto & member) {
    auto f = [&member, &init_flags, flag=current_flag](auto & ctx) {
      if (init_flags & flag) {
        _pass(ctx) = false;
        return ;
      }
      init_flags |= flag;
      member = _attr(ctx);
    };
    current_flag <<= 1;
    return f;
  };

  auto check_initialized = [&init_flags, &current_flag](auto & ctx){
    if ((init_flags & (current_flag-1)) != (current_flag-1)) {
      _pass(ctx) = false;
    }
  };

  auto json_value = [](char const * key, auto parser){
    return x3::lexeme['"' >> x3::lit(key) >> '"'] >> ':' >> parser;
  };

  auto values =
    json_value("a", x3::int_[m(data.a)])
  | json_value("b", x3::int_[m(data.b)])
  ;

  auto parser = (values % ',')[check_initialized];

  // auto values = members(
  //   ref("a", data.a)
  //   ref("b", data.b)
  // );
  // auto json_values = transform(values.parser, to_json_value);
  // auto parser = (json_values % ',')[values.checker];


  std::cout << R"~(example: `"a": 3, "b": 1`)~""\n> ";

  std::string s;
  if (std::getline(std::cin, s)) {
    auto first = begin(s);
    auto last = end(s);
    x3::phrase_parse(first, last, parser, x3::space);
    if (first != last) {
      std::cerr << "  " << std::setw(int(1 + (first - begin(s)))) << '^' << "~~ error here\n";
      return 1;
    }
    std::cout << "a = " << data.a << "  b = " << data.b << "\n";
  }
  else {
    return 2;
  }
  return 0;
}
+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte