2016-06-26 1 views
2

Je suis en train d'écrire un analyseur d'identifiant, qui analyse une chaîne alphanum qui n'est pas un mot-clé. les mots-clés sont dans une table:analyser les identifiants à l'exception des mots-clés

struct keywords_t : x3::symbols<x3::unused_type> { 
    keywords_t() { 
     add("for", x3::unused) 
       ("in", x3::unused) 
       ("while", x3::unused); 
    } 
} const keywords; 

et l'analyseur pour un identifiant doit être le suivant:

auto const identifier_def =  
      x3::lexeme[ 
       (x3::alpha | '_') >> *(x3::alnum | '_') 
      ]; 

maintenant j'essayer de combiner ces soi un analyseur d'identifiant échoue sur l'analyse d'un mot-clé. Je l'ai essayé comme ceci:

auto const identifier_def =  
       x3::lexeme[ 
        (x3::alpha | '_') >> *(x3::alnum | '_') 
       ]-keywords; 

et ceci:

auto const identifier_def =  
       x3::lexeme[ 
        (x3::alpha | '_') >> *(x3::alnum | '_') - keywords 
       ]; 

il fonctionne sur la plupart des entrées, mais si une chaîne commence par un mot-clé comme comme int, whilefoo, forbar l'analyseur ne parvient pas à analyser ces chaînes. comment puis-je obtenir ce parser correct?

+0

Vous voudrez peut-être regarder le libtooling de LLVM: http://clang.llvm.org/docs/LibTooling.html –

+2

Je me attendais aussi une telle sémantique de 'operator -', mais c'est plutôt différent. Il y a une discussion connexe [ici] (http://boost.2283326.n4.nabble.com/Parser-operator-Difference-td4675788.html). – Orient

Répondre

4

Votre problème est dû à la sémantique de l'opérateur de différence dans Spirit. Lorsque vous avez a - b Esprit fait ce qui suit:

  • vérifier si b matches:
    • si elle le fait, a - b échoue et rien est analysé.
    • si b échoue, il vérifie si a matches:
      • si a échoue, a - b échoue et rien est analysé.
      • si a réussit, a - b réussit et analyse toutes les analyses a.

Dans votre cas (unchecked_identifier - keyword) tant que l'identifiant commence par un mot-clé, keyword correspondra et votre analyseur échouera. Donc, vous devez échanger keyword avec quelque chose qui correspond chaque fois qu'un mot clé distinct est passé, mais échoue chaque fois que le mot-clé est suivi par quelque chose d'autre. Le not predicate (!) peut aider avec cela.

auto const distinct_keyword = x3::lexeme[ keyword >> !(x3::alnum | '_') ]; 

Échantillon complet (Running on Coliru):

//#define BOOST_SPIRIT_X3_DEBUG 
#include <iostream> 
#include <boost/spirit/home/x3.hpp> 

namespace parser { 
    namespace x3 = boost::spirit::x3; 

    struct keywords_t : x3::symbols<x3::unused_type> { 
     keywords_t() { 
      add("for", x3::unused) 
        ("in", x3::unused) 
        ("while", x3::unused); 
     } 
    } const keywords; 

    x3::rule<struct identifier_tag,std::string> const identifier ("identifier"); 

    auto const distinct_keyword = x3::lexeme[ keywords >> !(x3::alnum | '_') ]; 
    auto const unchecked_identifier = x3::lexeme[(x3::alpha | x3::char_('_')) >> *(x3::alnum | x3::char_('_'))]; 


    auto const identifier_def = unchecked_identifier - distinct_keyword; 

    //This should also work: 
    //auto const identifier_def = !distinct_keyword >> unchecked_identifier 


    BOOST_SPIRIT_DEFINE(identifier); 

    bool is_identifier(const std::string& input) 
    { 
     auto iter = std::begin(input), end= std::end(input); 

     bool result = x3::phrase_parse(iter,end,identifier,x3::space); 

     return result && iter==end; 
    } 
} 



int main() { 

    std::cout << parser::is_identifier("fortran") << std::endl; 
    std::cout << parser::is_identifier("for") << std::endl; 
    std::cout << parser::is_identifier("integer") << std::endl; 
    std::cout << parser::is_identifier("in") << std::endl; 
    std::cout << parser::is_identifier("whileechoyote") << std::endl; 
    std::cout << parser::is_identifier("while") << std::endl; 
} 
2

Le problème est que cela va sans lexer, qui est, si vous écrivez

keyword >> *char_ 

Et mettre en whilefoo il analysera while comme keyword et foo comme *char_.

Vous pouvez empêcher que de deux manières: soit besoin d'avoir un espace après le mot-clé, à savoir

auto keyword_rule = (keyword >> x3::space); 
//or if you use phrase_parse 
auto keyword_rule = x3::lexeme[keyword >> x3::space]; 

L'autre façon que vous avez décrite est également possible, à savoir retirer le mot-clé de la chaîne explicitement (I » d faire de cette façon):

auto string = x3::lexeme[!keyword >> (x3::alpha | '_') >> *(x3::alnum | '_')]; 

le problème avec votre définition est, qu'il interprétera le premier jeu de caractères que le mot-clé, en choisissant ainsi de ne pas l'analyser du tout. L'opérateur 'x-y' signifie analyser par x, mais pas y. Mais si vous passez 'whilefoo', il interprétera 'while' comme mot-clé et par conséquent n'analysera pas du tout.