2012-05-22 3 views
5

In Boost :: Spirit, wie kann ich einen expectation_failure von einer Funktion auslösen, die an Boost::Bind gebunden ist?Wie man einen expectation_failure von einer Funktion in Boost Spirit wirft?

Hintergrund: Ich analysiere eine große Datei, die komplexe Einträge enthält. Wenn ein Eintrag mit einem vorherigen Eintrag inkonsistent ist, möchte ich einen Fehler auslösen und einen expectation_failure (der die richtige Syntaxanalyse enthält) auslösen. Wenn ich einen Eintrag analysiere, binde ich eine Funktion, die entscheidet, ob der Eintrag mit etwas übereinstimmt, das vorher gesehen wurde.

Ich habe ein kleines Spielzeugbeispiel erfunden, das den Punkt zeigt. Hier möchte ich einfach ein expectation_failure werfen, wenn die int nicht teilbar ist um 10:

#include <iostream> 
#include <iomanip> 
#include <boost/spirit/include/qi.hpp> 
#include <boost/bind.hpp> 
#include <boost/spirit/include/classic_position_iterator.hpp> 
namespace qi = boost::spirit::qi; 
namespace classic = boost::spirit::classic; 

void checkNum(int const& i) { 
    if (i % 10 != 0) // >> How to throw proper expectation_failure? << 
    std::cerr << "ERROR: Number check failed" << std::endl; 
} 

template <typename Iterator, typename Skipper> 
struct MyGrammar : qi::grammar<Iterator, int(), Skipper> { 
    MyGrammar() : MyGrammar::base_type(start) { 
    start %= qi::eps > qi::int_[boost::bind(&checkNum, _1)]; 
    } 
    qi::rule<Iterator, int(), Skipper> start; 
}; 

template<class PosIter> 
std::string errorMsg(PosIter const& iter) { 
    const classic::file_position_base<std::string>& pos = iter.get_position(); 
    std::stringstream msg; 
    msg << "parse error at file " << pos.file 
     << " line " << pos.line << " column " << pos.column << std::endl 
     << "'" << iter.get_currentline() << "'" << std::endl 
     << std::setw(pos.column) << " " << "^- here"; 
    return msg.str(); 
} 

int main() { 
    std::string in = "11"; 
    typedef std::string::const_iterator Iter; 
    typedef classic::position_iterator2<Iter> PosIter; 
    MyGrammar<PosIter, qi::space_type> grm; 
    int i; 
    PosIter it(in.begin(), in.end(), "<string>"); 
    PosIter end; 
    try { 
    qi::phrase_parse(it, end, grm, qi::space, i); 
    if (it != end) 
     throw std::runtime_error(errorMsg(it)); 
    } catch(const qi::expectation_failure<PosIter>& e) { 
    throw std::runtime_error(errorMsg(e.first)); 
    } 
    return 0; 
} 

ein expectation_failure Werfen würde bedeuten, dass ich eine Fehlermeldung wie diese auf ein int bekommen, die nicht teilbar ist um 10:

parse error at file <string> line 1 column 2 
'11' 
    ^- here 
+0

Können Sie anstelle von int_ eine andere Regel erstellen, die nur dann der Ganzzahl entspricht, wenn Ihre Bedingung erfüllt ist? Ich kenne Spirit nicht gut, aber ich würde annehmen, dass es eine ähnliche Regel wie r_bool in AX gibt, die ein Prädikat umschließt, das ist eine ganz normale Situation. –

+0

Ich denke, ich werde so etwas brauchen, leider: http://boost-spirit.com/home/articles/qi-example/creating-your-own-parser-component-for-spirit-qi/ – Frank

+0

Entschuldigung Das ist sehr benutzerunfreundlich. Deshalb brauchst du AX :-) –

Antwort

5

Ich bin mir nicht sicher, aber ich denke, dass Sie den _pass Platzhalter in Phoenix verwenden können, um einen Parse-Fehler zu erzwingen. So etwas wie sollte funktionieren.

bool myfunc(int i) {return i%10 == 0;} 

... 
_int [ _pass = phoenix::bind(myfunc,_1)] 
+0

Ja, das funktioniert. Vielen Dank! – Frank

+1

@Frank Bitte beachten Sie, dass das Setzen von _pass auf false nur die aktuelle Regel stoppt, aber nicht den Parser als Ganzes mit all den anderen Regeln (aka Ihre Grammatik). –

0

Jahre spät, aber trotzdem:

Wenn Sie unbedingt eine Ausnahme werfen wollen und wollen, dass die on_error es zu fangen, haben Sie die expectation_exception vom qi Namensraum zu werfen, da der Handler Fehler on_error fangen nichts tut sonst.

Dies kann für eine semantische Aktion oder eine benutzerdefinierte Parserimplementierung gelten.

würde so aussehen:

boost::throw_exception(Exception(first, last, component.what(context))); 

wo Exception ein qi::expactation_exception und nichts anderes ist.

Wenn Sie keine Komponente zur Hand haben wie in einer semantischen Aktion, müssen Sie Ihr eigenes qi::info Objekt anstelle von component.what(..) angeben.

Sie können von überall in einem Kontext von on_error geschützt werfen.

Verwandte Themen