#include "PgclParser.h" // If the parser fails due to ill-formed data, this exception is thrown. #include "storm/exceptions/WrongFormatException.h" namespace storm { namespace parser { storm::pgcl::PgclProgram PgclParser::parse(std::string const& filename) { // Create empty program. storm::pgcl::PgclProgram result; // Open file and initialize result. std::ifstream inputFileStream(filename, std::ios::in); STORM_LOG_THROW(inputFileStream.good(), storm::exceptions::WrongFormatException, "Unable to read from file '" << filename << "'."); // Now try to parse the contents of the file. try { std::string fileContent((std::istreambuf_iterator(inputFileStream)), (std::istreambuf_iterator())); result = parseFromString(fileContent, filename); } catch(std::exception& e) { // In case of an exception properly close the file before passing exception. inputFileStream.close(); throw e; } // Close the stream in case everything went smoothly and return result. inputFileStream.close(); return result; } storm::pgcl::PgclProgram PgclParser::parseFromString(std::string const& input, std::string const& filename) { PositionIteratorType first(input.begin()); PositionIteratorType iter = first; PositionIteratorType last(input.end()); // Create empty program. storm::pgcl::PgclProgram result; // Create grammar. storm::parser::PgclParser grammar(filename, first); try { // Start the parsing run. bool succeeded = qi::phrase_parse(iter, last, grammar, boost::spirit::ascii::space | qi::lit("//") >> *(qi::char_ - (qi::eol | qi::eoi)) >> (qi::eol | qi::eoi), result); STORM_LOG_THROW(succeeded, storm::exceptions::WrongFormatException, "Parsing of PGCL program failed."); STORM_LOG_DEBUG("Parse of PGCL program finished."); } catch(qi::expectation_failure const& e) { // If the parser expected content different than the one provided, display information about the location of the error. std::size_t lineNumber = boost::spirit::get_line(e.first); // Now propagate exception. STORM_LOG_THROW(false, storm::exceptions::WrongFormatException, "Parsing error in line " << lineNumber << " of file " << filename << "."); } return result; } PgclParser::PgclParser(std::string const& filename, Iterator first) : PgclParser::base_type(program, "PGCL grammar"), annotate(first), expressionManager(std::shared_ptr(new storm::expressions::ExpressionManager())), expressionParser(*expressionManager, invalidIdentifiers) { this->enableExpressions(); /* * PGCL grammar is defined here */ // Rough program structure program = (qi::lit("function ") > programName > qi::lit("(") > -(doubleDeclaration % qi::lit(",")) > qi::lit(")") > qi::lit("{") > variableDeclarations > sequenceOfStatements > qi::lit("}"))[qi::_val = phoenix::bind(&PgclParser::createProgram, phoenix::ref(*this), qi::_1, qi::_2, qi::_3, qi::_4)]; sequenceOfStatements %= +(statement); sequenceOfStatements.name("sequence of statements"); variableDeclarations %= qi::lit("var") > qi::lit("{") > +(integerDeclaration | booleanDeclaration) > qi::lit("}"); variableDeclarations.name("variable declarations"); // Statements statement %= simpleStatement | compoundStatement; simpleStatement %= assignmentStatement | observeStatement; compoundStatement %= ifElseStatement | loopStatement | branchStatement; // Simple statements doubleDeclaration = (qi::lit("double ") >> variableName)[qi::_val = phoenix::bind(&PgclParser::declareDoubleVariable, phoenix::ref(*this), qi::_1)]; integerDeclaration = (qi::lit("int ") > variableName > qi::lit(":=") > expression > qi::lit(";"))[qi::_val = phoenix::bind(&PgclParser::createIntegerDeclarationStatement, phoenix::ref(*this), qi::_1, qi::_2)]; integerDeclaration.name("integer declaration"); booleanDeclaration = (qi::lit("bool ") > variableName > qi::lit(":=") > expression > qi::lit(";"))[qi::_val = phoenix::bind(&PgclParser::createBooleanDeclarationStatement, phoenix::ref(*this), qi::_1, qi::_2)]; booleanDeclaration.name("boolean declaration"); assignmentStatement = (variableName > qi::lit(":=") > (expression | uniformExpression) > qi::lit(";"))[qi::_val = phoenix::bind(&PgclParser::createAssignmentStatement, phoenix::ref(*this), qi::_1, qi::_2)]; observeStatement = (qi::lit("observe") > qi::lit("(") >> booleanCondition >> qi::lit(")") > qi::lit(";"))[qi::_val = phoenix::bind(&PgclParser::createObserveStatement, phoenix::ref(*this), qi::_1)]; // Compound statements ifElseStatement = (qi::lit("if") > qi::lit("(") >> booleanCondition >> qi::lit(")") >> qi::lit("{") >> sequenceOfStatements >> qi::lit("}") >> -(qi::lit("else") >> qi::lit("{") >> sequenceOfStatements >> qi::lit("}"))) [qi::_val = phoenix::bind(&PgclParser::createIfElseStatement, phoenix::ref(*this), qi::_1, qi::_2, qi::_3)]; ifElseStatement.name("if/else statement"); branchStatement = nondeterministicBranch | probabilisticBranch; loopStatement = (qi::lit("while") > qi::lit("(") > booleanCondition > qi::lit(")") > qi::lit("{") >> sequenceOfStatements >> qi::lit("}")) [qi::_val = phoenix::bind(&PgclParser::createLoopStatement, phoenix::ref(*this), qi::_1, qi::_2)]; loopStatement.name("loop statement"); nondeterministicBranch = (qi::lit("{") >> sequenceOfStatements >> qi::lit("}") >> qi::lit("[]") >> qi::lit("{") >> sequenceOfStatements>> qi::lit("}"))[qi::_val = phoenix::bind(&PgclParser::createNondeterministicBranch, phoenix::ref(*this), qi::_1, qi::_2)]; probabilisticBranch = (qi::lit("{") >> sequenceOfStatements >> qi::lit("}") >> qi::lit("[") >> expression >> qi::lit("]") >> qi::lit("{") >> sequenceOfStatements >> qi::lit("}"))[qi::_val = phoenix::bind(&PgclParser::createProbabilisticBranch, phoenix::ref(*this), qi::_2, qi::_1, qi::_3)]; // Expression and condition building, and basic identifiers expression %= expressionParser; expression.name("expression"); booleanCondition = expressionParser[qi::_val = phoenix::bind(&PgclParser::createBooleanExpression, phoenix::ref(*this), qi::_1)]; uniformExpression = (qi::lit("unif") >> qi::lit("(") >> qi::int_ >> qi::lit(",") >> qi::int_ >> qi::lit(")"))[qi::_val = phoenix::bind(&PgclParser::createUniformExpression, phoenix::ref(*this), qi::_1, qi::_2)]; variableName %= (+(qi::alnum | qi::lit("_"))) - invalidIdentifiers; variableName.name("variable name"); programName %= +(qi::alnum | qi::lit("_")); programName.name("program name"); // Enables location tracking for important entities auto setLocationInfoFunction = this->annotate(*qi::_val, qi::_1, qi::_3); qi::on_success(assignmentStatement, setLocationInfoFunction); qi::on_success(observeStatement, setLocationInfoFunction); qi::on_success(nondeterministicBranch, setLocationInfoFunction); qi::on_success(probabilisticBranch, setLocationInfoFunction); qi::on_success(loopStatement, setLocationInfoFunction); qi::on_success(ifElseStatement, setLocationInfoFunction); // Enable error reporting. qi::on_error(program, handler(qi::_1, qi::_2, qi::_3, qi::_4)); qi::on_error(variableDeclarations, handler(qi::_1, qi::_2, qi::_3, qi::_4)); qi::on_error(integerDeclaration, handler(qi::_1, qi::_2, qi::_3, qi::_4)); qi::on_error(booleanDeclaration, handler(qi::_1, qi::_2, qi::_3, qi::_4)); qi::on_error(assignmentStatement, handler(qi::_1, qi::_2, qi::_3, qi::_4)); qi::on_error(observeStatement, handler(qi::_1, qi::_2, qi::_3, qi::_4)); qi::on_error(ifElseStatement, handler(qi::_1, qi::_2, qi::_3, qi::_4)); qi::on_error(loopStatement, handler(qi::_1, qi::_2, qi::_3, qi::_4)); qi::on_error(branchStatement, handler(qi::_1, qi::_2, qi::_3, qi::_4)); qi::on_error(expression, handler(qi::_1, qi::_2, qi::_3, qi::_4)); qi::on_error(booleanCondition, handler(qi::_1, qi::_2, qi::_3, qi::_4)); qi::on_error(uniformExpression, handler(qi::_1, qi::_2, qi::_3, qi::_4)); // Adds dummy to the 0-th location. std::shared_ptr dummy(new storm::pgcl::AssignmentStatement()); this->locationToStatement.insert(this->locationToStatement.begin(), dummy); } // PgclParser() void PgclParser::enableExpressions() { (this->expressionParser).setIdentifierMapping(&this->identifiers_); } /* * Creators for various program parts. They all follow the basic scheme * to retrieve the subparts of the program part and mold them together * into one new statement. They wrap the statement constructors and * throw excpetions in case something unexpected was parsed, e.g. * (x+5) as a boolean expression, or types of assignments don't match. */ storm::pgcl::PgclProgram PgclParser::createProgram(std::string const& programName, boost::optional> parameters, std::vector> const& variableDeclarations, std::vector> const& statements) { // Creates an empty paramter list in case no parameters were given. std::vector params; if(parameters != boost::none) { params = *parameters; } std::vector declarations; for (auto const& decl : variableDeclarations) { declarations.push_back(*decl); } // Creates the actual program. std::shared_ptr result( new storm::pgcl::PgclProgram(declarations, statements, this->locationToStatement, params, this->expressionManager, this->loopCreated, this->nondetCreated, this->observeCreated) ); result->back()->setLast(true); // Sets the current program as a parent to all its direct children statements. for(storm::pgcl::iterator it = result->begin(); it != result->end(); ++it) { (*it)->setParentBlock(result.get()); } return *result; } storm::expressions::Variable PgclParser::declareDoubleVariable(std::string const& variableName) { storm::expressions::Variable variable = expressionManager->declareRationalVariable(variableName); this->identifiers_.add(variableName, variable.getExpression()); return variable; } std::shared_ptr PgclParser::createAssignmentStatement(std::string const& variableName, boost::variant const& assignedExpression) { storm::expressions::Variable variable; if(!(*expressionManager).hasVariable(variableName)) { variable = (*expressionManager).declareIntegerVariable(variableName); } else { variable = (*expressionManager).getVariable(variableName); // Checks if assignment types match. if(assignedExpression.which() == 0 && !(variable.getType() == boost::get(assignedExpression).getType())) { STORM_LOG_THROW(false, storm::exceptions::WrongFormatException, "Wrong type when assigning to " << variableName << "."); } } std::shared_ptr newAssignment(new storm::pgcl::AssignmentStatement(variable, assignedExpression)); newAssignment->setLocationNumber(this->currentLocationNumber); this->locationToStatement.insert(this->locationToStatement.begin() + this->currentLocationNumber, newAssignment); currentLocationNumber++; return newAssignment; } std::shared_ptr PgclParser::createIntegerDeclarationStatement(std::string const& variableName, storm::expressions::Expression const& assignedExpression) { storm::expressions::Variable variable; if(!(*expressionManager).hasVariable(variableName)) { variable = (*expressionManager).declareIntegerVariable(variableName); this->identifiers_.add(variableName, variable.getExpression()); } else { // In case that a declaration already happened. variable = (*expressionManager).getVariable(variableName); STORM_LOG_THROW(false, storm::exceptions::WrongFormatException, "Declaration of integer variable " << variableName << " which was already declared previously."); } // Todo add some checks return std::make_shared(variable, assignedExpression); } std::shared_ptr PgclParser::createBooleanDeclarationStatement(std::string const& variableName, storm::expressions::Expression const& assignedExpression) { storm::expressions::Variable variable; if(!(*expressionManager).hasVariable(variableName)) { variable = (*expressionManager).declareBooleanVariable(variableName); this->identifiers_.add(variableName, variable.getExpression()); } else { // In case that a declaration already happened. variable = (*expressionManager).getVariable(variableName); STORM_LOG_THROW(false, storm::exceptions::WrongFormatException, "Declaration of boolean variable " << variableName << " which was already declared previously."); } // TODO add some checks. return std::make_shared(variable, assignedExpression); } std::shared_ptr PgclParser::createObserveStatement(storm::pgcl::BooleanExpression const& condition) { std::shared_ptr observe(new storm::pgcl::ObserveStatement(condition)); this->observeCreated = true; observe->setLocationNumber(this->currentLocationNumber); this->locationToStatement.insert(this->locationToStatement.begin() + this->currentLocationNumber, observe); currentLocationNumber++; return observe; } std::shared_ptr PgclParser::createIfElseStatement(storm::pgcl::BooleanExpression const& condition, std::vector > const& ifBody, boost::optional > > const& elseBody) { std::shared_ptr ifBodyProgram(new storm::pgcl::PgclBlock(ifBody, this->expressionManager, this->loopCreated, this->nondetCreated, this->observeCreated)); std::shared_ptr ifElse; if(elseBody) { std::shared_ptr elseBodyProgram(new storm::pgcl::PgclBlock(*elseBody, this->expressionManager, this->loopCreated, this->nondetCreated, this->observeCreated)); ifElse = std::shared_ptr(new storm::pgcl::IfStatement(condition, ifBodyProgram, elseBodyProgram)); (*(ifElse->getIfBody()->back())).setLast(true); (*(ifElse->getElseBody()->back())).setLast(true); // Sets the current program as a parent to all its children statements. for(storm::pgcl::iterator it = ifElse->getElseBody()->begin(); it != ifElse->getElseBody()->end(); ++it) { (*it)->setParentBlock(ifElse->getElseBody().get()); } for(storm::pgcl::iterator it = ifElse->getIfBody()->begin(); it != ifElse->getIfBody()->end(); ++it) { (*it)->setParentBlock(ifElse->getIfBody().get()); } } else { ifElse = std::shared_ptr(new storm::pgcl::IfStatement(condition, ifBodyProgram)); (*(ifElse->getIfBody()->back())).setLast(true); // Sets the current program as a parent to all its children statements. for(storm::pgcl::iterator it = ifElse->getIfBody()->begin(); it != ifElse->getIfBody()->end(); ++it) { (*it)->setParentBlock(ifElse->getIfBody().get()); } } ifElse->setLocationNumber(this->currentLocationNumber); this->locationToStatement.insert(this->locationToStatement.begin() + this->currentLocationNumber, ifElse); currentLocationNumber++; return ifElse; } std::shared_ptr PgclParser::createLoopStatement(storm::pgcl::BooleanExpression const& condition, std::vector > const& body) { std::shared_ptr bodyProgram(new storm::pgcl::PgclBlock(body, this->expressionManager, this->loopCreated, this->nondetCreated, this->observeCreated)); std::shared_ptr loop(new storm::pgcl::LoopStatement(condition, bodyProgram)); this->loopCreated = true; // Sets the current program as a parent to all its children statements. for(storm::pgcl::iterator it = loop->getBody()->begin(); it != loop->getBody()->end(); ++it) { (*it)->setParentBlock(loop->getBody().get()); } (*(loop->getBody()->back())).setLast(true); loop->setLocationNumber(this->currentLocationNumber); this->locationToStatement.insert(this->locationToStatement.begin() + this->currentLocationNumber, loop); currentLocationNumber++; return loop; } std::shared_ptr PgclParser::createNondeterministicBranch(std::vector > const& left, std::vector > const& right) { std::shared_ptr leftProgram(new storm::pgcl::PgclBlock(left, this->expressionManager, this->loopCreated, this->nondetCreated, this->observeCreated)); std::shared_ptr rightProgram(new storm::pgcl::PgclBlock(right, this->expressionManager, this->loopCreated, this->nondetCreated, this->observeCreated)); std::shared_ptr branch(new storm::pgcl::NondeterministicBranch(leftProgram, rightProgram)); this->nondetCreated = true; // Sets the left program as a parent to all its children statements. for(storm::pgcl::iterator it = branch->getLeftBranch()->begin(); it != branch->getLeftBranch()->end(); ++it) { (*it)->setParentBlock(branch->getLeftBranch().get()); } // Sets the right program as a parent to all its children statements. for(storm::pgcl::iterator it = branch->getRightBranch()->begin(); it != branch->getRightBranch()->end(); ++it) { (*it)->setParentBlock(branch->getRightBranch().get()); } (*(branch->getLeftBranch()->back())).setLast(true); (*(branch->getRightBranch()->back())).setLast(true); branch->setLocationNumber(this->currentLocationNumber); this->locationToStatement.insert(this->locationToStatement.begin() + this->currentLocationNumber, branch); currentLocationNumber++; return branch; } std::shared_ptr PgclParser::createProbabilisticBranch(storm::expressions::Expression const& probability, std::vector > const& left, std::vector > const& right) { std::shared_ptr leftProgram(new storm::pgcl::PgclBlock(left, this->expressionManager, this->loopCreated, this->nondetCreated, this->observeCreated)); std::shared_ptr rightProgram(new storm::pgcl::PgclBlock(right, this->expressionManager, this->loopCreated, this->nondetCreated, this->observeCreated)); std::shared_ptr branch(new storm::pgcl::ProbabilisticBranch(probability, leftProgram, rightProgram)); // Sets the left program as a parent to all its children statements. for(storm::pgcl::iterator it = branch->getLeftBranch()->begin(); it != branch->getLeftBranch()->end(); ++it) { (*it)->setParentBlock(branch->getLeftBranch().get()); } // Sets the right program as a parent to all its children statements. for(storm::pgcl::iterator it = branch->getRightBranch()->begin(); it != branch->getRightBranch()->end(); ++it) { (*it)->setParentBlock(branch->getRightBranch().get()); } (*(branch->getLeftBranch()->back())).setLast(true); (*(branch->getRightBranch()->back())).setLast(true); branch->setLocationNumber(this->currentLocationNumber); this->locationToStatement.insert(this->locationToStatement.begin() + this->currentLocationNumber, branch); currentLocationNumber++; return branch; } storm::pgcl::BooleanExpression PgclParser::createBooleanExpression(storm::expressions::Expression const& expression) { if(expression.hasBooleanType()) { storm::pgcl::BooleanExpression booleanExpression = storm::pgcl::BooleanExpression(expression); return booleanExpression; } else { // In case that a non-boolean expression was used (e.g. (x+5)). STORM_LOG_THROW(false, storm::exceptions::WrongFormatException, "Non boolean expression was used in a condition."); } } storm::pgcl::UniformExpression PgclParser::createUniformExpression(int const& begin, int const& end) { return storm::pgcl::UniformExpression(begin, end); } } // namespace parser } // namespace storm