/* * CslParser.cpp * * Created on: 08.04.2013 * Author: Thomas Heinemann */ #include "src/parser/CslParser.h" #include "src/utility/OsDetection.h" #include "src/utility/constants.h" // The action class headers. #include "src/properties/actions/AbstractAction.h" #include "src/properties/actions/BoundAction.h" #include "src/properties/actions/InvertAction.h" #include "src/properties/actions/FormulaAction.h" #include "src/properties/actions/RangeAction.h" #include "src/properties/actions/SortAction.h" // If the parser fails due to ill-formed data, this exception is thrown. #include "src/exceptions/WrongFormatException.h" // Used for Boost spirit. #include #include #include // Include headers for spirit iterators. Needed for diagnostics and input stream iteration. #include #include // Needed for file IO. #include #include #include // Some typedefs and namespace definitions to reduce code size. #define MAKE(Type, ...) phoenix::construct>(phoenix::new_(__VA_ARGS__)) typedef std::string::const_iterator BaseIteratorType; typedef boost::spirit::classic::position_iterator2 PositionIteratorType; namespace qi = boost::spirit::qi; namespace phoenix = boost::phoenix; namespace csl = storm::properties::csl; namespace storm { namespace parser { template struct CslParser::CslGrammar : qi::grammar>(), Skipper > { CslGrammar() : CslGrammar::base_type(start) { //This block contains helper rules that may be used several times freeIdentifierName = qi::lexeme[qi::alpha >> *(qi::alnum | qi::char_('_'))]; comparisonType = ( (qi::lit(">="))[qi::_val = storm::properties::GREATER_EQUAL] | (qi::lit(">"))[qi::_val = storm::properties::GREATER] | (qi::lit("<="))[qi::_val = storm::properties::LESS_EQUAL] | (qi::lit("<"))[qi::_val = storm::properties::LESS]); sortingCategory = ( (qi::lit("index"))[qi::_val = storm::properties::action::SortAction::INDEX] | (qi::lit("value"))[qi::_val = storm::properties::action::SortAction::VALUE] ); //Comment: Empty line or line starting with "//" comment = (qi::lit("//") >> *(qi::char_))[qi::_val = nullptr]; //This block defines rules for parsing state formulas stateFormula %= orFormula; stateFormula.name("state formula"); orFormula = andFormula[qi::_val = qi::_1] > *(qi::lit("|") > andFormula)[qi::_val = MAKE(csl::Or, qi::_val, qi::_1)]; orFormula.name("or formula"); andFormula = notFormula[qi::_val = qi::_1] > *(qi::lit("&") > notFormula)[qi::_val = MAKE(csl::And, qi::_val, qi::_1)]; andFormula.name("and formula"); notFormula = atomicStateFormula[qi::_val = qi::_1] | (qi::lit("!") > atomicStateFormula)[qi::_val = MAKE(csl::Not, qi::_1)]; notFormula.name("not formula"); //This block defines rules for "atomic" state formulas //(Propositions, probabilistic/reward formulas, and state formulas in brackets) atomicStateFormula %= probabilisticBoundOperator | steadyStateBoundOperator | atomicProposition | (qi::lit("(") >> stateFormula >> qi::lit(")")) | (qi::lit("[") >> stateFormula >> qi::lit("]")); atomicStateFormula.name("atomic state formula"); atomicProposition = (freeIdentifierName)[qi::_val = MAKE(csl::Ap, qi::_1)] | (qi::lit("\"") > (freeIdentifierName)[qi::_val = MAKE(csl::Ap, qi::_1)] > qi::lit("\"")); atomicProposition.name("atomic proposition"); probabilisticBoundOperator = ( (qi::lit("P") >> comparisonType > qi::double_ > pathFormula )[qi::_val = MAKE(csl::ProbabilisticBoundOperator , qi::_1, qi::_2, qi::_3)] ); probabilisticBoundOperator.name("probabilistic bound operator"); steadyStateBoundOperator = ( (qi::lit("S") >> comparisonType > qi::double_ > stateFormula )[qi::_val = MAKE(csl::SteadyStateBoundOperator , qi::_1, qi::_2, qi::_3)] ); steadyStateBoundOperator.name("steady state bound operator"); //This block defines rules for parsing probabilistic path formulas pathFormula = (timeBoundedEventually | eventually | globally | next | timeBoundedUntil | until | (qi::lit("(") >> pathFormula >> qi::lit(")")) | (qi::lit("[") >> pathFormula >> qi::lit("]"))); pathFormula.name("path formula"); timeBoundedEventually = ( ((qi::lit("F") >> qi::lit("[") >> qi::double_ >> qi::lit(",")) > qi::double_ > qi::lit("]") > stateFormula)[qi::_val = MAKE(csl::TimeBoundedEventually, qi::_1, qi::_2, qi::_3)] | ((qi::lit("F") >> (qi::lit("<=") | qi::lit("<"))) > qi::double_ > stateFormula)[qi::_val = MAKE(csl::TimeBoundedEventually, 0, qi::_1, qi::_2)] | ((qi::lit("F") >> (qi::lit(">=") | qi::lit(">"))) > qi::double_ > stateFormula)[qi::_val = MAKE(csl::TimeBoundedEventually, qi::_1, std::numeric_limits::infinity(), qi::_2)] ); timeBoundedEventually.name("time bounded eventually"); eventually = (qi::lit("F") > stateFormula)[qi::_val = MAKE(csl::Eventually , qi::_1)]; eventually.name("eventually"); next = (qi::lit("X") > stateFormula)[qi::_val = MAKE(csl::Next , qi::_1)]; next.name("next"); globally = (qi::lit("G") > stateFormula)[qi::_val = MAKE(csl::Globally , qi::_1)]; globally.name("globally"); timeBoundedUntil = ( (stateFormula[qi::_a = qi::_1] >> qi::lit("U") >> qi::lit("[") >> qi::double_ >> qi::lit(",") >> qi::double_ >> qi::lit("]") >> stateFormula) [qi::_val = MAKE(csl::TimeBoundedUntil, qi::_2, qi::_3, qi::_a, qi::_4)] | (stateFormula[qi::_a = qi::_1] >> qi::lit("U") >> (qi::lit("<=") | qi::lit("<")) > qi::double_ > stateFormula) [qi::_val = MAKE(csl::TimeBoundedUntil, 0, qi::_2, qi::_a, qi::_3)] | (stateFormula[qi::_a = qi::_1] >> qi::lit("U") >> (qi::lit(">=") | qi::lit(">")) > qi::double_ > stateFormula) [qi::_val = MAKE(csl::TimeBoundedUntil, qi::_2, std::numeric_limits::infinity(), qi::_a, qi::_3)] ); timeBoundedUntil.name("time bounded until"); until = (stateFormula[qi::_a = qi::_1] >> qi::lit("U") > stateFormula)[qi::_val = MAKE(csl::Until, qi::_a, qi::_2)]; until.name("until formula"); formula = (pathFormula | stateFormula); formula.name("CSL formula"); //This block defines rules for parsing formulas with noBoundOperators noBoundOperator = (probabilisticNoBoundOperator | steadyStateNoBoundOperator); noBoundOperator.name("no bound operator"); probabilisticNoBoundOperator = ((qi::lit("P") >> qi::lit("min") >> qi::lit("=")) > qi::lit("?") >> pathFormula)[qi::_val = MAKE(csl::CslFilter, qi::_1, storm::properties::MINIMIZE)] | ((qi::lit("P") >> qi::lit("max") >> qi::lit("=")) > qi::lit("?") >> pathFormula)[qi::_val = MAKE(csl::CslFilter, qi::_1, storm::properties::MAXIMIZE)] | ((qi::lit("P") >> qi::lit("=")) > qi::lit("?") >> pathFormula)[qi::_val = MAKE(csl::CslFilter, qi::_1)]; probabilisticNoBoundOperator.name("probabilistic no bound operator"); steadyStateNoBoundOperator = ((qi::lit("S") >> qi::lit("=")) > qi::lit("?") >> stateFormula )[qi::_val = MAKE(csl::CslFilter, qi::_1, storm::properties::UNDEFINED, true)]; steadyStateNoBoundOperator.name("steady state no bound operator"); // This block defines rules for parsing filter actions. boundAction = (qi::lit("bound") > qi::lit("(") >> comparisonType >> qi::lit(",") >> qi::double_ >> qi::lit(")"))[qi::_val = MAKE(storm::properties::action::BoundAction ,qi::_1, qi::_2)]; boundAction.name("bound action"); invertAction = qi::lit("invert")[qi::_val = MAKE(storm::properties::action::InvertAction, )]; invertAction.name("invert action"); formulaAction = (qi::lit("formula") > qi::lit("(") >> stateFormula >> qi::lit(")"))[qi::_val = MAKE(storm::properties::action::FormulaAction, qi::_1)]; formulaAction.name("formula action"); rangeAction = ( ((qi::lit("range") >> qi::lit("(") >> qi::uint_ >> qi::lit(",")) > qi::uint_ >> qi::lit(")"))[qi::_val = MAKE(storm::properties::action::RangeAction, qi::_1, qi::_2)] | (qi::lit("range") >> qi::lit("(") >> qi::uint_ >> qi::lit(")"))[qi::_val = MAKE(storm::properties::action::RangeAction, qi::_1, qi::_1 + 1)] ); rangeAction.name("range action"); sortAction = ( (qi::lit("sort") >> qi::lit("(") >> sortingCategory >> qi::lit(")"))[qi::_val = MAKE(storm::properties::action::SortAction, qi::_1)] | (qi::lit("sort") >> qi::lit("(") >> sortingCategory >> qi::lit(", ") >> (qi::lit("ascending") | qi::lit("asc")) > qi::lit(")"))[qi::_val = MAKE(storm::properties::action::SortAction, qi::_1, true)] | (qi::lit("sort") >> qi::lit("(") >> sortingCategory >> qi::lit(", ") >> (qi::lit("descending") | qi::lit("desc")) > qi::lit(")"))[qi::_val = MAKE(storm::properties::action::SortAction, qi::_1, false)] ); sortAction.name("sort action"); abstractAction = (qi::lit(";") | qi::eps) >> (boundAction | invertAction | formulaAction | rangeAction | sortAction) >> (qi::lit(";") | qi::eps); abstractAction.name("filter action"); filter = (qi::lit("filter") >> qi::lit("[") >> +abstractAction >> qi::lit("]") >> qi::lit("(") >> formula >> qi::lit(")"))[qi::_val = MAKE(csl::CslFilter, qi::_2, qi::_1)] | ((qi::lit("filter") >> qi::lit("[") >> qi::lit("max")) > +abstractAction >> qi::lit("]") >> qi::lit("(") >> formula >> qi::lit(")"))[qi::_val = MAKE(csl::CslFilter, qi::_2, qi::_1, storm::properties::MAXIMIZE)] | ((qi::lit("filter") >> qi::lit("[") >> qi::lit("min")) > +abstractAction >> qi::lit("]") >> qi::lit("(") >> formula >> qi::lit(")"))[qi::_val = MAKE(csl::CslFilter, qi::_2, qi::_1, storm::properties::MINIMIZE)] | (noBoundOperator)[qi::_val = qi::_1] | (formula)[qi::_val = MAKE(csl::CslFilter, qi::_1)]; filter.name("CSL formula filter"); start = (((filter) > (comment | qi::eps))[qi::_val = qi::_1] | comment[qi::_val = MAKE(csl::CslFilter, nullptr)] ) > qi::eoi; start.name("CSL formula filter start"); } qi::rule>(), Skipper> start; qi::rule>(), Skipper> filter; qi::rule>(), Skipper> noBoundOperator; qi::rule>(), Skipper> probabilisticNoBoundOperator; qi::rule>(), Skipper> steadyStateNoBoundOperator; qi::rule>(), Skipper> abstractAction; qi::rule>(), Skipper> boundAction; qi::rule>(), Skipper> invertAction; qi::rule>(), Skipper> formulaAction; qi::rule>(), Skipper> rangeAction; qi::rule>(), Skipper> sortAction; qi::rule>(), Skipper> formula; qi::rule>(), Skipper> comment; qi::rule>(), Skipper> stateFormula; qi::rule>(), Skipper> atomicStateFormula; qi::rule>(), Skipper> andFormula; qi::rule>(), Skipper> atomicProposition; qi::rule>(), Skipper> orFormula; qi::rule>(), Skipper> notFormula; qi::rule>(), Skipper> probabilisticBoundOperator; qi::rule>(), Skipper> steadyStateBoundOperator; qi::rule>(), Skipper> pathFormula; qi::rule>(), Skipper> timeBoundedEventually; qi::rule>(), Skipper> eventually; qi::rule>(), Skipper> next; qi::rule>(), Skipper> globally; qi::rule>(), qi::locals< std::shared_ptr>>, Skipper> timeBoundedUntil; qi::rule>(), qi::locals< std::shared_ptr>>, Skipper> until; qi::rule freeIdentifierName; qi::rule comparisonType; qi::rule::SortingCategory(), Skipper> sortingCategory; }; std::shared_ptr> CslParser::parseCslFormula(std::string formulaString) { // Prepare iterators to input. BaseIteratorType stringIteratorBegin = formulaString.begin(); BaseIteratorType stringIteratorEnd = formulaString.end(); PositionIteratorType positionIteratorBegin(stringIteratorBegin, stringIteratorEnd, formulaString); PositionIteratorType positionIteratorEnd; // Prepare resulting intermediate representation of input. std::shared_ptr> result_pointer(nullptr); CslGrammar grammar; // Now, parse the formula from the given string try { qi::phrase_parse(positionIteratorBegin, positionIteratorEnd, grammar, boost::spirit::ascii::space, result_pointer); } catch(const qi::expectation_failure& e) { // If the parser expected content different than the one provided, display information // about the location of the error. const boost::spirit::classic::file_position_base& pos = e.first.get_position(); // Construct the error message including a caret display of the position in the // erroneous line. std::stringstream msg; msg << pos.file << ", line " << pos.line << ", column " << pos.column << ": parse error: expected " << e.what_ << std::endl << "\t" << e.first.get_currentline() << std::endl << "\t"; int i = 0; for (i = 0; i < pos.column; ++i) { msg << "-"; } msg << "^"; for (; i < 80; ++i) { msg << "-"; } msg << std::endl; std::cerr << msg.str(); // Now propagate exception. throw storm::exceptions::WrongFormatException() << msg.str(); } // The syntax can be so wrong that no rule can be matched at all // In that case, no expectation failure is thrown, but the parser just returns nullptr // Then, of course the result is not usable, hence we throw a WrongFormatException, too. if (!result_pointer) { throw storm::exceptions::WrongFormatException() << "Syntax error in formula"; } return result_pointer; } } /* namespace parser */ } /* namespace storm */