017
06.01.2007, 23:27 Uhr
0xdeadbeef
Gott (Operator)
|
Ich hab das zum Spaß mal mit boost.spirit implementiert: calculator.hh:
C++: |
#ifndef INCLUDED_CALCULATOR_HH #define INCLUDED_CALCULATOR_HH
#include <boost/spirit/tree/common.hpp>
#include <exception> #include <map> #include <ostream> #include <string>
class calculator { public: typedef char ast_data_t;
struct invalid_input : public std::exception { virtual char const *what() const throw() { return "invalid input data" ; } }; struct malformed : public invalid_input { virtual char const *what() const throw() { return "malformed or missing expression" ; } }; struct vars_missing : public invalid_input { virtual char const *what() const throw() { return "variables missing" ; } };
calculator(); calculator(std::string const &str) throw(malformed); calculator( char const *str) throw(malformed);
void parse(std::string const &str) throw(malformed); void parse( char const *str) throw(malformed);
inline bool full_match() const throw() { return tree_info_.full; }
bool operator()(std::map<char, bool> const &vars) const throw(vars_missing, malformed);
void to_xml(std::ostream &os, char const *str) const;
private: typedef boost::spirit::tree_parse_info<char const *, boost::spirit::node_val_data_factory<ast_data_t> > tree_type; typedef boost::spirit::tree_match <char const *, boost::spirit::node_val_data_factory<ast_data_t>, boost::spirit::nil_t>::const_tree_iterator tree_iterator;
void parse_aux(char const *begin, char const *end);
bool eval(tree_iterator const &i, std::map<char, bool> const &vars) const throw(vars_missing);
tree_type tree_info_; };
#endif
|
calculator.cc:
C++: |
#include "calculator.hh"
#include <boost/spirit/core.hpp> #include <boost/spirit/tree/ast.hpp> #include <boost/spirit/tree/parse_tree.hpp> #include <boost/spirit/tree/tree_to_xml.hpp>
#include <cstring> #include <exception>
namespace bs = boost::spirit;
namespace { class grammar : public bs::grammar<grammar> { public: typedef calculator::ast_data_t result_t; typedef bs::tree_node<bs::node_val_data<char const *, result_t> > node_t;
enum rule_id { false_id = 1, true_id, variable_id, term_id, conjunction_id, disjunction_id };
template<typename scanner_t> class definition { public: struct assign_char_to_node { void operator()(node_t &n, typename scanner_t::iterator_t const &first, typename scanner_t::iterator_t const &last) const { namespace bs = bs;
result_t r; bs::parse(first, last, bs::range_p('a', 'z')[bs::assign_a(r)]); n.value.value(r); } };
definition(grammar const &self) { namespace bs = bs;
false_rule = bs::str_p("#f") | '0'; true_rule = bs::str_p("#t") | '1'; variable = bs::access_node_d[bs::range_p('a', 'z')][assign_char_to_node_a];
term = false_rule | true_rule | variable | bs::inner_node_d['(' >> disjunction >> ')'] | (bs::root_node_d[bs::ch_p('!')] >> term);
conjunction = term >> *(bs::root_node_d[bs::ch_p('&')] >> term ); disjunction = conjunction >> *(bs::root_node_d[bs::ch_p('|')] >> conjunction); }
bs::rule<scanner_t, bs::parser_context<>, bs::parser_tag<disjunction_id> > const &start() const { return disjunction; }
private: bs::rule<scanner_t, bs::parser_context<>, bs::parser_tag< true_id> > true_rule; bs::rule<scanner_t, bs::parser_context<>, bs::parser_tag< false_id> > false_rule; bs::rule<scanner_t, bs::parser_context<>, bs::parser_tag< variable_id> > variable; bs::rule<scanner_t, bs::parser_context<>, bs::parser_tag< term_id> > term; bs::rule<scanner_t, bs::parser_context<>, bs::parser_tag<conjunction_id> > conjunction; bs::rule<scanner_t, bs::parser_context<>, bs::parser_tag<disjunction_id> > disjunction;
assign_char_to_node assign_char_to_node_a; };
private: }; }
calculator::calculator() { } calculator::calculator(std::string const &str) throw(malformed) { parse(str); } calculator::calculator( char const *str) throw(malformed) { parse(str); }
void calculator::parse(std::string const &str) throw(malformed) { parse_aux(str.c_str(), str.c_str() + str.size() ); } void calculator::parse( char const *str) throw(malformed) { parse_aux(str , str + std::strlen(str)); }
void calculator::parse_aux(char const *begin, char const *end) { grammar g; tree_info_ = bs::ast_parse<bs::node_val_data_factory<ast_data_t> >(begin, end, g, bs::space_p); }
bool calculator::operator()(std::map<char, bool> const &vars) const throw(vars_missing, malformed) { if(!full_match()) throw malformed();
return eval(tree_info_.trees.begin(), vars); }
bool calculator::eval(tree_iterator const &i, std::map<char, bool> const &vars) const throw(vars_missing) { switch(i->value.id().to_long()) { case grammar:: false_id: return false; case grammar:: true_id: return true; case grammar::variable_id: { std::map<char, bool>::const_iterator iter = vars.find(i->value.value()); if(iter == vars.end()) throw vars_missing(); return iter->second; } case grammar:: term_id: return !eval(i->children.begin(), vars); case grammar::conjunction_id: return eval(i->children.begin(), vars) && eval(i->children.begin() + 1, vars); case grammar::disjunction_id: return eval(i->children.begin(), vars) || eval(i->children.begin() + 1, vars); } // passiert nie throw std::bad_exception(); }
void calculator::to_xml(std::ostream &os, char const *str) const { std::map<bs::parser_id, std::string> rule_names;
rule_names[grammar:: false_id] = "false"; rule_names[grammar:: true_id] = "true"; rule_names[grammar:: variable_id] = "variable";
rule_names[grammar:: term_id] = "term";
rule_names[grammar::conjunction_id] = "conjunction"; rule_names[grammar::disjunction_id] = "disjunction";
bs::tree_to_xml(os, tree_info_.trees, str, rule_names); }
|
main.cc (zum Beispiel):
C++: |
#include "calculator.hh"
#include <iostream> #include <map> #include <string>
int main() { calculator calc; std::string s; std::map<char, bool> vars;
std::getline(std::cin, s);
vars['a'] = true; vars['b'] = false;
try { calc.parse(s);
calc.to_xml(std::cout, s.c_str()); std::cout << std::endl << calc(vars) << std::endl; } catch(calculator::invalid_input &e) { std::cerr << e.what() << std::endl; } }
|
Bearbeitung: |
Hmm. Die Formatierung ist da zwar etwas im Eimer, aber kopiers halt raus, dann sollte es einsichtiger sein.
Ich hab calculator.cc mal mit namespace bs = boost::spirit versehen, ums ein bisschen besser da reinzuquetschen.
|
-- Einfachheit ist Voraussetzung für Zuverlässigkeit. -- Edsger Wybe Dijkstra Dieser Post wurde am 06.01.2007 um 23:31 Uhr von 0xdeadbeef editiert. |