Codeschnipsel verbessern / verschönern
-
Ich möchte Funktionsparameter parsen, und der folgende Code gefällt mir nicht so besonders.
Es geht darum den sauberer zu machen. Und evtl habe ich einen Fall übersehen, der hier durchschlüpft, obwohl er falsch ist, oder einen der nicht funktioniert, obwohl er stimmt.
Variablennamen dürfen nur normale Buchstaben enthalten (bis jetzt)
[unter dem code ist noch text]
#include <iostream> #include <string> #include <algorithm> #include <boost/regex.hpp> #include <boost/tokenizer.hpp> int main () // nur zum testen, soll später in eine Funktion { // Variablen dürfen nur buchstaben enthalten std::string VmaskStr = R"([A-Za-z]+)"; boost::regex Varmask(VmaskStr); /* Valid: (typen und parameternamen (buchstaben only)) int abc, float b *empty* int AbD Invalid: int 2, float c int 2a int a2 int2 a int a float b int a, float b_ int a, float b char */ auto a = std::begin(/* hier ein array von strings, den ich zum testen benutze, habe ich entfernt + die schleife */); boost::tokenizer<> tok(*a); unsigned int counter = 0; // hiermit prüfe ich die Anzahl an Wörtern for (boost::tokenizer<>::iterator i = tok.begin(); i != tok.end(); ++i) { ++counter; boost::smatch match; if (!boost::regex_match(*i, match, Varmask)) { std::cout << "[INVALID] "; } if (typeswitch) std::cout << "TYPE: "; else { std::cout << "VARNAME: "; } std::cout << *i << "\n"; typeswitch = !typeswitch; } if (counter % 2) { std::cout << "invalid amount of words\n"; } if (counter != 0 && (counter / 2U) - 1U != std::count(a->begin(), a->end(), ',')) { std::cout << "amount of ',' does not match the amount of words\n"; } return 0; }
Was mir nicht gefällt:
- counter, ich wollte eigentlich die differenz der tokenizer pointer verwenden, aber da lande ich im Header von boost. "has no member named 'distance_to'"
- Die boolesche Variable zum toggeln, ob typ oder vname, ich würde lieber paare bekommen
EDIT: verdammt, der tokenizer schneidet ja auch Zeichen wie ";" gleich raus. :|
-
Gegenvorschlag:
#include <boost/spirit/include/qi.hpp> #include <boost/fusion/include/std_pair.hpp> #include <string> #include <utility> #include <vector> typedef std::vector<std::pair<std::string, std::string> > parameter_list; bool parse_parameter_list(std::string const &s, parameter_list &list) { using namespace boost::spirit::qi; std::string::const_iterator iter = s.begin(); bool r = phrase_parse(iter, s.end(), // --> Grammatik hier <-- -((lexeme[+alpha] >> lexeme[+alpha]) % ','), space, list); return r && iter == s.end(); } void dump_parameter_list(parameter_list const &vec) { std::cout << "-------------\n"; for(std::size_t i = 0; i < vec.size(); ++i) { std::cout << vec[i].first << ", " << vec[i].second << '\n'; } std::cout << "-------------\n\n"; } void check_parameter_list(std::string const &s) { parameter_list ls; if(parse_parameter_list(s, ls)) { dump_parameter_list(ls); } else { std::cout << "invalid!\n"; } } int main() { check_parameter_list("int abc, float b"); check_parameter_list(""); check_parameter_list("int AbD"); check_parameter_list("int 2, float c"); check_parameter_list("int 2a"); check_parameter_list("int a2"); check_parameter_list("int2 a"); check_parameter_list("int a float b"); check_parameter_list("int a, float b_"); check_parameter_list("int a, float b char"); }
-
Das sieht supi aus.
*nimmt sich kaffe und liest sich durch Spirit 2.5.2*.Ich werde wohl ein bisschen refactoring betreiben müssen
EDIT: Die boost Dokumentation ist richtig gut...
EDIT: Was ich in einem der Beispiele gefunden habe:
Try defining the roman numerals grammar in YACC or PCCTS. Spirit rules!
haha
EDIT: [lange pause] Bin jetzt bei lexeme
EDIT: Fertig! *ding*
- Wieder etwas mehr, das ich kann
EDIT: Ich schreibe gerade den kompletten Funktionsparser um. Ich poste das Ergebnis, wenn es soweit ist.
-
Ok Ich habe nun den Funktionsparser neu geschrieben mittels boost::spirit::qi
Kritik erwünscht.#include <iostream> #include <string> #include <vector> #include <boost/spirit/include/qi.hpp> #include <boost/fusion/include/std_pair.hpp> #include <boost/spirit/include/phoenix_core.hpp> #include <boost/spirit/include/phoenix_operator.hpp> #include <boost/spirit/include/phoenix_stl.hpp> #include <boost/spirit/include/phoenix_fusion.hpp> #include <boost/spirit/include/phoenix_object.hpp> #include <boost/fusion/include/adapt_struct.hpp> namespace QiSubroutines { namespace qi = boost::spirit::qi; namespace ascii = boost::spirit::ascii; namespace fusion = boost::fusion; namespace phoenix = boost::phoenix; typedef std::vector <std::pair <std::string, std::string> > paramContainerType; struct SubroutineBase { std::string name; paramContainerType parameters; }; } BOOST_FUSION_ADAPT_STRUCT( QiSubroutines::SubroutineBase, (std::string, name) (QiSubroutines::paramContainerType, parameters) ) namespace QiSubroutines { template <typename iterator_type> struct subroutine_parser : qi::grammar <iterator_type, SubroutineBase(), ascii::space_type> { subroutine_parser(): subroutine_parser::base_type(start, "subroutine") { using qi::lexeme; using ascii::char_; using qi::alpha; using qi::space; using qi::on_error; using qi::fail; using phoenix::at_c; using phoenix::push_back; using namespace qi::labels; using phoenix::construct; using phoenix::val; valid_text %= lexeme[+alpha]; argpair = lexeme[+alpha [at_c<0>(_val) += _1]] > lexeme[+alpha [at_c<1>(_val) += _1]] >> -char_(',') ; start = valid_text [at_c<0>(_val) = _1] > char_('(') >> *argpair [phoenix::push_back(at_c<1>(_val), _1)] > char_(')') > char_(';') ; start.name("qi_subroutine"); argpair.name("argpair"); valid_text.name("valid_text"); on_error<fail> ( start , std::cout << val("Error in subroutine parser - expecting: ") << _4 << val( " here: \"") << construct<std::string> (_3, _2) << val("\"\n") ); } qi::rule <iterator_type, std::string(), ascii::space_type> valid_text; qi::rule <iterator_type, std::pair<std::string, std::string> (), ascii::space_type> argpair; qi::rule <iterator_type, SubroutineBase(), ascii::space_type> start; }; }
Main
int main() { std::string str = "Name(int a, double b);"; using QiSubroutines::SubroutineBase; using QiSubroutines::subroutine_parser; using boost::spirit::ascii::space; using boost::spirit::qi::phrase_parse; SubroutineBase base; bool ret = phrase_parse<std::string::const_iterator> (str.begin(), str.end(), subroutine_parser<std::string::const_iterator>(), space, base); if (!ret) std::cout << "dadümm\n"; else { std::cout << "Name: " << base.name << "\n"; for (auto i = base.parameters.begin(); i != base.parameters.end(); ++i) { std::cout << "parm pair: " << i->first << "-" << i->second << "\n"; } } return 0; }
EDIT: Die Lösung macht micht schon deutlich glücklicher.
EDIT: boost::spirit::qi macht so viel fun, ich habe gleich mit den Schleifen weitergemacht