@@ -597,6 +597,186 @@ void github_pr_297()
597597 }
598598}
599599
600+ namespace github_issue_312_ {
601+ /*
602+ * Recursive descent parser for expressions.
603+ * Supports addition (+), multiplication (*) and
604+ * parethesized expressions, nothing else.
605+ *
606+ * Creates a tree of "evaluatable" objects which
607+ * own their downstream objects in a unique_ptr
608+ */
609+
610+ // base class for all tree nodes
611+ struct evaluatable
612+ {
613+ virtual double evaluate () = 0;
614+ virtual ~evaluatable () = default ;
615+ };
616+
617+ namespace bp = boost::parser;
618+
619+ // top level parser
620+ constexpr bp::rule<struct expression_tag , std::unique_ptr<evaluatable>> expression_parser = " expression_parser" ;
621+
622+ /*
623+ * LITERAL EXPRESSION
624+ */
625+ struct literal_evaluatable : evaluatable
626+ {
627+ explicit literal_evaluatable (double v) : value_(v) {}
628+ double evaluate () override
629+ {
630+ return value_;
631+ }
632+ double value_;
633+ };
634+ constexpr bp::rule<struct literal_tag , std::unique_ptr<evaluatable>> literal_parser = " literal_parser" ;
635+ constexpr auto literal_parser_action = [](auto & ctx) {
636+ std::unique_ptr<evaluatable>& val = _val (ctx);
637+ double & parsed_value = _attr (ctx);
638+ val = std::make_unique<literal_evaluatable>(parsed_value);
639+ };
640+ constexpr auto literal_parser_def =
641+ bp::double_[literal_parser_action];
642+
643+ /*
644+ * PARENTHESIZED EXPRESSION
645+ */
646+ struct parenthesized_evaluatable : evaluatable
647+ {
648+ explicit parenthesized_evaluatable (std::unique_ptr<evaluatable>&& e) : evaluatable_(std::move(e)) {}
649+ double evaluate () override
650+ {
651+ return evaluatable_->evaluate ();
652+ }
653+ std::unique_ptr<evaluatable> evaluatable_;
654+ };
655+ constexpr bp::rule<struct parenthesized_tag , std::unique_ptr<evaluatable>> parenthesized_parser = " parenthesized_parser" ;
656+ constexpr auto parenthesized_action = [](auto & ctx) {
657+ std::unique_ptr<evaluatable>& val = _val (ctx);
658+ std::unique_ptr<evaluatable>& attr = _attr (ctx);
659+ val = std::make_unique<parenthesized_evaluatable>(std::move (attr));
660+ };
661+ constexpr auto parenthesized_parser_def =
662+ (
663+ bp::lit (' (' ) > expression_parser > bp::lit (' )' )
664+ )[parenthesized_action];
665+
666+ /*
667+ * ATOM EXPRESSION
668+ */
669+ struct atom_evaluatable : evaluatable
670+ {
671+ explicit atom_evaluatable (std::unique_ptr<evaluatable>&& e) : evaluatable_(std::move(e)) {}
672+ double evaluate () override
673+ {
674+ return evaluatable_->evaluate ();
675+ }
676+ std::unique_ptr<evaluatable> evaluatable_;
677+ };
678+ constexpr bp::rule<struct atom_tag , std::unique_ptr<evaluatable>> atom_parser = " atom_parser" ;
679+ constexpr auto atom_action = [](auto & ctx) {
680+ std::unique_ptr<evaluatable>& val = _val (ctx);
681+ std::unique_ptr<evaluatable>& attr = _attr (ctx);
682+ val = std::make_unique<atom_evaluatable>(std::move (attr));
683+ };
684+ constexpr auto atom_parser_def =
685+ (
686+ parenthesized_parser
687+ |
688+ literal_parser
689+ )[atom_action];
690+
691+ /*
692+ * MULTIPLICATION EXPRESSION
693+ */
694+ struct multiplication_evaluatable : evaluatable
695+ {
696+ multiplication_evaluatable (std::vector<std::unique_ptr<evaluatable>>&& e)
697+ : evaluatables_(std::move(e))
698+ {}
699+ double evaluate () override
700+ {
701+ double result = 1 ;
702+ for (const auto & e : evaluatables_) {
703+ result *= e->evaluate ();
704+ }
705+ return result;
706+ }
707+ std::vector<std::unique_ptr<evaluatable>> evaluatables_;
708+ };
709+ constexpr bp::rule<struct mult_tag , std::unique_ptr<evaluatable>> mult_parser = " mult_parser" ;
710+ constexpr auto mult_parser_action = [](auto & ctx) {
711+ std::unique_ptr<evaluatable>& val = _val (ctx);
712+ std::vector<std::unique_ptr<evaluatable>>& operands = _attr (ctx);
713+ val = std::make_unique<multiplication_evaluatable>(std::move (operands));
714+ };
715+ constexpr auto mult_parser_def =
716+ (atom_parser % bp::lit(' *' ))[mult_parser_action];
717+
718+ /*
719+ * ADDITION EXPRESSION
720+ */
721+ struct addition_evaluatable : evaluatable
722+ {
723+ addition_evaluatable (std::vector<std::unique_ptr<evaluatable>>&& e)
724+ : evaluatables_(std::move(e))
725+ {}
726+ double evaluate () override
727+ {
728+ double result = 0 ;
729+ for (const auto & e : evaluatables_) {
730+ result += e->evaluate ();
731+ }
732+ return result;
733+ }
734+ std::vector<std::unique_ptr<evaluatable>> evaluatables_;
735+ };
736+ constexpr bp::rule<struct add_tag , std::unique_ptr<evaluatable>> add_parser = " add_parser" ;
737+ constexpr auto add_parser_action = [](auto & ctx) {
738+ std::unique_ptr<evaluatable>& val = _val (ctx);
739+ std::vector<std::unique_ptr<evaluatable>>& operands = _attr (ctx);
740+ val = std::make_unique<addition_evaluatable>(std::move (operands));
741+ };
742+ constexpr auto add_parser_def =
743+ (mult_parser % bp::lit(' +' ))[add_parser_action];
744+
745+ constexpr auto expression_parser_action = [](auto & ctx) {
746+ std::unique_ptr<evaluatable>& val = _val (ctx);
747+ std::unique_ptr<evaluatable>& attr = _attr (ctx);
748+ val = std::move (attr);
749+ };
750+
751+ /*
752+ * EXPRESSION
753+ */
754+ constexpr auto expression_parser_def =
755+ add_parser[expression_parser_action];
756+
757+ BOOST_PARSER_DEFINE_RULES (
758+ literal_parser,
759+ mult_parser,
760+ add_parser,
761+ expression_parser,
762+ parenthesized_parser,
763+ atom_parser);
764+ }
765+
766+ void github_issue_312 ()
767+ {
768+ namespace bp = boost::parser;
769+ using namespace github_issue_312_ ;
770+
771+ auto result = bp::parse (" (2 + 3) + 3.1415 * 2" , expression_parser, bp::blank);
772+ BOOST_TEST (result);
773+ BOOST_TEST (result.value ()->evaluate () == (2 + 3 ) + 3.1415 * 2 );
774+
775+ result = bp::parse (" ((2*0.1) + 33.) * (2 + 3) + 3.1415 * 2" , expression_parser, bp::blank);
776+ BOOST_TEST (result);
777+ BOOST_TEST (result.value ()->evaluate () == ((2 *0.1 ) + 33 .) * (2 + 3 ) + 3.1415 * 2 );
778+ }
779+
600780
601781int main ()
602782{
@@ -615,5 +795,6 @@ int main()
615795 github_pr_290 ();
616796 github_issue_294 ();
617797 github_pr_297 ();
798+ github_issue_312 ();
618799 return boost::report_errors ();
619800}
0 commit comments