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

#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<char>(inputFileStream)), (std::istreambuf_iterator<char>()));
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<PositionIteratorType> 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<storm::expressions::ExpressionManager>(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<qi::fail>(program, handler(qi::_1, qi::_2, qi::_3, qi::_4));
qi::on_error<qi::fail>(variableDeclarations, handler(qi::_1, qi::_2, qi::_3, qi::_4));
qi::on_error<qi::fail>(integerDeclaration, handler(qi::_1, qi::_2, qi::_3, qi::_4));
qi::on_error<qi::fail>(booleanDeclaration, handler(qi::_1, qi::_2, qi::_3, qi::_4));
qi::on_error<qi::fail>(assignmentStatement, handler(qi::_1, qi::_2, qi::_3, qi::_4));
qi::on_error<qi::fail>(observeStatement, handler(qi::_1, qi::_2, qi::_3, qi::_4));
qi::on_error<qi::fail>(ifElseStatement, handler(qi::_1, qi::_2, qi::_3, qi::_4));
qi::on_error<qi::fail>(loopStatement, handler(qi::_1, qi::_2, qi::_3, qi::_4));
qi::on_error<qi::fail>(branchStatement, handler(qi::_1, qi::_2, qi::_3, qi::_4));
qi::on_error<qi::fail>(expression, handler(qi::_1, qi::_2, qi::_3, qi::_4));
qi::on_error<qi::fail>(booleanCondition, handler(qi::_1, qi::_2, qi::_3, qi::_4));
qi::on_error<qi::fail>(uniformExpression, handler(qi::_1, qi::_2, qi::_3, qi::_4));
// Adds dummy to the 0-th location.
std::shared_ptr<storm::pgcl::AssignmentStatement> 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<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) {
// Creates an empty paramter list in case no parameters were given.
std::vector<storm::expressions::Variable> params;
if(parameters != boost::none) {
params = *parameters;
}
std::vector<storm::pgcl::VariableDeclaration> declarations;
for (auto const& decl : variableDeclarations) {
declarations.push_back(*decl);
}
// Creates the actual program.
std::shared_ptr<storm::pgcl::PgclProgram> 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<storm::pgcl::AssignmentStatement> PgclParser::createAssignmentStatement(std::string const& variableName, boost::variant<storm::expressions::Expression, storm::pgcl::UniformExpression> 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<storm::expressions::Expression>(assignedExpression).getType())) {
STORM_LOG_THROW(false, storm::exceptions::WrongFormatException, "Wrong type when assigning to " << variableName << ".");
}
}
std::shared_ptr<storm::pgcl::AssignmentStatement> 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<storm::pgcl::VariableDeclaration> 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<storm::pgcl::VariableDeclaration>(variable, assignedExpression);
}
std::shared_ptr<storm::pgcl::VariableDeclaration> 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<storm::pgcl::VariableDeclaration>(variable, assignedExpression);
}
std::shared_ptr<storm::pgcl::ObserveStatement> PgclParser::createObserveStatement(storm::pgcl::BooleanExpression const& condition) {
std::shared_ptr<storm::pgcl::ObserveStatement> 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<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) {
std::shared_ptr<storm::pgcl::PgclBlock> ifBodyProgram(new storm::pgcl::PgclBlock(ifBody, this->expressionManager, this->loopCreated, this->nondetCreated, this->observeCreated));
std::shared_ptr<storm::pgcl::IfStatement> ifElse;
if(elseBody) {
std::shared_ptr<storm::pgcl::PgclBlock> elseBodyProgram(new storm::pgcl::PgclBlock(*elseBody, this->expressionManager, this->loopCreated, this->nondetCreated, this->observeCreated));
ifElse = std::shared_ptr<storm::pgcl::IfStatement>(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<storm::pgcl::IfStatement>(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<storm::pgcl::LoopStatement> PgclParser::createLoopStatement(storm::pgcl::BooleanExpression const& condition, std::vector<std::shared_ptr<storm::pgcl::Statement> > const& body) {
std::shared_ptr<storm::pgcl::PgclBlock> bodyProgram(new storm::pgcl::PgclBlock(body, this->expressionManager, this->loopCreated, this->nondetCreated, this->observeCreated));
std::shared_ptr<storm::pgcl::LoopStatement> 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<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) {
std::shared_ptr<storm::pgcl::PgclBlock> leftProgram(new storm::pgcl::PgclBlock(left, this->expressionManager, this->loopCreated, this->nondetCreated, this->observeCreated));
std::shared_ptr<storm::pgcl::PgclBlock> rightProgram(new storm::pgcl::PgclBlock(right, this->expressionManager, this->loopCreated, this->nondetCreated, this->observeCreated));
std::shared_ptr<storm::pgcl::NondeterministicBranch> 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<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) {
std::shared_ptr<storm::pgcl::PgclBlock> leftProgram(new storm::pgcl::PgclBlock(left, this->expressionManager, this->loopCreated, this->nondetCreated, this->observeCreated));
std::shared_ptr<storm::pgcl::PgclBlock> rightProgram(new storm::pgcl::PgclBlock(right, this->expressionManager, this->loopCreated, this->nondetCreated, this->observeCreated));
std::shared_ptr<storm::pgcl::ProbabilisticBranch> 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