You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

345 lines
24 KiB

  1. #include "PgclParser.h"
  2. // If the parser fails due to ill-formed data, this exception is thrown.
  3. #include "storm/exceptions/WrongFormatException.h"
  4. namespace storm {
  5. namespace parser {
  6. storm::pgcl::PgclProgram PgclParser::parse(std::string const& filename) {
  7. // Create empty program.
  8. storm::pgcl::PgclProgram result;
  9. // Open file and initialize result.
  10. std::ifstream inputFileStream(filename, std::ios::in);
  11. STORM_LOG_THROW(inputFileStream.good(), storm::exceptions::WrongFormatException, "Unable to read from file '" << filename << "'.");
  12. // Now try to parse the contents of the file.
  13. try {
  14. std::string fileContent((std::istreambuf_iterator<char>(inputFileStream)), (std::istreambuf_iterator<char>()));
  15. result = parseFromString(fileContent, filename);
  16. } catch(std::exception& e) {
  17. // In case of an exception properly close the file before passing exception.
  18. inputFileStream.close();
  19. throw e;
  20. }
  21. // Close the stream in case everything went smoothly and return result.
  22. inputFileStream.close();
  23. return result;
  24. }
  25. storm::pgcl::PgclProgram PgclParser::parseFromString(std::string const& input, std::string const& filename) {
  26. PositionIteratorType first(input.begin());
  27. PositionIteratorType iter = first;
  28. PositionIteratorType last(input.end());
  29. // Create empty program.
  30. storm::pgcl::PgclProgram result;
  31. // Create grammar.
  32. storm::parser::PgclParser grammar(filename, first);
  33. try {
  34. // Start the parsing run.
  35. 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);
  36. STORM_LOG_THROW(succeeded, storm::exceptions::WrongFormatException, "Parsing of PGCL program failed.");
  37. STORM_LOG_DEBUG("Parse of PGCL program finished.");
  38. } catch(qi::expectation_failure<PositionIteratorType> const& e) {
  39. // If the parser expected content different than the one provided, display information about the location of the error.
  40. std::size_t lineNumber = boost::spirit::get_line(e.first);
  41. // Now propagate exception.
  42. STORM_LOG_THROW(false, storm::exceptions::WrongFormatException, "Parsing error in line " << lineNumber << " of file " << filename << ".");
  43. }
  44. return result;
  45. }
  46. PgclParser::PgclParser(std::string const& filename, Iterator first) :
  47. PgclParser::base_type(program, "PGCL grammar"),
  48. annotate(first),
  49. expressionManager(std::shared_ptr<storm::expressions::ExpressionManager>(new storm::expressions::ExpressionManager())),
  50. expressionParser(*expressionManager, invalidIdentifiers)
  51. {
  52. this->enableExpressions();
  53. /*
  54. * PGCL grammar is defined here
  55. */
  56. // Rough program structure
  57. program = (qi::lit("function ") > programName > qi::lit("(") > -(doubleDeclaration % qi::lit(",")) > qi::lit(")") > qi::lit("{") >
  58. variableDeclarations >
  59. sequenceOfStatements >
  60. qi::lit("}"))[qi::_val = phoenix::bind(&PgclParser::createProgram, phoenix::ref(*this), qi::_1, qi::_2, qi::_3, qi::_4)];
  61. sequenceOfStatements %= +(statement);
  62. sequenceOfStatements.name("sequence of statements");
  63. variableDeclarations %= qi::lit("var") > qi::lit("{") > +(integerDeclaration | booleanDeclaration) > qi::lit("}");
  64. variableDeclarations.name("variable declarations");
  65. // Statements
  66. statement %= simpleStatement | compoundStatement;
  67. simpleStatement %= assignmentStatement | observeStatement;
  68. compoundStatement %= ifElseStatement | loopStatement | branchStatement;
  69. // Simple statements
  70. doubleDeclaration = (qi::lit("double ") >> variableName)[qi::_val = phoenix::bind(&PgclParser::declareDoubleVariable, phoenix::ref(*this), qi::_1)];
  71. integerDeclaration = (qi::lit("int ") > variableName > qi::lit(":=") > expression > qi::lit(";"))[qi::_val = phoenix::bind(&PgclParser::createIntegerDeclarationStatement, phoenix::ref(*this), qi::_1, qi::_2)];
  72. integerDeclaration.name("integer declaration");
  73. booleanDeclaration = (qi::lit("bool ") > variableName > qi::lit(":=") > expression > qi::lit(";"))[qi::_val = phoenix::bind(&PgclParser::createBooleanDeclarationStatement, phoenix::ref(*this), qi::_1, qi::_2)];
  74. booleanDeclaration.name("boolean declaration");
  75. assignmentStatement = (variableName > qi::lit(":=") > (expression | uniformExpression) > qi::lit(";"))[qi::_val = phoenix::bind(&PgclParser::createAssignmentStatement, phoenix::ref(*this), qi::_1, qi::_2)];
  76. observeStatement = (qi::lit("observe") > qi::lit("(") >> booleanCondition >> qi::lit(")") > qi::lit(";"))[qi::_val = phoenix::bind(&PgclParser::createObserveStatement, phoenix::ref(*this), qi::_1)];
  77. // Compound statements
  78. ifElseStatement = (qi::lit("if") > qi::lit("(") >> booleanCondition >> qi::lit(")") >> qi::lit("{") >>
  79. sequenceOfStatements >>
  80. qi::lit("}") >> -(qi::lit("else") >> qi::lit("{") >>
  81. sequenceOfStatements >>
  82. qi::lit("}")))
  83. [qi::_val = phoenix::bind(&PgclParser::createIfElseStatement, phoenix::ref(*this), qi::_1, qi::_2, qi::_3)];
  84. ifElseStatement.name("if/else statement");
  85. branchStatement = nondeterministicBranch | probabilisticBranch;
  86. loopStatement = (qi::lit("while") > qi::lit("(") > booleanCondition > qi::lit(")") > qi::lit("{") >>
  87. sequenceOfStatements >>
  88. qi::lit("}"))
  89. [qi::_val = phoenix::bind(&PgclParser::createLoopStatement, phoenix::ref(*this), qi::_1, qi::_2)];
  90. loopStatement.name("loop statement");
  91. 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)];
  92. 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)];
  93. // Expression and condition building, and basic identifiers
  94. expression %= expressionParser;
  95. expression.name("expression");
  96. booleanCondition = expressionParser[qi::_val = phoenix::bind(&PgclParser::createBooleanExpression, phoenix::ref(*this), qi::_1)];
  97. 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)];
  98. variableName %= (+(qi::alnum | qi::lit("_"))) - invalidIdentifiers;
  99. variableName.name("variable name");
  100. programName %= +(qi::alnum | qi::lit("_"));
  101. programName.name("program name");
  102. // Enables location tracking for important entities
  103. auto setLocationInfoFunction = this->annotate(*qi::_val, qi::_1, qi::_3);
  104. qi::on_success(assignmentStatement, setLocationInfoFunction);
  105. qi::on_success(observeStatement, setLocationInfoFunction);
  106. qi::on_success(nondeterministicBranch, setLocationInfoFunction);
  107. qi::on_success(probabilisticBranch, setLocationInfoFunction);
  108. qi::on_success(loopStatement, setLocationInfoFunction);
  109. qi::on_success(ifElseStatement, setLocationInfoFunction);
  110. // Enable error reporting.
  111. qi::on_error<qi::fail>(program, handler(qi::_1, qi::_2, qi::_3, qi::_4));
  112. qi::on_error<qi::fail>(variableDeclarations, handler(qi::_1, qi::_2, qi::_3, qi::_4));
  113. qi::on_error<qi::fail>(integerDeclaration, handler(qi::_1, qi::_2, qi::_3, qi::_4));
  114. qi::on_error<qi::fail>(booleanDeclaration, handler(qi::_1, qi::_2, qi::_3, qi::_4));
  115. qi::on_error<qi::fail>(assignmentStatement, handler(qi::_1, qi::_2, qi::_3, qi::_4));
  116. qi::on_error<qi::fail>(observeStatement, handler(qi::_1, qi::_2, qi::_3, qi::_4));
  117. qi::on_error<qi::fail>(ifElseStatement, handler(qi::_1, qi::_2, qi::_3, qi::_4));
  118. qi::on_error<qi::fail>(loopStatement, handler(qi::_1, qi::_2, qi::_3, qi::_4));
  119. qi::on_error<qi::fail>(branchStatement, handler(qi::_1, qi::_2, qi::_3, qi::_4));
  120. qi::on_error<qi::fail>(expression, handler(qi::_1, qi::_2, qi::_3, qi::_4));
  121. qi::on_error<qi::fail>(booleanCondition, handler(qi::_1, qi::_2, qi::_3, qi::_4));
  122. qi::on_error<qi::fail>(uniformExpression, handler(qi::_1, qi::_2, qi::_3, qi::_4));
  123. // Adds dummy to the 0-th location.
  124. std::shared_ptr<storm::pgcl::AssignmentStatement> dummy(new storm::pgcl::AssignmentStatement());
  125. this->locationToStatement.insert(this->locationToStatement.begin(), dummy);
  126. } // PgclParser()
  127. void PgclParser::enableExpressions() {
  128. (this->expressionParser).setIdentifierMapping(&this->identifiers_);
  129. }
  130. /*
  131. * Creators for various program parts. They all follow the basic scheme
  132. * to retrieve the subparts of the program part and mold them together
  133. * into one new statement. They wrap the statement constructors and
  134. * throw excpetions in case something unexpected was parsed, e.g.
  135. * (x+5) as a boolean expression, or types of assignments don't match.
  136. */
  137. storm::pgcl::PgclProgram PgclParser::createProgram(std::string const& programName, boost::optional<std::vector<storm::expressions::Variable>> parameters, std::vector<std::shared_ptr<storm::pgcl::VariableDeclaration>> const& variableDeclarations, std::vector<std::shared_ptr<storm::pgcl::Statement>> const& statements) {
  138. // Creates an empty paramter list in case no parameters were given.
  139. std::vector<storm::expressions::Variable> params;
  140. if(parameters != boost::none) {
  141. params = *parameters;
  142. }
  143. std::vector<storm::pgcl::VariableDeclaration> declarations;
  144. for (auto const& decl : variableDeclarations) {
  145. declarations.push_back(*decl);
  146. }
  147. // Creates the actual program.
  148. std::shared_ptr<storm::pgcl::PgclProgram> result(
  149. new storm::pgcl::PgclProgram(declarations, statements, this->locationToStatement, params, this->expressionManager, this->loopCreated, this->nondetCreated, this->observeCreated)
  150. );
  151. result->back()->setLast(true);
  152. // Sets the current program as a parent to all its direct children statements.
  153. for(storm::pgcl::iterator it = result->begin(); it != result->end(); ++it) {
  154. (*it)->setParentBlock(result.get());
  155. }
  156. return *result;
  157. }
  158. storm::expressions::Variable PgclParser::declareDoubleVariable(std::string const& variableName) {
  159. storm::expressions::Variable variable = expressionManager->declareRationalVariable(variableName);
  160. this->identifiers_.add(variableName, variable.getExpression());
  161. return variable;
  162. }
  163. std::shared_ptr<storm::pgcl::AssignmentStatement> PgclParser::createAssignmentStatement(std::string const& variableName, boost::variant<storm::expressions::Expression, storm::pgcl::UniformExpression> const& assignedExpression) {
  164. storm::expressions::Variable variable;
  165. if(!(*expressionManager).hasVariable(variableName)) {
  166. variable = (*expressionManager).declareIntegerVariable(variableName);
  167. } else {
  168. variable = (*expressionManager).getVariable(variableName);
  169. // Checks if assignment types match.
  170. if(assignedExpression.which() == 0 && !(variable.getType() == boost::get<storm::expressions::Expression>(assignedExpression).getType())) {
  171. STORM_LOG_THROW(false, storm::exceptions::WrongFormatException, "Wrong type when assigning to " << variableName << ".");
  172. }
  173. }
  174. std::shared_ptr<storm::pgcl::AssignmentStatement> newAssignment(new storm::pgcl::AssignmentStatement(variable, assignedExpression));
  175. newAssignment->setLocationNumber(this->currentLocationNumber);
  176. this->locationToStatement.insert(this->locationToStatement.begin() + this->currentLocationNumber, newAssignment);
  177. currentLocationNumber++;
  178. return newAssignment;
  179. }
  180. std::shared_ptr<storm::pgcl::VariableDeclaration> PgclParser::createIntegerDeclarationStatement(std::string const& variableName, storm::expressions::Expression const& assignedExpression) {
  181. storm::expressions::Variable variable;
  182. if(!(*expressionManager).hasVariable(variableName)) {
  183. variable = (*expressionManager).declareIntegerVariable(variableName);
  184. this->identifiers_.add(variableName, variable.getExpression());
  185. } else {
  186. // In case that a declaration already happened.
  187. variable = (*expressionManager).getVariable(variableName);
  188. STORM_LOG_THROW(false, storm::exceptions::WrongFormatException, "Declaration of integer variable " << variableName << " which was already declared previously.");
  189. }
  190. // Todo add some checks
  191. return std::make_shared<storm::pgcl::VariableDeclaration>(variable, assignedExpression);
  192. }
  193. std::shared_ptr<storm::pgcl::VariableDeclaration> PgclParser::createBooleanDeclarationStatement(std::string const& variableName, storm::expressions::Expression const& assignedExpression) {
  194. storm::expressions::Variable variable;
  195. if(!(*expressionManager).hasVariable(variableName)) {
  196. variable = (*expressionManager).declareBooleanVariable(variableName);
  197. this->identifiers_.add(variableName, variable.getExpression());
  198. } else {
  199. // In case that a declaration already happened.
  200. variable = (*expressionManager).getVariable(variableName);
  201. STORM_LOG_THROW(false, storm::exceptions::WrongFormatException, "Declaration of boolean variable " << variableName << " which was already declared previously.");
  202. }
  203. // TODO add some checks.
  204. return std::make_shared<storm::pgcl::VariableDeclaration>(variable, assignedExpression);
  205. }
  206. std::shared_ptr<storm::pgcl::ObserveStatement> PgclParser::createObserveStatement(storm::pgcl::BooleanExpression const& condition) {
  207. std::shared_ptr<storm::pgcl::ObserveStatement> observe(new storm::pgcl::ObserveStatement(condition));
  208. this->observeCreated = true;
  209. observe->setLocationNumber(this->currentLocationNumber);
  210. this->locationToStatement.insert(this->locationToStatement.begin() + this->currentLocationNumber, observe);
  211. currentLocationNumber++;
  212. return observe;
  213. }
  214. std::shared_ptr<storm::pgcl::IfStatement> PgclParser::createIfElseStatement(storm::pgcl::BooleanExpression const& condition, std::vector<std::shared_ptr<storm::pgcl::Statement> > const& ifBody, boost::optional<std::vector<std::shared_ptr<storm::pgcl::Statement> > > const& elseBody) {
  215. std::shared_ptr<storm::pgcl::PgclBlock> ifBodyProgram(new storm::pgcl::PgclBlock(ifBody, this->expressionManager, this->loopCreated, this->nondetCreated, this->observeCreated));
  216. std::shared_ptr<storm::pgcl::IfStatement> ifElse;
  217. if(elseBody) {
  218. std::shared_ptr<storm::pgcl::PgclBlock> elseBodyProgram(new storm::pgcl::PgclBlock(*elseBody, this->expressionManager, this->loopCreated, this->nondetCreated, this->observeCreated));
  219. ifElse = std::shared_ptr<storm::pgcl::IfStatement>(new storm::pgcl::IfStatement(condition, ifBodyProgram, elseBodyProgram));
  220. (*(ifElse->getIfBody()->back())).setLast(true);
  221. (*(ifElse->getElseBody()->back())).setLast(true);
  222. // Sets the current program as a parent to all its children statements.
  223. for(storm::pgcl::iterator it = ifElse->getElseBody()->begin(); it != ifElse->getElseBody()->end(); ++it) {
  224. (*it)->setParentBlock(ifElse->getElseBody().get());
  225. }
  226. for(storm::pgcl::iterator it = ifElse->getIfBody()->begin(); it != ifElse->getIfBody()->end(); ++it) {
  227. (*it)->setParentBlock(ifElse->getIfBody().get());
  228. }
  229. } else {
  230. ifElse = std::shared_ptr<storm::pgcl::IfStatement>(new storm::pgcl::IfStatement(condition, ifBodyProgram));
  231. (*(ifElse->getIfBody()->back())).setLast(true);
  232. // Sets the current program as a parent to all its children statements.
  233. for(storm::pgcl::iterator it = ifElse->getIfBody()->begin(); it != ifElse->getIfBody()->end(); ++it) {
  234. (*it)->setParentBlock(ifElse->getIfBody().get());
  235. }
  236. }
  237. ifElse->setLocationNumber(this->currentLocationNumber);
  238. this->locationToStatement.insert(this->locationToStatement.begin() + this->currentLocationNumber, ifElse);
  239. currentLocationNumber++;
  240. return ifElse;
  241. }
  242. std::shared_ptr<storm::pgcl::LoopStatement> PgclParser::createLoopStatement(storm::pgcl::BooleanExpression const& condition, std::vector<std::shared_ptr<storm::pgcl::Statement> > const& body) {
  243. std::shared_ptr<storm::pgcl::PgclBlock> bodyProgram(new storm::pgcl::PgclBlock(body, this->expressionManager, this->loopCreated, this->nondetCreated, this->observeCreated));
  244. std::shared_ptr<storm::pgcl::LoopStatement> loop(new storm::pgcl::LoopStatement(condition, bodyProgram));
  245. this->loopCreated = true;
  246. // Sets the current program as a parent to all its children statements.
  247. for(storm::pgcl::iterator it = loop->getBody()->begin(); it != loop->getBody()->end(); ++it) {
  248. (*it)->setParentBlock(loop->getBody().get());
  249. }
  250. (*(loop->getBody()->back())).setLast(true);
  251. loop->setLocationNumber(this->currentLocationNumber);
  252. this->locationToStatement.insert(this->locationToStatement.begin() + this->currentLocationNumber, loop);
  253. currentLocationNumber++;
  254. return loop;
  255. }
  256. std::shared_ptr<storm::pgcl::NondeterministicBranch> PgclParser::createNondeterministicBranch(std::vector<std::shared_ptr<storm::pgcl::Statement> > const& left, std::vector<std::shared_ptr<storm::pgcl::Statement> > const& right) {
  257. std::shared_ptr<storm::pgcl::PgclBlock> leftProgram(new storm::pgcl::PgclBlock(left, this->expressionManager, this->loopCreated, this->nondetCreated, this->observeCreated));
  258. std::shared_ptr<storm::pgcl::PgclBlock> rightProgram(new storm::pgcl::PgclBlock(right, this->expressionManager, this->loopCreated, this->nondetCreated, this->observeCreated));
  259. std::shared_ptr<storm::pgcl::NondeterministicBranch> branch(new storm::pgcl::NondeterministicBranch(leftProgram, rightProgram));
  260. this->nondetCreated = true;
  261. // Sets the left program as a parent to all its children statements.
  262. for(storm::pgcl::iterator it = branch->getLeftBranch()->begin(); it != branch->getLeftBranch()->end(); ++it) {
  263. (*it)->setParentBlock(branch->getLeftBranch().get());
  264. }
  265. // Sets the right program as a parent to all its children statements.
  266. for(storm::pgcl::iterator it = branch->getRightBranch()->begin(); it != branch->getRightBranch()->end(); ++it) {
  267. (*it)->setParentBlock(branch->getRightBranch().get());
  268. }
  269. (*(branch->getLeftBranch()->back())).setLast(true);
  270. (*(branch->getRightBranch()->back())).setLast(true);
  271. branch->setLocationNumber(this->currentLocationNumber);
  272. this->locationToStatement.insert(this->locationToStatement.begin() + this->currentLocationNumber, branch);
  273. currentLocationNumber++;
  274. return branch;
  275. }
  276. std::shared_ptr<storm::pgcl::ProbabilisticBranch> PgclParser::createProbabilisticBranch(storm::expressions::Expression const& probability, std::vector<std::shared_ptr<storm::pgcl::Statement> > const& left, std::vector<std::shared_ptr<storm::pgcl::Statement> > const& right) {
  277. std::shared_ptr<storm::pgcl::PgclBlock> leftProgram(new storm::pgcl::PgclBlock(left, this->expressionManager, this->loopCreated, this->nondetCreated, this->observeCreated));
  278. std::shared_ptr<storm::pgcl::PgclBlock> rightProgram(new storm::pgcl::PgclBlock(right, this->expressionManager, this->loopCreated, this->nondetCreated, this->observeCreated));
  279. std::shared_ptr<storm::pgcl::ProbabilisticBranch> branch(new storm::pgcl::ProbabilisticBranch(probability, leftProgram, rightProgram));
  280. // Sets the left program as a parent to all its children statements.
  281. for(storm::pgcl::iterator it = branch->getLeftBranch()->begin(); it != branch->getLeftBranch()->end(); ++it) {
  282. (*it)->setParentBlock(branch->getLeftBranch().get());
  283. }
  284. // Sets the right program as a parent to all its children statements.
  285. for(storm::pgcl::iterator it = branch->getRightBranch()->begin(); it != branch->getRightBranch()->end(); ++it) {
  286. (*it)->setParentBlock(branch->getRightBranch().get());
  287. }
  288. (*(branch->getLeftBranch()->back())).setLast(true);
  289. (*(branch->getRightBranch()->back())).setLast(true);
  290. branch->setLocationNumber(this->currentLocationNumber);
  291. this->locationToStatement.insert(this->locationToStatement.begin() + this->currentLocationNumber, branch);
  292. currentLocationNumber++;
  293. return branch;
  294. }
  295. storm::pgcl::BooleanExpression PgclParser::createBooleanExpression(storm::expressions::Expression const& expression) {
  296. if(expression.hasBooleanType()) {
  297. storm::pgcl::BooleanExpression booleanExpression = storm::pgcl::BooleanExpression(expression);
  298. return booleanExpression;
  299. } else {
  300. // In case that a non-boolean expression was used (e.g. (x+5)).
  301. STORM_LOG_THROW(false, storm::exceptions::WrongFormatException, "Non boolean expression was used in a condition.");
  302. }
  303. }
  304. storm::pgcl::UniformExpression PgclParser::createUniformExpression(int const& begin, int const& end) {
  305. return storm::pgcl::UniformExpression(begin, end);
  306. }
  307. } // namespace parser
  308. } // namespace storm