2012-04-24 15 views
5

Vorrei analizzare una frase in cui alcune stringhe potrebbero non essere quotate, "quotate" o "quotate". Il codice sottostante funziona quasi, ma non riesce a far corrispondere le virgolette di chiusura. Suppongo che questo sia dovuto al riferimento qq. Una modifica è commentata nel codice, la modifica riporta in "quotato" o "quotato" anche l'analisi e aiuta a mostrare il problema originale è con la citazione di chiusura. Il codice descrive anche la grammatica esatta.Stringhe tra virgolette con boost: spirit

Per essere completamente chiari: analisi stringhe non quotate. Una stringa quotata come 'hello' analizzerà la citazione aperta ', tutti i caratteri hello, ma non riuscirà a analizzare la citazione finale '.

Ho fatto un altro tentativo, simile al tag di inizio/fine corrispondenza nello boost tutorials, ma senza successo.

template <typename Iterator> 
struct test_parser : qi::grammar<Iterator, dectest::Test(), ascii::space_type> 
{ 
    test_parser() 
     : 
    test_parser::base_type(test, "test") 
    { 
     using qi::fail; 
     using qi::on_error; 
     using qi::lit; 
     using qi::lexeme; 
     using ascii::char_; 
     using qi::repeat; 
     using namespace qi::labels; 
     using boost::phoenix::construct; 
     using boost::phoenix::at_c; 
     using boost::phoenix::push_back; 
     using boost::phoenix::val; 
     using boost::phoenix::ref; 
     using qi::space; 

     char qq;   

     arrow = lit("->"); 

     open_quote = (char_('\'') | char_('"')) [ref(qq) = _1]; // Remember what the opening quote was 
     close_quote = lit(val(qq)); // Close must match the open 
     // close_quote = (char_('\'') | char_('"')); // Enable this line to get code 'almost' working 

     quoted_string = 
      open_quote 
      >> +ascii::alnum   
      >> close_quote; 

     unquoted_string %= +ascii::alnum; 
     any_string %= (quoted_string | unquoted_string); 

     test = 
      unquoted_string    [at_c<0>(_val) = _1] 
      > unquoted_string   [at_c<1>(_val) = _1] 
      > repeat(1,3)[any_string] [at_c<2>(_val) = _1] 
      > arrow 
      > any_string    [at_c<3>(_val) = _1] 
      ; 

     // .. <snip>set rule names 
     on_error<fail>(/* <snip> */); 
     // debug rules 
    } 

    qi::rule<Iterator> arrow; 
    qi::rule<Iterator> open_quote; 
    qi::rule<Iterator> close_quote; 

    qi::rule<Iterator, std::string()> quoted_string; 
    qi::rule<Iterator, std::string()> unquoted_string; 
    qi::rule<Iterator, std::string()> any_string;  // A quoted or unquoted string 

    qi::rule<Iterator, dectest::Test(), ascii::space_type> test; 

}; 


// main() 
// This example should fail at the very end 
// (ie not parse "str3' because of the mismatched quote 
// However, it fails to parse the closing quote of str1 
typedef boost::tuple<string, string, vector<string>, string> DataT; 
DataT data; 
std::string str("addx001 add 'str1' \"str2\"  -> \"str3'"); 
std::string::const_iterator iter = str.begin(); 
const std::string::const_iterator end = str.end(); 
bool r = phrase_parse(iter, end, grammar, boost::spirit::ascii::space, data); 

Per il credito bonus: una soluzione che evita un membro di dati locale (come char qq nell'esempio sopra) sarebbe preferibile, ma da un punto di vista pratico userò tutto ciò che funziona!

+0

Per la cronaca, rendendo 'char qq' una variabile membro della' struct test_parser' non esattamente nello stesso modo. – Zero

+0

Non riesce a "nello stesso modo?" Non ci hai detto come questo fallisce (anche se posso immaginarlo è dovuto al riferimento a 'qq'). –

+0

@NicolBolas È stato un commento nel codice: ho chiarito la domanda, grazie per averlo indicato.Sospetto anche il ref (qq), ma il lato negativo di boost lambda & co è che sono difficili da eseguire il debug in quanto non è possibile passare attraverso il senso tradizionale! – Zero

risposta

12

Il riferimento a qq diventa pendente dopo aver lasciato il costruttore, quindi è effettivamente un problema.

qi::locals è la canonica modo per mantenere stato locale all'interno di espressioni parser. L'altra opzione sarebbe quella di estendere la durata di qq (rendendolo un membro della classe di grammatica, ad es.). Infine, potresti essere interessato allo inherited attributes. Questo meccanismo ti offre un modo per chiamare una regola/grammatica con 'parametri' (passando lo stato locale intorno).

NOTA ci sono avvertimenti con l'uso dell'operatore Kleene +: è avido, e l'analisi fallisce se la stringa non termina con la citazione previsto.

Vedere un'altra risposta che ho scritto per gli esempi più completi di trattare contenuti arbitrari (opzionale/parzialmente) ha citato le stringhe, che permettono la fuga di citazioni all'interno di stringhe tra virgolette e altre cose del genere:

ho ridotto la grammatica al bit rilevanti, e comprendeva un paio di casi di test:

#include <boost/spirit/include/qi.hpp> 
#include <boost/spirit/include/phoenix.hpp> 
#include <boost/fusion/adapted.hpp> 

namespace qi = boost::spirit::qi; 

template <typename Iterator> 
struct test_parser : qi::grammar<Iterator, std::string(), qi::space_type, qi::locals<char> > 
{ 
    test_parser() : test_parser::base_type(any_string, "test") 
    { 
     using namespace qi; 

     quoted_string = 
       omit [ char_("'\"") [_a =_1] ]    
      >> no_skip [ *(char_ - char_(_a)) ] 
      >> lit(_a) 
     ; 

     any_string = quoted_string | +qi::alnum; 
    } 

    qi::rule<Iterator, std::string(), qi::space_type, qi::locals<char> > quoted_string, any_string; 
}; 

int main() 
{ 
    test_parser<std::string::const_iterator> grammar; 
    const char* strs[] = { "\"str1\"", 
          "'str2'", 
          "'str3' trailing ok", 
          "'st\"r4' embedded also ok", 
          "str5", 
          "str6'", 
          NULL }; 

    for (const char** it = strs; *it; ++it) 
    { 
     const std::string str(*it); 
     std::string::const_iterator iter = str.begin(); 
     std::string::const_iterator end = str.end(); 

     std::string data; 
     bool r = phrase_parse(iter, end, grammar, qi::space, data); 

     if (r) 
      std::cout << "Parsed: " << str << " --> " << data << "\n"; 
     if (iter!=end) 
      std::cout << "Remaining: " << std::string(iter,end) << "\n"; 
    } 
} 

uscita:

Parsed: "str1" --> str1 
Parsed: 'str2' --> str2 
Parsed: 'str3' trailing ok --> str3 
Remaining: trailing ok 
Parsed: 'st"r4' embedded also ok --> st"r4 
Remaining: embedded also ok 
Parsed: str5 --> str5 
Parsed: str6' --> str6 
Remaining: ' 
+0

Grazie, questo è esattamente quello che cercavo. Sarebbe in grado di inviare un collegamento a qualsiasi documentazione/esempi sulla gente del posto, mi ci è voluto un po 'per notare il 'qi :: local ' nella firma della regola, e sarebbe un buon riferimento per me e per chiunque altrimenti guardando questa domanda. – Zero

+0

@Zero grazie! E, erm ** ['qi :: locals'] (http://www.boost.org/doc/libs/1_48_0/libs/spirit/doc/html/spirit/qi/reference/parser_concepts/nonterminal.html# spirit.qi.reference.parser_concepts.nonterminal.locals) ** era un collegamento ipertestuale nella mia risposta :) - _clicalo per documentazione_ – sehe

+0

@Zero Per un buon esempio, mi riferisco alla pagina a cui hai collegato nella tua domanda, in particolare qui: [One More Take] (http://www.boost.org/doc/libs/1_49_0/libs/spirit/doc/html/spirit/qi/tutorials/mini_xml___asts_.html#spirit.qi.tutorials.mini_xml___asts_. one_more_take) – sehe

Problemi correlati