1574 lines
128 KiB

7 years ago
  1. #include "JaniParser.h"
  2. #include "storm/storage/jani/Edge.h"
  3. #include "storm/storage/jani/TemplateEdge.h"
  4. #include "storm/storage/jani/EdgeDestination.h"
  5. #include "storm/storage/jani/Model.h"
  6. #include "storm/storage/jani/Automaton.h"
  7. #include "storm/storage/jani/Location.h"
  8. #include "storm/storage/jani/Property.h"
  9. #include "storm/storage/jani/AutomatonComposition.h"
  10. #include "storm/storage/jani/ParallelComposition.h"
  11. #include "storm/storage/jani/ModelType.h"
  12. #include "storm/storage/jani/CompositionInformationVisitor.h"
  13. #include "storm/storage/jani/expressions/JaniExpressions.h"
  14. #include "storm/logic/RewardAccumulationEliminationVisitor.h"
  15. #include "storm/exceptions/FileIoException.h"
  16. #include "storm/exceptions/InvalidJaniException.h"
  17. #include "storm/exceptions/NotSupportedException.h"
  18. #include "storm/exceptions/NotImplementedException.h"
  19. #include "storm/modelchecker/results/FilterType.h"
  20. #include <iostream>
  21. #include <sstream>
  22. #include <fstream>
  23. #include <boost/lexical_cast.hpp>
  24. #include "storm/storage/jani/ArrayVariable.h"
  25. #include "storm/utility/macros.h"
  26. #include "storm/utility/file.h"
  27. namespace storm {
  28. namespace parser {
  29. ////////////
  30. // Defaults
  31. ////////////
  32. const bool JaniParser::defaultVariableTransient = false;
  33. const bool JaniParser::defaultBooleanInitialValue = false;
  34. const double JaniParser::defaultRationalInitialValue = 0.0;
  35. const int64_t JaniParser::defaultIntegerInitialValue = 0;
  36. const std::string VARIABLE_AUTOMATON_DELIMITER = "_";
  37. const std::set<std::string> JaniParser::unsupportedOpstrings({"sin", "cos", "tan", "cot", "sec", "csc", "asin", "acos", "atan", "acot", "asec", "acsc",
  38. "sinh", "cosh", "tanh", "coth", "sech", "csch", "asinh", "acosh", "atanh", "asinh", "acosh"});
  39. std::string getString(json const& structure, std::string const& errorInfo) {
  40. STORM_LOG_THROW(structure.is_string(), storm::exceptions::InvalidJaniException, "Expected a string in " << errorInfo << ", got '" << structure.dump() << "'");
  41. return structure.front();
  42. }
  43. bool getBoolean(json const& structure, std::string const& errorInfo) {
  44. STORM_LOG_THROW(structure.is_boolean(), storm::exceptions::InvalidJaniException, "Expected a Boolean in " << errorInfo << ", got " << structure.dump() << "'");
  45. return structure.front();
  46. }
  47. uint64_t getUnsignedInt(json const& structure, std::string const& errorInfo) {
  48. STORM_LOG_THROW(structure.is_number(), storm::exceptions::InvalidJaniException, "Expected a number in " << errorInfo << ", got '" << structure.dump() << "'");
  49. int num = structure.front();
  50. STORM_LOG_THROW(num >= 0, storm::exceptions::InvalidJaniException, "Expected a positive number in " << errorInfo << ", got '" << num << "'");
  51. return static_cast<uint64_t>(num);
  52. }
  53. int64_t getSignedInt(json const& structure, std::string const& errorInfo) {
  54. STORM_LOG_THROW(structure.is_number(), storm::exceptions::InvalidJaniException, "Expected a number in " << errorInfo << ", got '" << structure.dump() << "'");
  55. int num = structure.front();
  56. return static_cast<int64_t>(num);
  57. }
  58. std::pair<storm::jani::Model, std::vector<storm::jani::Property>> JaniParser::parse(std::string const& path, bool parseProperties) {
  59. JaniParser parser;
  60. parser.readFile(path);
  61. return parser.parseModel(parseProperties);
  62. }
  63. JaniParser::JaniParser(std::string const& jsonstring) {
  64. parsedStructure = json::parse(jsonstring);
  65. }
  66. void JaniParser::readFile(std::string const &path) {
  67. std::ifstream file;
  68. storm::utility::openFile(path, file);
  69. parsedStructure << file;
  70. storm::utility::closeFile(file);
  71. }
  72. std::pair<storm::jani::Model, std::vector<storm::jani::Property>> JaniParser::parseModel(bool parseProperties) {
  73. //jani-version
  74. STORM_LOG_THROW(parsedStructure.count("jani-version") == 1, storm::exceptions::InvalidJaniException, "Jani-version must be given exactly once.");
  75. uint64_t version = getUnsignedInt(parsedStructure.at("jani-version"), "jani version");
  76. STORM_LOG_WARN_COND(version >= 1 && version <=1, "JANI Version " << version << " is not supported. Results may be wrong.");
  77. //name
  78. STORM_LOG_THROW(parsedStructure.count("name") == 1, storm::exceptions::InvalidJaniException, "A model must have a (single) name");
  79. std::string name = getString(parsedStructure.at("name"), "model name");
  80. //model type
  81. STORM_LOG_THROW(parsedStructure.count("type") == 1, storm::exceptions::InvalidJaniException, "A type must be given exactly once");
  82. std::string modeltypestring = getString(parsedStructure.at("type"), "type of the model");
  83. storm::jani::ModelType type = storm::jani::getModelType(modeltypestring);
  84. STORM_LOG_THROW(type != storm::jani::ModelType::UNDEFINED, storm::exceptions::InvalidJaniException, "model type " + modeltypestring + " not recognized");
  85. storm::jani::Model model(name, type, version, expressionManager);
  86. size_t featuresCount = parsedStructure.count("features");
  87. STORM_LOG_THROW(featuresCount < 2, storm::exceptions::InvalidJaniException, "features-declarations can be given at most once.");
  88. if (featuresCount == 1) {
  89. auto allKnownModelFeatures = storm::jani::getAllKnownModelFeatures();
  90. for (auto const& feature : parsedStructure.at("features")) {
  91. std::string featureStr = getString(feature, "Model feature");
  92. bool found = false;
  93. for (auto const& knownFeature : allKnownModelFeatures.asSet()) {
  94. if (featureStr == storm::jani::toString(knownFeature)) {
  95. model.getModelFeatures().add(knownFeature);
  96. found = true;
  97. break;
  98. }
  99. }
  100. STORM_LOG_THROW(found, storm::exceptions::NotSupportedException, "Storm does not support the model feature " << featureStr);
  101. }
  102. }
  103. size_t actionCount = parsedStructure.count("actions");
  104. STORM_LOG_THROW(actionCount < 2, storm::exceptions::InvalidJaniException, "Action-declarations can be given at most once.");
  105. if (actionCount > 0) {
  106. parseActions(parsedStructure.at("actions"), model);
  107. }
  108. Scope scope(name);
  109. // Parse constants
  110. ConstantsMap constants;
  111. scope.constants = &constants;
  112. size_t constantsCount = parsedStructure.count("constants");
  113. STORM_LOG_THROW(constantsCount < 2, storm::exceptions::InvalidJaniException, "Constant-declarations can be given at most once.");
  114. if (constantsCount == 1) {
  115. // Reserve enough space to make sure that pointers to constants remain valid after adding new ones.
  116. model.getConstants().reserve(parsedStructure.at("constants").size());
  117. for (auto const &constStructure : parsedStructure.at("constants")) {
  118. std::shared_ptr<storm::jani::Constant> constant = parseConstant(constStructure, scope.refine("constants[" + std::to_string(constants.size()) + "]"));
  119. model.addConstant(*constant);
  120. assert(model.getConstants().back().getName() == constant->getName());
  121. constants.emplace(constant->getName(), &model.getConstants().back());
  122. }
  123. }
  124. // Parse variables
  125. size_t variablesCount = parsedStructure.count("variables");
  126. STORM_LOG_THROW(variablesCount < 2, storm::exceptions::InvalidJaniException, "Variable-declarations can be given at most once for global variables.");
  127. VariablesMap globalVars;
  128. scope.globalVars = &globalVars;
  129. if (variablesCount == 1) {
  130. bool requireInitialValues = parsedStructure.count("restrict-initial") == 0;
  131. for (auto const& varStructure : parsedStructure.at("variables")) {
  132. std::shared_ptr<storm::jani::Variable> variable = parseVariable(varStructure, requireInitialValues, scope.refine("variables[" + std::to_string(globalVars.size())));
  133. globalVars.emplace(variable->getName(), &model.addVariable(*variable));
  134. }
  135. }
  136. uint64_t funDeclCount = parsedStructure.count("functions");
  137. STORM_LOG_THROW(funDeclCount < 2, storm::exceptions::InvalidJaniException, "Model '" << name << "' has more than one list of functions");
  138. FunctionsMap globalFuns;
  139. scope.globalFunctions = &globalFuns;
  140. if (funDeclCount > 0) {
  141. // We require two passes through the function definitions array to allow referring to functions before they were defined.
  142. std::vector<storm::jani::FunctionDefinition> dummyFunctionDefinitions;
  143. for (auto const& funStructure : parsedStructure.at("functions")) {
  144. // Skip parsing of function body
  145. dummyFunctionDefinitions.push_back(parseFunctionDefinition(funStructure, scope.refine("functions[" + std::to_string(globalFuns.size()) + "] of model " + name),
  146. true));
  147. }
  148. // Store references to the dummy function definitions. This needs to happen in a separate loop since otherwise, references to FunDefs can be invalidated after calling dummyFunctionDefinitions.push_back
  149. for (auto const& funDef : dummyFunctionDefinitions) {
  150. bool unused = globalFuns.emplace(funDef.getName(), &funDef).second;
  151. STORM_LOG_THROW(unused, storm::exceptions::InvalidJaniException, "Multiple definitions of functions with the name " << funDef.getName() << " in " << scope.description);
  152. }
  153. for (auto const& funStructure : parsedStructure.at("functions")) {
  154. // Actually parse the function body
  155. storm::jani::FunctionDefinition funDef = parseFunctionDefinition(funStructure, scope.refine("functions[" + std::to_string(globalFuns.size()) + "] of model " + name),
  156. false);
  157. assert(globalFuns.count(funDef.getName()) == 1);
  158. globalFuns[funDef.getName()] = &model.addFunctionDefinition(funDef);
  159. }
  160. }
  161. // Parse Automata
  162. STORM_LOG_THROW(parsedStructure.count("automata") == 1, storm::exceptions::InvalidJaniException, "Exactly one list of automata must be given");
  163. STORM_LOG_THROW(parsedStructure.at("automata").is_array(), storm::exceptions::InvalidJaniException, "Automata must be an array");
  164. // Automatons can only be parsed after constants and variables.
  165. for (auto const& automataEntry : parsedStructure.at("automata")) {
  166. model.addAutomaton(parseAutomaton(automataEntry, model, scope.refine("automata[" + std::to_string(model.getNumberOfAutomata()) + "]")));
  167. }
  168. STORM_LOG_THROW(parsedStructure.count("restrict-initial") < 2, storm::exceptions::InvalidJaniException, "Model has multiple initial value restrictions");
  169. storm::expressions::Expression initialValueRestriction = expressionManager->boolean(true);
  170. if (parsedStructure.count("restrict-initial") > 0) {
  171. STORM_LOG_THROW(parsedStructure.at("restrict-initial").count("exp") == 1, storm::exceptions::InvalidJaniException, "Model needs an expression inside the initial restricion");
  172. initialValueRestriction = parseExpression(parsedStructure.at("restrict-initial").at("exp"), scope.refine("Initial value restriction"));
  173. }
  174. model.setInitialStatesRestriction(initialValueRestriction);
  175. STORM_LOG_THROW(parsedStructure.count("system") == 1, storm::exceptions::InvalidJaniException, "Exactly one system description must be given");
  176. std::shared_ptr<storm::jani::Composition> composition = parseComposition(parsedStructure.at("system"));
  177. model.setSystemComposition(composition);
  178. model.finalize();
  179. // Parse properties
  180. storm::logic::RewardAccumulationEliminationVisitor rewAccEliminator(model);
  181. STORM_LOG_THROW(parsedStructure.count("properties") <= 1, storm::exceptions::InvalidJaniException, "At most one list of properties can be given");
  182. std::vector<storm::jani::Property> properties;
  183. if (parseProperties && parsedStructure.count("properties") == 1) {
  184. STORM_LOG_THROW(parsedStructure.at("properties").is_array(), storm::exceptions::InvalidJaniException, "Properties should be an array");
  185. for(auto const& propertyEntry : parsedStructure.at("properties")) {
  186. try {
  187. auto prop = this->parseProperty(model, propertyEntry, scope.refine("property[" + std::to_string(properties.size()) + "]"));
  188. // Eliminate reward accumulations as much as possible
  189. rewAccEliminator.eliminateRewardAccumulations(prop);
  190. properties.push_back(prop);
  191. } catch (storm::exceptions::NotSupportedException const& ex) {
  192. STORM_LOG_WARN("Cannot handle property: " << ex.what());
  193. } catch (storm::exceptions::NotImplementedException const& ex) {
  194. STORM_LOG_WARN("Cannot handle property: " << ex.what());
  195. }
  196. }
  197. }
  198. return {model, properties};
  199. }
  200. std::vector<std::shared_ptr<storm::logic::Formula const>> JaniParser::parseUnaryFormulaArgument(storm::jani::Model& model, json const& propertyStructure, storm::logic::FormulaContext formulaContext, std::string const& opstring, Scope const& scope) {
  201. STORM_LOG_THROW(propertyStructure.count("exp") == 1, storm::exceptions::InvalidJaniException, "Expecting operand for operator " << opstring << " in " << scope.description);
  202. return { parseFormula(model, propertyStructure.at("exp"), formulaContext, scope.refine("Operand of operator " + opstring)) };
  203. }
  204. std::vector<std::shared_ptr<storm::logic::Formula const>> JaniParser::parseBinaryFormulaArguments(storm::jani::Model& model, json const& propertyStructure, storm::logic::FormulaContext formulaContext, std::string const& opstring, Scope const& scope) {
  205. STORM_LOG_THROW(propertyStructure.count("left") == 1, storm::exceptions::InvalidJaniException, "Expecting left operand for operator " << opstring << " in " << scope.description);
  206. STORM_LOG_THROW(propertyStructure.count("right") == 1, storm::exceptions::InvalidJaniException, "Expecting right operand for operator " << opstring << " in " << scope.description);
  207. return { parseFormula(model, propertyStructure.at("left"), formulaContext, scope.refine("Operand of operator " + opstring)), parseFormula(model, propertyStructure.at("right"), formulaContext, scope.refine("Operand of operator " + opstring)) };
  208. }
  209. storm::jani::PropertyInterval JaniParser::parsePropertyInterval(json const& piStructure, Scope const& scope) {
  210. storm::jani::PropertyInterval pi;
  211. if (piStructure.count("lower") > 0) {
  212. pi.lowerBound = parseExpression(piStructure.at("lower"), scope.refine("Lower bound for property interval"));
  213. }
  214. if (piStructure.count("lower-exclusive") > 0) {
  215. STORM_LOG_THROW(pi.lowerBound.isInitialized(), storm::exceptions::InvalidJaniException, "Lower-exclusive can only be set if a lower bound is present");
  216. pi.lowerBoundStrict = piStructure.at("lower-exclusive");
  217. }
  218. if (piStructure.count("upper") > 0) {
  219. pi.upperBound = parseExpression(piStructure.at("upper"), scope.refine("Upper bound for property interval"));
  220. }
  221. if (piStructure.count("upper-exclusive") > 0) {
  222. STORM_LOG_THROW(pi.upperBound.isInitialized(), storm::exceptions::InvalidJaniException, "Lower-exclusive can only be set if a lower bound is present");
  223. pi.upperBoundStrict = piStructure.at("upper-exclusive");
  224. }
  225. STORM_LOG_THROW(pi.lowerBound.isInitialized() || pi.upperBound.isInitialized(), storm::exceptions::InvalidJaniException, "Bounded operator must have a bounded interval, but no bounds found in '" << piStructure << "'");
  226. return pi;
  227. }
  228. storm::logic::RewardAccumulation JaniParser::parseRewardAccumulation(json const& accStructure, std::string const& context) {
  229. bool accTime = false;
  230. bool accSteps = false;
  231. bool accExit = false;
  232. STORM_LOG_THROW(accStructure.is_array(), storm::exceptions::InvalidJaniException, "Accumulate should be an array");
  233. for (auto const& accEntry : accStructure) {
  234. if (accEntry == "steps") {
  235. accSteps = true;
  236. } else if (accEntry == "time") {
  237. accTime = true;
  238. } else if (accEntry == "exit") {
  239. accExit = true;
  240. } else {
  241. STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException, "One may only accumulate either 'steps' or 'time' or 'exit', got " << accEntry.dump() << " in " << context);
  242. }
  243. }
  244. return storm::logic::RewardAccumulation(accSteps, accTime, accExit);
  245. }
  246. void insertLowerUpperTimeBounds(std::vector<boost::optional<storm::logic::TimeBound>>& lowerBounds, std::vector<boost::optional<storm::logic::TimeBound>>& upperBounds, storm::jani::PropertyInterval const& pi) {
  247. if (pi.hasLowerBound()) {
  248. lowerBounds.push_back(storm::logic::TimeBound(pi.lowerBoundStrict, pi.lowerBound));
  249. } else {
  250. lowerBounds.push_back(boost::none);
  251. }
  252. if (pi.hasUpperBound()) {
  253. upperBounds.push_back(storm::logic::TimeBound(pi.upperBoundStrict, pi.upperBound));
  254. } else {
  255. upperBounds.push_back(boost::none);
  256. }
  257. }
  258. std::shared_ptr<storm::logic::Formula const> JaniParser::parseFormula(storm::jani::Model& model, json const& propertyStructure, storm::logic::FormulaContext formulaContext, Scope const& scope, boost::optional<storm::logic::Bound> bound) {
  259. if (propertyStructure.is_boolean()) {
  260. return std::make_shared<storm::logic::BooleanLiteralFormula>(propertyStructure.get<bool>());
  261. }
  262. if (propertyStructure.is_string()) {
  263. if (labels.count(propertyStructure.get<std::string>()) > 0) {
  264. return std::make_shared<storm::logic::AtomicLabelFormula>(propertyStructure.get<std::string>());
  265. }
  266. }
  267. storm::expressions::Expression expr = parseExpression(propertyStructure, scope.refine("expression in property"), true);
  268. if (expr.isInitialized()) {
  269. bool exprContainsLabel = false;
  270. auto varsInExpr = expr.getVariables();
  271. for (auto const& varInExpr : varsInExpr) {
  272. if (labels.count(varInExpr.getName()) > 0) {
  273. exprContainsLabel = true;
  274. break;
  275. }
  276. }
  277. if (!exprContainsLabel) {
  278. assert(bound == boost::none);
  279. return std::make_shared<storm::logic::AtomicExpressionFormula>(expr);
  280. }
  281. }
  282. if (propertyStructure.count("op") == 1) {
  283. std::string opString = getString(propertyStructure.at("op"), "Operation description");
  284. if(opString == "Pmin" || opString == "Pmax") {
  285. std::vector<std::shared_ptr<storm::logic::Formula const>> args = parseUnaryFormulaArgument(model, propertyStructure, storm::logic::FormulaContext::Probability, opString, scope);
  286. assert(args.size() == 1);
  287. storm::logic::OperatorInformation opInfo;
  288. opInfo.optimalityType = opString == "Pmin" ? storm::solver::OptimizationDirection::Minimize : storm::solver::OptimizationDirection::Maximize;
  289. opInfo.bound = bound;
  290. return std::make_shared<storm::logic::ProbabilityOperatorFormula>(args[0], opInfo);
  291. } else if (opString == "" || opString == "") {
  292. assert(bound == boost::none);
  293. STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Forall and Exists are currently not supported in " << scope.description);
  294. } else if (opString == "Emin" || opString == "Emax") {
  295. STORM_LOG_WARN_COND(model.getJaniVersion() == 1, "Model not compliant: Contains Emin/Emax property in " << scope.description << ".");
  296. STORM_LOG_THROW(propertyStructure.count("exp") == 1, storm::exceptions::InvalidJaniException, "Expecting reward-expression for operator " << opString << " in " << scope.description);
  297. storm::expressions::Expression rewExpr = parseExpression(propertyStructure.at("exp"), scope.refine("Reward expression"));
  298. STORM_LOG_THROW(rewExpr.hasNumericalType(), storm::exceptions::InvalidJaniException, "Reward expression '" << rewExpr << "' does not have numerical type in " << scope.description);
  299. std::string rewardName = rewExpr.toString();
  300. storm::logic::OperatorInformation opInfo;
  301. opInfo.optimalityType = opString == "Emin" ? storm::solver::OptimizationDirection::Minimize : storm::solver::OptimizationDirection::Maximize;
  302. opInfo.bound = bound;
  303. storm::logic::RewardAccumulation rewardAccumulation(false, false, false);
  304. if (propertyStructure.count("accumulate") > 0) {
  305. rewardAccumulation = parseRewardAccumulation(propertyStructure.at("accumulate"), scope.description);
  306. }
  307. bool time = false;
  308. if (propertyStructure.count("step-instant") > 0) {
  309. STORM_LOG_THROW(propertyStructure.count("time-instant") == 0, storm::exceptions::NotSupportedException, "Storm does not support to have a step-instant and a time-instant in " + scope.description);
  310. STORM_LOG_THROW(propertyStructure.count("reward-instants") == 0, storm::exceptions::NotSupportedException, "Storm does not support to have a step-instant and a reward-instant in " + scope.description);
  311. storm::expressions::Expression stepInstantExpr = parseExpression(propertyStructure.at("step-instant"), scope.refine("Step instant"));
  312. if (!rewExpr.isVariable()) {
  313. model.addNonTrivialRewardExpression(rewardName, rewExpr);
  314. }
  315. if (rewardAccumulation.isEmpty()) {
  316. return std::make_shared<storm::logic::RewardOperatorFormula>(std::make_shared<storm::logic::InstantaneousRewardFormula>(stepInstantExpr, storm::logic::TimeBoundType::Steps), rewardName, opInfo);
  317. } else {
  318. return std::make_shared<storm::logic::RewardOperatorFormula>(std::make_shared<storm::logic::CumulativeRewardFormula>(storm::logic::TimeBound(false, stepInstantExpr), storm::logic::TimeBoundReference(storm::logic::TimeBoundType::Steps), rewardAccumulation), rewardName, opInfo);
  319. }
  320. } else if (propertyStructure.count("time-instant") > 0) {
  321. STORM_LOG_THROW(propertyStructure.count("reward-instants") == 0, storm::exceptions::NotSupportedException, "Storm does not support to have a time-instant and a reward-instant in " + scope.description);
  322. storm::expressions::Expression timeInstantExpr = parseExpression(propertyStructure.at("time-instant"), scope.refine("time instant"));
  323. if (!rewExpr.isVariable()) {
  324. model.addNonTrivialRewardExpression(rewardName, rewExpr);
  325. }
  326. if (rewardAccumulation.isEmpty()) {
  327. return std::make_shared<storm::logic::RewardOperatorFormula>(std::make_shared<storm::logic::InstantaneousRewardFormula>(timeInstantExpr, storm::logic::TimeBoundType::Time), rewardName, opInfo);
  328. } else {
  329. return std::make_shared<storm::logic::RewardOperatorFormula>(std::make_shared<storm::logic::CumulativeRewardFormula>(storm::logic::TimeBound(false, timeInstantExpr), storm::logic::TimeBoundReference(storm::logic::TimeBoundType::Time), rewardAccumulation), rewardName, opInfo);
  330. }
  331. } else if (propertyStructure.count("reward-instants") > 0) {
  332. std::vector<storm::logic::TimeBound> bounds;
  333. std::vector<storm::logic::TimeBoundReference> boundReferences;
  334. for (auto const& rewInst : propertyStructure.at("reward-instants")) {
  335. storm::expressions::Expression rewInstRewardModelExpression = parseExpression(rewInst.at("exp"), scope.refine("Reward expression at reward instant"));
  336. STORM_LOG_THROW(rewInstRewardModelExpression.hasNumericalType(), storm::exceptions::InvalidJaniException, "Reward expression '" << rewInstRewardModelExpression << "' does not have numerical type in " << scope.description);
  337. storm::logic::RewardAccumulation boundRewardAccumulation = parseRewardAccumulation(rewInst.at("accumulate"), scope.description);
  338. bool steps = (boundRewardAccumulation.isStepsSet() || boundRewardAccumulation.isExitSet()) && boundRewardAccumulation.size() == 1;
  339. bool time = boundRewardAccumulation.isTimeSet() && boundRewardAccumulation.size() == 1 && !model.isDiscreteTimeModel();
  340. if ((steps || time) && !rewInstRewardModelExpression.containsVariables() && storm::utility::isOne(rewInstRewardModelExpression.evaluateAsRational())) {
  341. boundReferences.emplace_back(steps ? storm::logic::TimeBoundType::Steps : storm::logic::TimeBoundType::Time);
  342. } else {
  343. std::string rewInstRewardModelName = rewInstRewardModelExpression.toString();
  344. if (!rewInstRewardModelExpression.isVariable()) {
  345. model.addNonTrivialRewardExpression(rewInstRewardModelName, rewInstRewardModelExpression);
  346. }
  347. boundReferences.emplace_back(rewInstRewardModelName, boundRewardAccumulation);
  348. }
  349. storm::expressions::Expression rewInstantExpr = parseExpression(rewInst.at("instant"), scope.refine("reward instant"));
  350. bounds.emplace_back(false, rewInstantExpr);
  351. }
  352. if (!rewExpr.isVariable()) {
  353. model.addNonTrivialRewardExpression(rewardName, rewExpr);
  354. }
  355. return std::make_shared<storm::logic::RewardOperatorFormula>(std::make_shared<storm::logic::CumulativeRewardFormula>(bounds, boundReferences, rewardAccumulation), rewardName, opInfo);
  356. } else {
  357. time = !rewExpr.containsVariables() && storm::utility::isOne(rewExpr.evaluateAsRational());
  358. std::shared_ptr<storm::logic::Formula const> subformula;
  359. if (propertyStructure.count("reach") > 0) {
  360. auto formulaContext = time ? storm::logic::FormulaContext::Time : storm::logic::FormulaContext::Reward;
  361. subformula = std::make_shared<storm::logic::EventuallyFormula>(parseFormula(model, propertyStructure.at("reach"), formulaContext, scope.refine("Reach-expression of operator " + opString)), formulaContext, rewardAccumulation);
  362. } else {
  363. subformula = std::make_shared<storm::logic::TotalRewardFormula>(rewardAccumulation);
  364. }
  365. if (time) {
  366. assert(subformula->isTotalRewardFormula() || subformula->isTimePathFormula());
  367. return std::make_shared<storm::logic::TimeOperatorFormula>(subformula, opInfo);
  368. } else {
  369. if (!rewExpr.isVariable()) {
  370. model.addNonTrivialRewardExpression(rewardName, rewExpr);
  371. }
  372. return std::make_shared<storm::logic::RewardOperatorFormula>(subformula, rewardName, opInfo);
  373. }
  374. }
  375. } else if (opString == "Smin" || opString == "Smax") {
  376. storm::logic::OperatorInformation opInfo;
  377. opInfo.optimalityType = opString == "Smin" ? storm::solver::OptimizationDirection::Minimize : storm::solver::OptimizationDirection::Maximize;
  378. opInfo.bound = bound;
  379. // Reward accumulation is optional as it was not available in the early days...
  380. boost::optional<storm::logic::RewardAccumulation> rewardAccumulation;
  381. if (propertyStructure.count("accumulate") > 0) {
  382. STORM_LOG_WARN_COND(model.getJaniVersion() == 1, "Unexpected accumulate field in " << scope.description << ".");
  383. rewardAccumulation = parseRewardAccumulation(propertyStructure.at("accumulate"), scope.description);
  384. }
  385. STORM_LOG_THROW(propertyStructure.count("exp") > 0, storm::exceptions::InvalidJaniException, "Expected an expression at steady state property at " << scope.description);
  386. auto exp = parseExpression(propertyStructure["exp"], scope.refine("steady-state operator"), true);
  387. if (!exp.isInitialized() || exp.hasBooleanType()) {
  388. STORM_LOG_THROW(!rewardAccumulation.is_initialized(), storm::exceptions::InvalidJaniException, "Long-run average probabilities are not allowed to have a reward accumulation at" << scope.description);
  389. std::shared_ptr<storm::logic::Formula const> subformula = parseUnaryFormulaArgument(model, propertyStructure, formulaContext, opString, scope.refine("Steady-state operator"))[0];
  390. return std::make_shared<storm::logic::LongRunAverageOperatorFormula>(subformula, opInfo);
  391. }
  392. STORM_LOG_THROW(exp.hasNumericalType(), storm::exceptions::InvalidJaniException, "Reward expression '" << exp << "' does not have numerical type in " << scope.description);
  393. std::string rewardName = exp.toString();
  394. if (!exp.isVariable()) {
  395. model.addNonTrivialRewardExpression(rewardName, exp);
  396. }
  397. auto subformula = std::make_shared<storm::logic::LongRunAverageRewardFormula>(rewardAccumulation);
  398. return std::make_shared<storm::logic::RewardOperatorFormula>(subformula, rewardName, opInfo);
  399. } else if (opString == "U" || opString == "F") {
  400. assert(bound == boost::none);
  401. std::vector<std::shared_ptr<storm::logic::Formula const>> args;
  402. if (opString == "U") {
  403. args = parseBinaryFormulaArguments(model, propertyStructure, formulaContext, opString, scope);
  404. } else {
  405. assert(opString == "F");
  406. args = parseUnaryFormulaArgument(model, propertyStructure, formulaContext, opString, scope);
  407. args.push_back(args[0]);
  408. args[0] = storm::logic::BooleanLiteralFormula::getTrueFormula();
  409. }
  410. std::vector<boost::optional<storm::logic::TimeBound>> lowerBounds, upperBounds;
  411. std::vector<storm::logic::TimeBoundReference> tbReferences;
  412. if (propertyStructure.count("step-bounds") > 0) {
  413. STORM_LOG_WARN_COND(model.getJaniVersion() == 1, "Jani model not compliant: Contains step-bounds in " << scope.description << ".");
  414. storm::jani::PropertyInterval pi = parsePropertyInterval(propertyStructure.at("step-bounds"), scope.refine("step-bounded until").clearVariables());
  415. insertLowerUpperTimeBounds(lowerBounds, upperBounds, pi);
  416. tbReferences.emplace_back(storm::logic::TimeBoundType::Steps);
  417. }
  418. if (propertyStructure.count("time-bounds") > 0) {
  419. STORM_LOG_WARN_COND(model.getJaniVersion() == 1, "Jani model not compliant: Contains time-bounds in " << scope.description << ".");
  420. storm::jani::PropertyInterval pi = parsePropertyInterval(propertyStructure.at("time-bounds"), scope.refine("time-bounded until").clearVariables());
  421. insertLowerUpperTimeBounds(lowerBounds, upperBounds, pi);
  422. tbReferences.emplace_back(storm::logic::TimeBoundType::Time);
  423. }
  424. if (propertyStructure.count("reward-bounds") > 0 ) {
  425. for (auto const& rbStructure : propertyStructure.at("reward-bounds")) {
  426. storm::jani::PropertyInterval pi = parsePropertyInterval(rbStructure.at("bounds"), scope.refine("reward-bounded until").clearVariables());
  427. insertLowerUpperTimeBounds(lowerBounds, upperBounds, pi);
  428. STORM_LOG_THROW(rbStructure.count("exp") == 1, storm::exceptions::InvalidJaniException, "Expecting reward-expression for operator " << opString << " in " << scope.description);
  429. storm::expressions::Expression rewInstRewardModelExpression = parseExpression(rbStructure.at("exp"), scope.refine("Reward expression at reward-bounds"));
  430. STORM_LOG_THROW(rewInstRewardModelExpression.hasNumericalType(), storm::exceptions::InvalidJaniException, "Reward expression '" << rewInstRewardModelExpression << "' does not have numerical type in " << scope.description);
  431. storm::logic::RewardAccumulation boundRewardAccumulation = parseRewardAccumulation(rbStructure.at("accumulate"), scope.description);
  432. bool steps = (boundRewardAccumulation.isStepsSet() || boundRewardAccumulation.isExitSet()) && boundRewardAccumulation.size() == 1;
  433. bool time = boundRewardAccumulation.isTimeSet() && boundRewardAccumulation.size() == 1 && !model.isDiscreteTimeModel();
  434. if ((steps || time) && !rewInstRewardModelExpression.containsVariables() && storm::utility::isOne(rewInstRewardModelExpression.evaluateAsRational())) {
  435. tbReferences.emplace_back(steps ? storm::logic::TimeBoundType::Steps : storm::logic::TimeBoundType::Time);
  436. } else {
  437. std::string rewInstRewardModelName = rewInstRewardModelExpression.toString();
  438. if (!rewInstRewardModelExpression.isVariable()) {
  439. model.addNonTrivialRewardExpression(rewInstRewardModelName, rewInstRewardModelExpression);
  440. }
  441. tbReferences.emplace_back(rewInstRewardModelName, boundRewardAccumulation);
  442. }
  443. }
  444. }
  445. if (!tbReferences.empty()) {
  446. return std::make_shared<storm::logic::BoundedUntilFormula const>(args[0], args[1], lowerBounds, upperBounds, tbReferences);
  447. } else if (args[0]->isTrueFormula()) {
  448. return std::make_shared<storm::logic::EventuallyFormula const>(args[1], formulaContext);
  449. } else {
  450. return std::make_shared<storm::logic::UntilFormula const>(args[0], args[1]);
  451. }
  452. } else if (opString == "G") {
  453. assert(bound == boost::none);
  454. std::vector<std::shared_ptr<storm::logic::Formula const>> args = parseUnaryFormulaArgument(model, propertyStructure, formulaContext, opString, scope.refine("Subformula of globally operator "));
  455. if (propertyStructure.count("step-bounds") > 0) {
  456. STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Globally and step-bounds are not supported.");
  457. } else if (propertyStructure.count("time-bounds") > 0) {
  458. STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Globally and time bounds are not supported.");
  459. } else if (propertyStructure.count("reward-bounds") > 0 ) {
  460. STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Globally and reward bounded properties are not supported.");
  461. }
  462. return std::make_shared<storm::logic::GloballyFormula const>(args[0]);
  463. } else if (opString == "W") {
  464. assert(bound == boost::none);
  465. STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Weak until is not supported");
  466. } else if (opString == "R") {
  467. assert(bound == boost::none);
  468. STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Release is not supported");
  469. } else if (opString == "" || opString == "") {
  470. assert(bound == boost::none);
  471. std::vector<std::shared_ptr<storm::logic::Formula const>> args = parseBinaryFormulaArguments(model, propertyStructure, formulaContext, opString, scope);
  472. assert(args.size() == 2);
  473. storm::logic::BinaryBooleanStateFormula::OperatorType oper = opString == "" ? storm::logic::BinaryBooleanStateFormula::OperatorType::And : storm::logic::BinaryBooleanStateFormula::OperatorType::Or;
  474. return std::make_shared<storm::logic::BinaryBooleanStateFormula const>(oper, args[0], args[1]);
  475. } else if (opString == "") {
  476. assert(bound == boost::none);
  477. std::vector<std::shared_ptr<storm::logic::Formula const>> args = parseBinaryFormulaArguments(model, propertyStructure, formulaContext, opString, scope);
  478. assert(args.size() == 2);
  479. std::shared_ptr<storm::logic::UnaryBooleanStateFormula const> tmp = std::make_shared<storm::logic::UnaryBooleanStateFormula const>(storm::logic::UnaryBooleanStateFormula::OperatorType::Not, args[0]);
  480. return std::make_shared<storm::logic::BinaryBooleanStateFormula const>(storm::logic::BinaryBooleanStateFormula::OperatorType::Or, tmp, args[1]);
  481. } else if (opString == "¬") {
  482. assert(bound == boost::none);
  483. std::vector<std::shared_ptr<storm::logic::Formula const>> args = parseUnaryFormulaArgument(model, propertyStructure, formulaContext, opString, scope);
  484. assert(args.size() == 1);
  485. return std::make_shared<storm::logic::UnaryBooleanStateFormula const>(storm::logic::UnaryBooleanStateFormula::OperatorType::Not, args[0]);
  486. } else if (!expr.isInitialized() && (opString == "" || opString == "" || opString == "<" || opString == ">" || opString == "=" || opString == "")) {
  487. assert(bound == boost::none);
  488. storm::logic::ComparisonType ct;
  489. if(opString == "") {
  490. ct = storm::logic::ComparisonType::GreaterEqual;
  491. } else if (opString == "") {
  492. ct = storm::logic::ComparisonType::LessEqual;
  493. } else if (opString == "<") {
  494. ct = storm::logic::ComparisonType::Less;
  495. } else if (opString == ">") {
  496. ct = storm::logic::ComparisonType::Greater;
  497. }
  498. std::vector<std::string> const leftRight = {"left", "right"};
  499. for (uint64_t i = 0; i < 2; ++i) {
  500. if (propertyStructure.at(leftRight[i]).count("op") > 0) {
  501. std::string propertyOperatorString = getString(propertyStructure.at(leftRight[i]).at("op"), "property-operator");
  502. std::set<std::string> const propertyOperatorStrings = {"Pmin", "Pmax","Emin", "Emax", "Smin", "Smax"};
  503. if (propertyOperatorStrings.count(propertyOperatorString) > 0) {
  504. auto boundExpr = parseExpression(propertyStructure.at(leftRight[1-i]), scope.refine("Threshold for operator " + propertyStructure.at(leftRight[i]).at("op").get<std::string>()));
  505. if ((opString == "=" || opString == "")) {
  506. STORM_LOG_THROW(!boundExpr.containsVariables(), storm::exceptions::NotSupportedException, "Comparison operators '=' or '≠' in property specifications are currently not supported.");
  507. auto boundValue = boundExpr.evaluateAsRational();
  508. if (storm::utility::isZero(boundValue)) {
  509. if (opString == "=") {
  510. ct = storm::logic::ComparisonType::LessEqual;
  511. } else {
  512. ct = storm::logic::ComparisonType::Greater;
  513. }
  514. } else if (storm::utility::isOne(boundValue) && (propertyOperatorString == "Pmin" || propertyOperatorString == "Pmax")) {
  515. if (opString == "=") {
  516. ct = storm::logic::ComparisonType::GreaterEqual;
  517. } else {
  518. ct = storm::logic::ComparisonType::Less;
  519. }
  520. } else {
  521. STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Comparison operators '=' or '≠' in property specifications are currently not supported in " << scope.description << ".");
  522. }
  523. }
  524. return parseFormula(model, propertyStructure.at(leftRight[i]), formulaContext, scope, storm::logic::Bound(ct, boundExpr));
  525. }
  526. }
  527. }
  528. STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "No complex comparisons for properties are supported.");
  529. } else if (expr.isInitialized()) {
  530. STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException, "Non-trivial Expression '" << expr << "' contains a boolean transient variable. Can not translate to PRCTL-like formula at " << scope.description << ".");
  531. } else {
  532. STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException, "Unknown operator " << opString);
  533. }
  534. } else {
  535. STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException, "Looking for operator for formula " << propertyStructure.dump() << ", but did not find one");
  536. }
  537. }
  538. storm::jani::Property JaniParser::parseProperty(storm::jani::Model& model, json const& propertyStructure, Scope const& scope) {
  539. STORM_LOG_THROW(propertyStructure.count("name") == 1, storm::exceptions::InvalidJaniException, "Property must have a name");
  540. // TODO check unique name
  541. std::string name = getString(propertyStructure.at("name"), "property-name");
  542. STORM_LOG_TRACE("Parsing property named: " << name);
  543. std::string comment = "";
  544. if (propertyStructure.count("comment") > 0) {
  545. comment = getString(propertyStructure.at("comment"), "comment for property named '" + name + "'.");
  546. }
  547. STORM_LOG_THROW(propertyStructure.count("expression") == 1, storm::exceptions::InvalidJaniException, "Property must have an expression");
  548. // Parse filter expression.
  549. json const& expressionStructure = propertyStructure.at("expression");
  550. STORM_LOG_THROW(expressionStructure.count("op") == 1, storm::exceptions::InvalidJaniException, "Expression in property must have an operation description");
  551. STORM_LOG_THROW(expressionStructure.at("op") == "filter", storm::exceptions::InvalidJaniException, "Top level operation of a property must be a filter");
  552. STORM_LOG_THROW(expressionStructure.count("fun") == 1, storm::exceptions::InvalidJaniException, "Filter must have a function descritpion");
  553. std::string funDescr = getString(expressionStructure.at("fun"), "Filter function in property named " + name);
  554. storm::modelchecker::FilterType ft;
  555. if (funDescr == "min") {
  556. ft = storm::modelchecker::FilterType::MIN;
  557. } else if (funDescr == "max") {
  558. ft = storm::modelchecker::FilterType::MAX;
  559. } else if (funDescr == "sum") {
  560. ft = storm::modelchecker::FilterType::SUM;
  561. } else if (funDescr == "avg") {
  562. ft = storm::modelchecker::FilterType::AVG;
  563. } else if (funDescr == "count") {
  564. ft = storm::modelchecker::FilterType::COUNT;
  565. } else if (funDescr == "") {
  566. ft = storm::modelchecker::FilterType::FORALL;
  567. } else if (funDescr == "") {
  568. ft = storm::modelchecker::FilterType::EXISTS;
  569. } else if (funDescr == "argmin") {
  570. ft = storm::modelchecker::FilterType::ARGMIN;
  571. } else if (funDescr == "argmax") {
  572. ft = storm::modelchecker::FilterType::ARGMAX;
  573. } else if (funDescr == "values") {
  574. ft = storm::modelchecker::FilterType::VALUES;
  575. } else {
  576. STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException, "Unknown filter description " << funDescr << " in property named " << name);
  577. }
  578. STORM_LOG_THROW(expressionStructure.count("states") == 1, storm::exceptions::InvalidJaniException, "Filter must have a states description");
  579. std::shared_ptr<storm::logic::Formula const> statesFormula;
  580. if (expressionStructure.at("states").count("op") > 0) {
  581. std::string statesDescr = getString(expressionStructure.at("states").at("op"), "Filtered states in property named " + name);
  582. if (statesDescr == "initial") {
  583. statesFormula = std::make_shared<storm::logic::AtomicLabelFormula>("init");
  584. }
  585. }
  586. if (!statesFormula) {
  587. try {
  588. // Try to parse the states as formula.
  589. statesFormula = parseFormula(model, expressionStructure.at("states"), storm::logic::FormulaContext::Undefined, scope.refine("Values of property " + name));
  590. } catch (storm::exceptions::NotSupportedException const& ex) {
  591. throw ex;
  592. } catch (storm::exceptions::NotImplementedException const& ex) {
  593. throw ex;
  594. }
  595. }
  596. STORM_LOG_THROW(statesFormula, storm::exceptions::NotImplementedException, "Could not derive states formula.");
  597. STORM_LOG_THROW(expressionStructure.count("values") == 1, storm::exceptions::InvalidJaniException, "Values as input for a filter must be given");
  598. auto formula = parseFormula(model, expressionStructure.at("values"), storm::logic::FormulaContext::Undefined, scope.refine("Values of property " + name));
  599. return storm::jani::Property(name, storm::jani::FilterExpression(formula, ft, statesFormula), {}, comment);
  600. }
  601. std::shared_ptr<storm::jani::Constant> JaniParser::parseConstant(json const& constantStructure, Scope const& scope) {
  602. STORM_LOG_THROW(constantStructure.count("name") == 1, storm::exceptions::InvalidJaniException, "Variable (scope: " + scope.description + ") must have a name");
  603. std::string name = getString(constantStructure.at("name"), "variable-name in " + scope.description + "-scope");
  604. // TODO check existance of name.
  605. // TODO store prefix in variable.
  606. std::string exprManagerName = name;
  607. size_t valueCount = constantStructure.count("value");
  608. storm::expressions::Expression definingExpression;
  609. STORM_LOG_THROW(valueCount < 2, storm::exceptions::InvalidJaniException, "Value for constant '" + name + "' (scope: " + scope.description + ") must be given at most once.");
  610. if (valueCount == 1) {
  611. // Read initial value before; that makes creation later on a bit easier, and has as an additional benefit that we do not need to check whether the variable occurs also on the assignment.
  612. definingExpression = parseExpression(constantStructure.at("value"), scope.refine("Value of constant " + name));
  613. assert(definingExpression.isInitialized());
  614. }
  615. STORM_LOG_THROW(constantStructure.count("type") == 1, storm::exceptions::InvalidJaniException, "Constant '" + name + "' (scope: " + scope.description + ") must have a (single) type-declaration.");
  616. ParsedType type;
  617. parseType(type, constantStructure.at("type"), name, scope);
  618. STORM_LOG_THROW(type.basicType.is_initialized(), storm::exceptions::InvalidJaniException, "Constant '" + name + "' (scope: " + scope.description + ") has unexpected type");
  619. storm::expressions::Variable var;
  620. switch (type.basicType.get()) {
  621. case ParsedType::BasicType::Bool:
  622. var = expressionManager->declareBooleanVariable(exprManagerName);
  623. break;
  624. case ParsedType::BasicType::Int:
  625. var = expressionManager->declareIntegerVariable(exprManagerName);
  626. break;
  627. case ParsedType::BasicType::Real:
  628. var = expressionManager->declareRationalVariable(exprManagerName);
  629. break;
  630. }
  631. storm::expressions::Expression constraintExpression;
  632. if (type.bounds) {
  633. if (type.bounds->first.isInitialized()) {
  634. constraintExpression = var.getExpression() >= type.bounds->first;
  635. if (type.bounds->second.isInitialized()) {
  636. constraintExpression = constraintExpression && (var.getExpression() <= type.bounds->second);
  637. }
  638. } else if (type.bounds->second.isInitialized()) {
  639. constraintExpression = var.getExpression() <= type.bounds->second;
  640. }
  641. }
  642. return std::make_shared<storm::jani::Constant>(name, std::move(var), definingExpression, constraintExpression);
  643. }
  644. void JaniParser::parseType(ParsedType& result, json const& typeStructure, std::string variableName, Scope const& scope) {
  645. if (typeStructure.is_string()) {
  646. if (typeStructure == "real") {
  647. result.basicType = ParsedType::BasicType::Real;
  648. result.expressionType = expressionManager->getRationalType();
  649. } else if (typeStructure == "bool") {
  650. result.basicType = ParsedType::BasicType::Bool;
  651. result.expressionType = expressionManager->getBooleanType();
  652. } else if (typeStructure == "int") {
  653. result.basicType = ParsedType::BasicType::Int;
  654. result.expressionType = expressionManager->getIntegerType();
  655. } else if(typeStructure == "clock") {
  656. STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException, "Unsupported type 'clock' for variable '" << variableName << "' (scope: " << scope.description << ")");
  657. } else if(typeStructure == "continuous") {
  658. STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException, "Unsupported type 'continuous' for variable ''" << variableName << "' (scope: " << scope.description << ")");
  659. } else {
  660. STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException, "Unsupported type " << typeStructure.dump() << " for variable '" << variableName << "' (scope: " << scope.description << ")");
  661. }
  662. } else if (typeStructure.is_object()) {
  663. STORM_LOG_THROW(typeStructure.count("kind") == 1, storm::exceptions::InvalidJaniException, "For complex type as in variable " << variableName << "(scope: " << scope.description << ") kind must be given");
  664. std::string kind = getString(typeStructure.at("kind"), "kind for complex type as in variable " + variableName + "(scope: " + scope.description + ") ");
  665. if (kind == "bounded") {
  666. STORM_LOG_THROW(typeStructure.count("lower-bound") + typeStructure.count("upper-bound") > 0, storm::exceptions::InvalidJaniException, "For bounded type as in variable " << variableName << "(scope: " << scope.description << ") lower-bound or upper-bound must be given");
  667. storm::expressions::Expression lowerboundExpr;
  668. if (typeStructure.count("lower-bound") > 0) {
  669. lowerboundExpr = parseExpression(typeStructure.at("lower-bound"), scope.refine("Lower bound for variable " + variableName));
  670. }
  671. storm::expressions::Expression upperboundExpr;
  672. if (typeStructure.count("upper-bound") > 0) {
  673. upperboundExpr = parseExpression(typeStructure.at("upper-bound"), scope.refine("Upper bound for variable "+ variableName));
  674. }
  675. STORM_LOG_THROW(typeStructure.count("base") == 1, storm::exceptions::InvalidJaniException, "For bounded type as in variable " << variableName << "(scope: " << scope.description << ") base must be given");
  676. std::string basictype = getString(typeStructure.at("base"), "base for bounded type as in variable " + variableName + "(scope: " + scope.description + ") ");
  677. if (basictype == "int") {
  678. STORM_LOG_THROW(!lowerboundExpr.isInitialized() || lowerboundExpr.hasIntegerType(), storm::exceptions::InvalidJaniException, "Lower bound for bounded integer variable " << variableName << "(scope: " << scope.description << ") must be integer-typed");
  679. STORM_LOG_THROW(!upperboundExpr.isInitialized() || upperboundExpr.hasIntegerType(), storm::exceptions::InvalidJaniException, "Upper bound for bounded integer variable " << variableName << "(scope: " << scope.description << ") must be integer-typed");
  680. if (lowerboundExpr.isInitialized() && upperboundExpr.isInitialized() && !lowerboundExpr.containsVariables() && !upperboundExpr.containsVariables()) {
  681. STORM_LOG_THROW(lowerboundExpr.evaluateAsInt() <= upperboundExpr.evaluateAsInt(), storm::exceptions::InvalidJaniException, "Lower bound must not be larger than upper bound for bounded integer variable " << variableName << "(scope: " << scope.description << ")");
  682. }
  683. result.basicType = ParsedType::BasicType::Int;
  684. result.expressionType = expressionManager->getIntegerType();
  685. result.bounds = std::make_pair(lowerboundExpr, upperboundExpr);
  686. } else if (basictype == "real") {
  687. STORM_LOG_THROW(!lowerboundExpr.isInitialized() || lowerboundExpr.hasNumericalType(), storm::exceptions::InvalidJaniException, "Lower bound for bounded real variable " << variableName << "(scope: " << scope.description << ") must be numeric");
  688. STORM_LOG_THROW(!upperboundExpr.isInitialized() || upperboundExpr.hasNumericalType(), storm::exceptions::InvalidJaniException, "Upper bound for bounded real variable " << variableName << "(scope: " << scope.description << ") must be numeric");
  689. if (lowerboundExpr.isInitialized() && upperboundExpr.isInitialized() && !lowerboundExpr.containsVariables() && !upperboundExpr.containsVariables()) {
  690. STORM_LOG_THROW(lowerboundExpr.evaluateAsRational() <= upperboundExpr.evaluateAsRational(), storm::exceptions::InvalidJaniException, "Lower bound must not be larger than upper bound for bounded integer variable " << variableName << "(scope: " << scope.description << ")");
  691. }
  692. result.basicType = ParsedType::BasicType::Real;
  693. result.expressionType = expressionManager->getRationalType();
  694. result.bounds = std::make_pair(lowerboundExpr, upperboundExpr);
  695. } else {
  696. STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException, "Unsupported base " << basictype << " for bounded variable " << variableName << "(scope: " << scope.description << ") ");
  697. }
  698. } else if (kind == "array") {
  699. STORM_LOG_THROW(typeStructure.count("base") == 1, storm::exceptions::InvalidJaniException, "For array type as in variable " << variableName << "(scope: " << scope.description << ") base must be given");
  700. result.arrayBase = std::make_unique<ParsedType>();
  701. parseType(*result.arrayBase, typeStructure.at("base"), variableName, scope);
  702. result.expressionType = expressionManager->getArrayType(result.arrayBase->expressionType);
  703. } else {
  704. STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException, "Unsupported kind " << kind << " for complex type of variable " << variableName << "(scope: " << scope.description << ") ");
  705. }
  706. }
  707. }
  708. storm::jani::FunctionDefinition JaniParser::parseFunctionDefinition(json const& functionDefinitionStructure, Scope const& scope, bool firstPass, std::string const& parameterNamePrefix) {
  709. STORM_LOG_THROW(functionDefinitionStructure.count("name") == 1, storm::exceptions::InvalidJaniException, "Function definition (scope: " + scope.description + ") must have a name");
  710. std::string functionName = getString(functionDefinitionStructure.at("name"), "function-name in " + scope.description);
  711. STORM_LOG_THROW(functionDefinitionStructure.count("type") == 1, storm::exceptions::InvalidJaniException, "Function definition '" + functionName + "' (scope: " + scope.description + ") must have a (single) type-declaration.");
  712. ParsedType type;
  713. parseType(type, functionDefinitionStructure.at("type"), functionName, scope);
  714. std::unordered_map<std::string, storm::expressions::Variable> parameterNameToVariableMap;
  715. std::vector<storm::expressions::Variable> parameters;
  716. if (!firstPass && functionDefinitionStructure.count("parameters") > 0) {
  717. STORM_LOG_THROW(functionDefinitionStructure.count("parameters") == 1, storm::exceptions::InvalidJaniException, "Function definition '" + functionName + "' (scope: " + scope.description + ") must have exactly one list of parameters.");
  718. for (auto const& parameterStructure : functionDefinitionStructure.at("parameters")) {
  719. STORM_LOG_THROW(parameterStructure.count("name") == 1, storm::exceptions::InvalidJaniException, "Parameter declaration of parameter " + std::to_string(parameters.size()) + " of Function definition '" + functionName + "' (scope: " + scope.description + ") must have a name");
  720. std::string parameterName = getString(parameterStructure.at("name"), "parameter-name of parameter " + std::to_string(parameters.size()) + " of Function definition '" + functionName + "' (scope: " + scope.description + ")");
  721. ParsedType parameterType;
  722. STORM_LOG_THROW(parameterStructure.count("type") == 1, storm::exceptions::InvalidJaniException, "Parameter declaration of parameter " + std::to_string(parameters.size()) + " of Function definition '" + functionName + "' (scope: " + scope.description + ") must have exactly one type.");
  723. parseType(parameterType, parameterStructure.at("type"), parameterName, scope.refine("parameter declaration of parameter " + std::to_string(parameters.size()) + " of function definition " + functionName));
  724. STORM_LOG_WARN_COND(!parameterType.bounds.is_initialized(), "Bounds on parameter" + parameterName + " of function definition " + functionName + " will be ignored.");
  725. std::string exprParameterName = parameterNamePrefix + functionName + VARIABLE_AUTOMATON_DELIMITER + parameterName;
  726. parameters.push_back(expressionManager->declareVariable(exprParameterName, parameterType.expressionType));
  727. parameterNameToVariableMap.emplace(parameterName, parameters.back());
  728. }
  729. }
  730. STORM_LOG_THROW(functionDefinitionStructure.count("body") == 1, storm::exceptions::InvalidJaniException, "Function definition '" + functionName + "' (scope: " + scope.description + ") must have a (single) body.");
  731. storm::expressions::Expression functionBody;
  732. if (!firstPass) {
  733. functionBody = parseExpression(functionDefinitionStructure.at("body"), scope.refine("body of function definition " + functionName), false, parameterNameToVariableMap);
  734. STORM_LOG_WARN_COND(functionBody.getType() == type.expressionType, "Type of body of function " + functionName + "' (scope: " + scope.description + ") has type " << functionBody.getType() << " although the function type is given as " << type.expressionType);
  735. }
  736. return storm::jani::FunctionDefinition(functionName, type.expressionType, parameters, functionBody);
  737. }
  738. std::shared_ptr<storm::jani::Variable> JaniParser::parseVariable(json const& variableStructure, bool requireInitialValues, Scope const& scope, std::string const& namePrefix) {
  739. STORM_LOG_THROW(variableStructure.count("name") == 1, storm::exceptions::InvalidJaniException, "Variable (scope: " + scope.description + ") must have a name");
  740. std::string name = getString(variableStructure.at("name"), "variable-name in " + scope.description + "-scope");
  741. // TODO check existance of name.
  742. // TODO store prefix in variable.
  743. std::string exprManagerName = namePrefix + name;
  744. bool transientVar = defaultVariableTransient; // Default value for variables.
  745. size_t tvarcount = variableStructure.count("transient");
  746. STORM_LOG_THROW(tvarcount <= 1, storm::exceptions::InvalidJaniException, "Multiple definitions of transient not allowed in variable '" + name + "' (scope: " + scope.description + ") ");
  747. if(tvarcount == 1) {
  748. transientVar = getBoolean(variableStructure.at("transient"), "transient-attribute in variable '" + name + "' (scope: " + scope.description + ") ");
  749. }
  750. STORM_LOG_THROW(variableStructure.count("type") == 1, storm::exceptions::InvalidJaniException, "Variable '" + name + "' (scope: " + scope.description + ") must have a (single) type-declaration.");
  751. ParsedType type;
  752. parseType(type, variableStructure.at("type"), name, scope);
  753. size_t initvalcount = variableStructure.count("initial-value");
  754. if(transientVar) {
  755. STORM_LOG_THROW(initvalcount == 1, storm::exceptions::InvalidJaniException, "Initial value must be given once for transient variable '" + name + "' (scope: " + scope.description + ") "+ name + "' (scope: " + scope.description + ") ");
  756. } else {
  757. STORM_LOG_THROW(initvalcount <= 1, storm::exceptions::InvalidJaniException, "Initial value can be given at most one for variable " + name + "' (scope: " + scope.description + ")");
  758. }
  759. boost::optional<storm::expressions::Expression> initVal;
  760. if (initvalcount == 1 && !variableStructure.at("initial-value").is_null()) {
  761. initVal = parseExpression(variableStructure.at("initial-value"), scope.refine("Initial value for variable " + name));
  762. } else {
  763. assert(!transientVar);
  764. }
  765. bool setInitValFromDefault = !initVal.is_initialized() && requireInitialValues;
  766. if (type.basicType) {
  767. switch (type.basicType.get()) {
  768. case ParsedType::BasicType::Real:
  769. STORM_LOG_WARN_COND(!type.bounds.is_initialized(), "Bounds for rational variable " + name + "(scope " + scope.description + ") will be ignored.");
  770. if (setInitValFromDefault) {
  771. initVal = expressionManager->rational(defaultRationalInitialValue);
  772. }
  773. if (initVal) {
  774. STORM_LOG_THROW(initVal.get().hasRationalType() || initVal.get().hasIntegerType(), storm::exceptions::InvalidJaniException, "Initial value for rational variable " + name + "(scope " + scope.description + ") should be a rational");
  775. return std::make_shared<storm::jani::RealVariable>(name, expressionManager->declareRationalVariable(exprManagerName), initVal.get(), transientVar);
  776. } else {
  777. return std::make_shared<storm::jani::RealVariable>(name, expressionManager->declareRationalVariable(exprManagerName));
  778. }
  779. case ParsedType::BasicType::Int:
  780. if (setInitValFromDefault) {
  781. if (type.bounds) {
  782. storm::expressions::Expression takeDefaultCondition;
  783. if (type.bounds->first.isInitialized()) {
  784. takeDefaultCondition = type.bounds->first < defaultIntegerInitialValue;
  785. if (type.bounds->second.isInitialized()) {
  786. takeDefaultCondition = takeDefaultCondition && type.bounds->second >= defaultIntegerInitialValue;
  787. }
  788. } else {
  789. STORM_LOG_ASSERT(type.bounds->second.isInitialized(), "Expected to have either a lower or an upper bound");
  790. takeDefaultCondition = type.bounds->second >= defaultIntegerInitialValue;
  791. }
  792. initVal = storm::expressions::ite(takeDefaultCondition, expressionManager->integer(defaultIntegerInitialValue), type.bounds->first);
  793. } else {
  794. initVal = expressionManager->integer(defaultIntegerInitialValue);
  795. }
  796. }
  797. if (initVal) {
  798. STORM_LOG_THROW(initVal.get().hasIntegerType(), storm::exceptions::InvalidJaniException, "Initial value for integer variable " + name + "(scope " + scope.description + ") should be an integer");
  799. if (type.bounds) {
  800. return storm::jani::makeBoundedIntegerVariable(name, expressionManager->declareIntegerVariable(exprManagerName), initVal, transientVar, type.bounds->first, type.bounds->second);
  801. } else {
  802. return std::make_shared<storm::jani::UnboundedIntegerVariable>(name, expressionManager->declareIntegerVariable(exprManagerName), initVal.get(), transientVar);
  803. }
  804. } else {
  805. if (type.bounds) {
  806. return storm::jani::makeBoundedIntegerVariable(name, expressionManager->declareIntegerVariable(exprManagerName), boost::none, false, type.bounds->first, type.bounds->second);
  807. } else {
  808. return std::make_shared<storm::jani::UnboundedIntegerVariable>(name, expressionManager->declareIntegerVariable(exprManagerName));
  809. }
  810. }
  811. break;
  812. case ParsedType::BasicType::Bool:
  813. if (setInitValFromDefault) {
  814. initVal = expressionManager->boolean(defaultBooleanInitialValue);
  815. }
  816. if (initVal) {
  817. STORM_LOG_THROW(initVal.get().hasBooleanType(), storm::exceptions::InvalidJaniException, "Initial value for boolean variable " + name + "(scope " + scope.description + ") should be a Boolean");
  818. if (transientVar) {
  819. labels.insert(name);
  820. }
  821. return std::make_shared<storm::jani::BooleanVariable>(name, expressionManager->declareBooleanVariable(exprManagerName), initVal.get(), transientVar);
  822. } else {
  823. return std::make_shared<storm::jani::BooleanVariable>(name, expressionManager->declareBooleanVariable(exprManagerName));
  824. }
  825. }
  826. } else if (type.arrayBase) {
  827. STORM_LOG_THROW(type.arrayBase->basicType, storm::exceptions::InvalidJaniException, "Array base type for variable " + name + "(scope " + scope.description + ") should be a BasicType or a BoundedType.");
  828. storm::jani::ArrayVariable::ElementType elementType;
  829. storm::expressions::Type exprVariableType = type.expressionType;
  830. switch (type.arrayBase->basicType.get()) {
  831. case ParsedType::BasicType::Real:
  832. elementType = storm::jani::ArrayVariable::ElementType::Real;
  833. break;
  834. case ParsedType::BasicType::Bool:
  835. elementType = storm::jani::ArrayVariable::ElementType::Bool;
  836. break;
  837. case ParsedType::BasicType::Int:
  838. elementType = storm::jani::ArrayVariable::ElementType::Int;
  839. break;
  840. default:
  841. STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Unsupported type");
  842. }
  843. if (setInitValFromDefault) {
  844. initVal = storm::expressions::ValueArrayExpression(*expressionManager, exprVariableType, {}).toExpression();
  845. }
  846. std::shared_ptr<storm::jani::ArrayVariable> result;
  847. if (initVal) {
  848. STORM_LOG_THROW(initVal->getType().isArrayType(), storm::exceptions::InvalidJaniException, "Initial value for array variable " + name + "(scope " + scope.description + ") should be an Array");
  849. result = std::make_shared<storm::jani::ArrayVariable>(name, expressionManager->declareArrayVariable(exprManagerName, exprVariableType.getElementType()), elementType, initVal.get(), transientVar);
  850. } else {
  851. result = std::make_shared<storm::jani::ArrayVariable>(name, expressionManager->declareArrayVariable(exprManagerName, exprVariableType.getElementType()), elementType);
  852. }
  853. if (type.arrayBase->bounds) {
  854. auto const& bounds = type.arrayBase->bounds.get();
  855. if (bounds.first.isInitialized()) {
  856. result->setLowerElementTypeBound(bounds.first);
  857. }
  858. if (bounds.second.isInitialized()) {
  859. result->setUpperElementTypeBound(bounds.second);
  860. }
  861. }
  862. return result;
  863. }
  864. STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException, "Unknown type description, " << variableStructure.at("type").dump() << " for variable '" << name << "' (scope: " << scope.description << ")");
  865. }
  866. /**
  867. * Helper for parse expression.
  868. */
  869. void ensureNumberOfArguments(uint64_t expected, uint64_t actual, std::string const& opstring, std::string const& errorInfo) {
  870. STORM_LOG_THROW(expected == actual, storm::exceptions::InvalidJaniException, "Operator " << opstring << " expects " << expected << " arguments, but got " << actual << " in " << errorInfo << ".");
  871. }
  872. std::vector<storm::expressions::Expression> JaniParser::parseUnaryExpressionArguments(json const& expressionDecl, std::string const& opstring, Scope const& scope, bool returnNoneInitializedOnUnknownOperator, std::unordered_map<std::string, storm::expressions::Variable> const& auxiliaryVariables) {
  873. storm::expressions::Expression left = parseExpression(expressionDecl.at("exp"), scope.refine("Argument of operator " + opstring), returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
  874. return {left};
  875. }
  876. std::vector<storm::expressions::Expression> JaniParser::parseBinaryExpressionArguments(json const& expressionDecl, std::string const& opstring, Scope const& scope, bool returnNoneInitializedOnUnknownOperator, std::unordered_map<std::string, storm::expressions::Variable> const& auxiliaryVariables) {
  877. storm::expressions::Expression left = parseExpression(expressionDecl.at("left"), scope.refine("Left argument of operator " + opstring), returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
  878. storm::expressions::Expression right = parseExpression(expressionDecl.at("right"), scope.refine("Right argument of operator " + opstring), returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
  879. return {left, right};
  880. }
  881. /**
  882. * Helper for parse expression.
  883. */
  884. void ensureBooleanType(storm::expressions::Expression const& expr, std::string const& opstring, unsigned argNr, std::string const& errorInfo) {
  885. STORM_LOG_THROW(expr.hasBooleanType(), storm::exceptions::InvalidJaniException, "Operator " << opstring << " expects argument[" << argNr << "]: '" << expr << "' to be Boolean in " << errorInfo << ".");
  886. }
  887. /**
  888. * Helper for parse expression.
  889. */
  890. void ensureNumericalType(storm::expressions::Expression const& expr, std::string const& opstring, unsigned argNr, std::string const& errorInfo) {
  891. STORM_LOG_THROW(expr.hasNumericalType(), storm::exceptions::InvalidJaniException, "Operator " << opstring << " expects argument " + std::to_string(argNr) + " to be numerical in " << errorInfo << ".");
  892. }
  893. /**
  894. * Helper for parse expression.
  895. */
  896. void ensureIntegerType(storm::expressions::Expression const& expr, std::string const& opstring, unsigned argNr, std::string const& errorInfo) {
  897. STORM_LOG_THROW(expr.hasIntegerType(), storm::exceptions::InvalidJaniException, "Operator " << opstring << " expects argument " + std::to_string(argNr) + " to be numerical in " << errorInfo << ".");
  898. }
  899. /**
  900. * Helper for parse expression.
  901. */
  902. void ensureArrayType(storm::expressions::Expression const& expr, std::string const& opstring, unsigned argNr, std::string const& errorInfo) {
  903. STORM_LOG_THROW(expr.getType().isArrayType(), storm::exceptions::InvalidJaniException, "Operator " << opstring << " expects argument " + std::to_string(argNr) + " to be of type 'array' in " << errorInfo << ".");
  904. }
  905. storm::jani::LValue JaniParser::parseLValue(json const& lValueStructure, Scope const& scope) {
  906. if (lValueStructure.is_string()) {
  907. std::string ident = getString(lValueStructure, scope.description);
  908. if (scope.localVars != nullptr) {
  909. auto localVar = scope.localVars->find(ident);
  910. if (localVar != scope.localVars->end()) {
  911. return storm::jani::LValue(*localVar->second);
  912. }
  913. }
  914. STORM_LOG_THROW(scope.globalVars != nullptr, storm::exceptions::InvalidJaniException, "Unknown identifier '" << ident << "' occurs in " << scope.description);
  915. auto globalVar = scope.globalVars->find(ident);
  916. STORM_LOG_THROW(globalVar != scope.globalVars->end(), storm::exceptions::InvalidJaniException, "Unknown identifier '" << ident << "' occurs in " << scope.description);
  917. return storm::jani::LValue(*globalVar->second);
  918. } else if (lValueStructure.count("op") == 1) {
  919. std::string opstring = getString(lValueStructure.at("op"), scope.description);
  920. STORM_LOG_THROW(opstring == "aa", storm::exceptions::InvalidJaniException, "Unknown operation '" << opstring << "' occurs in " << scope.description);
  921. STORM_LOG_THROW(lValueStructure.count("exp"), storm::exceptions::InvalidJaniException, "Missing 'exp' in array access at " << scope.description);
  922. storm::jani::LValue exp = parseLValue(lValueStructure.at("exp"), scope.refine("LValue description of array expression"));
  923. STORM_LOG_THROW(lValueStructure.count("index"), storm::exceptions::InvalidJaniException, "Missing 'index' in array access at " << scope.description);
  924. storm::expressions::Expression index = parseExpression(lValueStructure.at("index"), scope.refine("Index expression of array access"));
  925. return storm::jani::LValue(exp, index);
  926. } else {
  927. STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException, "Unknown LValue '" << lValueStructure.dump() << "' occurs in " << scope.description);
  928. // Silly warning suppression.
  929. return storm::jani::LValue(*scope.globalVars->end()->second);
  930. }
  931. }
  932. storm::expressions::Variable JaniParser::getVariableOrConstantExpression(std::string const& ident, Scope const& scope, std::unordered_map<std::string, storm::expressions::Variable> const& auxiliaryVariables) {
  933. {
  934. auto it = auxiliaryVariables.find(ident);
  935. if (it != auxiliaryVariables.end()) {
  936. return it->second;
  937. }
  938. }
  939. if (scope.localVars != nullptr) {
  940. auto it = scope.localVars->find(ident);
  941. if (it != scope.localVars->end()) {
  942. return it->second->getExpressionVariable();
  943. }
  944. }
  945. if (scope.globalVars != nullptr) {
  946. auto it = scope.globalVars->find(ident);
  947. if (it != scope.globalVars->end()) {
  948. return it->second->getExpressionVariable();
  949. }
  950. }
  951. if (scope.constants != nullptr) {
  952. auto it = scope.constants->find(ident);
  953. if (it != scope.constants->end()) {
  954. return it->second->getExpressionVariable();
  955. }
  956. }
  957. STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException, "Unknown identifier '" << ident << "' occurs in " << scope.description);
  958. // Silly warning suppression.
  959. return storm::expressions::Variable();
  960. }
  961. storm::expressions::Expression JaniParser::parseExpression(json const& expressionStructure, Scope const& scope, bool returnNoneInitializedOnUnknownOperator, std::unordered_map<std::string, storm::expressions::Variable> const& auxiliaryVariables) {
  962. if (expressionStructure.is_boolean()) {
  963. if (expressionStructure.get<bool>()) {
  964. return expressionManager->boolean(true);
  965. } else {
  966. return expressionManager->boolean(false);
  967. }
  968. } else if (expressionStructure.is_number_integer()) {
  969. return expressionManager->integer(expressionStructure.get<int64_t>());
  970. } else if (expressionStructure.is_number_float()) {
  971. return expressionManager->rational(storm::utility::convertNumber<storm::RationalNumber>(expressionStructure.dump()));
  972. } else if (expressionStructure.is_string()) {
  973. std::string ident = expressionStructure.get<std::string>();
  974. return storm::expressions::Expression(getVariableOrConstantExpression(ident, scope, auxiliaryVariables));
  975. } else if (expressionStructure.is_object()) {
  976. if (expressionStructure.count("distribution") == 1) {
  977. STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException, "Distributions are not supported by storm expressions, cannot import " << expressionStructure.dump() << " in " << scope.description << ".");
  978. }
  979. if (expressionStructure.count("op") == 1) {
  980. std::string opstring = getString(expressionStructure.at("op"), scope.description);
  981. std::vector<storm::expressions::Expression> arguments = {};
  982. if(opstring == "ite") {
  983. STORM_LOG_THROW(expressionStructure.count("if") == 1, storm::exceptions::InvalidJaniException, "If operator required");
  984. STORM_LOG_THROW(expressionStructure.count("else") == 1, storm::exceptions::InvalidJaniException, "Else operator required");
  985. STORM_LOG_THROW(expressionStructure.count("then") == 1, storm::exceptions::InvalidJaniException, "Then operator required");
  986. arguments.push_back(parseExpression(expressionStructure.at("if"), scope.refine("if-formula"), returnNoneInitializedOnUnknownOperator, auxiliaryVariables));
  987. arguments.push_back(parseExpression(expressionStructure.at("then"), scope.refine("then-formula"), returnNoneInitializedOnUnknownOperator, auxiliaryVariables));
  988. arguments.push_back(parseExpression(expressionStructure.at("else"), scope.refine("else-formula"), returnNoneInitializedOnUnknownOperator, auxiliaryVariables));
  989. ensureNumberOfArguments(3, arguments.size(), opstring, scope.description);
  990. assert(arguments.size() == 3);
  991. ensureBooleanType(arguments[0], opstring, 0, scope.description);
  992. return storm::expressions::ite(arguments[0], arguments[1], arguments[2]);
  993. } else if (opstring == "") {
  994. arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
  995. assert(arguments.size() == 2);
  996. if(!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
  997. return storm::expressions::Expression();
  998. }
  999. ensureBooleanType(arguments[0], opstring, 0, scope.description);
  1000. ensureBooleanType(arguments[1], opstring, 1, scope.description);
  1001. return arguments[0] || arguments[1];
  1002. } else if (opstring == "") {
  1003. arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
  1004. assert(arguments.size() == 2);
  1005. if(!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
  1006. return storm::expressions::Expression();
  1007. }
  1008. ensureBooleanType(arguments[0], opstring, 0, scope.description);
  1009. ensureBooleanType(arguments[1], opstring, 1, scope.description);
  1010. return arguments[0] && arguments[1];
  1011. } else if (opstring == "") {
  1012. arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
  1013. assert(arguments.size() == 2);
  1014. if(!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
  1015. return storm::expressions::Expression();
  1016. }
  1017. ensureBooleanType(arguments[0], opstring, 0, scope.description);
  1018. ensureBooleanType(arguments[1], opstring, 1, scope.description);
  1019. return (!arguments[0]) || arguments[1];
  1020. } else if (opstring == "¬") {
  1021. arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
  1022. assert(arguments.size() == 1);
  1023. if(!arguments[0].isInitialized()) {
  1024. return storm::expressions::Expression();
  1025. }
  1026. ensureBooleanType(arguments[0], opstring, 0, scope.description);
  1027. return !arguments[0];
  1028. } else if (opstring == "=") {
  1029. arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
  1030. assert(arguments.size() == 2);
  1031. if(!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
  1032. return storm::expressions::Expression();
  1033. }
  1034. if(arguments[0].hasBooleanType()) {
  1035. ensureBooleanType(arguments[1], opstring, 1, scope.description);
  1036. return storm::expressions::iff(arguments[0], arguments[1]);
  1037. } else {
  1038. ensureNumericalType(arguments[1], opstring, 1, scope.description);
  1039. return arguments[0] == arguments[1];
  1040. }
  1041. } else if (opstring == "") {
  1042. arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
  1043. assert(arguments.size() == 2);
  1044. if(!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
  1045. return storm::expressions::Expression();
  1046. }
  1047. if(arguments[0].hasBooleanType()) {
  1048. ensureBooleanType(arguments[1], opstring, 1, scope.description);
  1049. return storm::expressions::xclusiveor(arguments[0], arguments[1]);
  1050. } else {
  1051. ensureNumericalType(arguments[1], opstring, 1, scope.description);
  1052. return arguments[0] != arguments[1];
  1053. }
  1054. } else if (opstring == "<") {
  1055. arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
  1056. assert(arguments.size() == 2);
  1057. if(!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
  1058. return storm::expressions::Expression();
  1059. }
  1060. ensureNumericalType(arguments[0], opstring, 0, scope.description);
  1061. ensureNumericalType(arguments[1], opstring, 1, scope.description);
  1062. return arguments[0] < arguments[1];
  1063. } else if (opstring == "") {
  1064. arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
  1065. assert(arguments.size() == 2);
  1066. if(!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
  1067. return storm::expressions::Expression();
  1068. }
  1069. ensureNumericalType(arguments[0], opstring, 0, scope.description);
  1070. ensureNumericalType(arguments[1], opstring, 1, scope.description);
  1071. return arguments[0] <= arguments[1];
  1072. } else if (opstring == ">") {
  1073. arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
  1074. assert(arguments.size() == 2);
  1075. if(!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
  1076. return storm::expressions::Expression();
  1077. }
  1078. ensureNumericalType(arguments[0], opstring, 0, scope.description);
  1079. ensureNumericalType(arguments[1], opstring, 1, scope.description);
  1080. return arguments[0] > arguments[1];
  1081. } else if (opstring == "") {
  1082. arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
  1083. assert(arguments.size() == 2);
  1084. if(!arguments[0].isInitialized() || !arguments[1].isInitialized()) {
  1085. return storm::expressions::Expression();
  1086. }
  1087. ensureNumericalType(arguments[0], opstring, 0, scope.description);
  1088. ensureNumericalType(arguments[1], opstring, 1, scope.description);
  1089. return arguments[0] >= arguments[1];
  1090. } else if (opstring == "+") {
  1091. arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
  1092. assert(arguments.size() == 2);
  1093. ensureNumericalType(arguments[0], opstring, 0, scope.description);
  1094. ensureNumericalType(arguments[1], opstring, 1, scope.description);
  1095. return arguments[0] + arguments[1];
  1096. } else if (opstring == "-" && expressionStructure.count("left") > 0) {
  1097. arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
  1098. assert(arguments.size() == 2);
  1099. ensureNumericalType(arguments[0], opstring, 0, scope.description);
  1100. ensureNumericalType(arguments[1], opstring, 1, scope.description);
  1101. return arguments[0] - arguments[1];
  1102. } else if (opstring == "-") {
  1103. arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
  1104. assert(arguments.size() == 1);
  1105. ensureNumericalType(arguments[0], opstring, 0, scope.description);
  1106. return -arguments[0];
  1107. } else if (opstring == "*") {
  1108. arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
  1109. assert(arguments.size() == 2);
  1110. ensureNumericalType(arguments[0], opstring, 0, scope.description);
  1111. ensureNumericalType(arguments[1], opstring, 1, scope.description);
  1112. return arguments[0] * arguments[1];
  1113. } else if (opstring == "/") {
  1114. arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
  1115. assert(arguments.size() == 2);
  1116. ensureNumericalType(arguments[0], opstring, 0, scope.description);
  1117. ensureNumericalType(arguments[1], opstring, 1, scope.description);
  1118. return arguments[0] / arguments[1];
  1119. } else if (opstring == "%") {
  1120. arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
  1121. assert(arguments.size() == 2);
  1122. ensureNumericalType(arguments[0], opstring, 0, scope.description);
  1123. ensureNumericalType(arguments[1], opstring, 1, scope.description);
  1124. return arguments[0] % arguments[1];
  1125. } else if (opstring == "max") {
  1126. arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
  1127. assert(arguments.size() == 2);
  1128. ensureNumericalType(arguments[0], opstring, 0, scope.description);
  1129. ensureNumericalType(arguments[1], opstring, 1, scope.description);
  1130. return storm::expressions::maximum(arguments[0],arguments[1]);
  1131. } else if (opstring == "min") {
  1132. arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
  1133. assert(arguments.size() == 2);
  1134. ensureNumericalType(arguments[0], opstring, 0, scope.description);
  1135. ensureNumericalType(arguments[1], opstring, 1, scope.description);
  1136. return storm::expressions::minimum(arguments[0],arguments[1]);
  1137. } else if (opstring == "floor") {
  1138. arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
  1139. assert(arguments.size() == 1);
  1140. ensureNumericalType(arguments[0], opstring, 0, scope.description);
  1141. return storm::expressions::floor(arguments[0]);
  1142. } else if (opstring == "ceil") {
  1143. arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
  1144. assert(arguments.size() == 1);
  1145. ensureNumericalType(arguments[0], opstring, 0, scope.description);
  1146. return storm::expressions::ceil(arguments[0]);
  1147. } else if (opstring == "abs") {
  1148. arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
  1149. assert(arguments.size() == 1);
  1150. ensureNumericalType(arguments[0], opstring, 0, scope.description);
  1151. return storm::expressions::abs(arguments[0]);
  1152. } else if (opstring == "sgn") {
  1153. arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
  1154. assert(arguments.size() == 1);
  1155. ensureNumericalType(arguments[0], opstring, 0, scope.description);
  1156. return storm::expressions::sign(arguments[0]);
  1157. } else if (opstring == "trc") {
  1158. arguments = parseUnaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
  1159. assert(arguments.size() == 1);
  1160. ensureNumericalType(arguments[0], opstring, 0, scope.description);
  1161. return storm::expressions::truncate(arguments[0]);
  1162. } else if (opstring == "pow") {
  1163. arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
  1164. assert(arguments.size() == 2);
  1165. ensureNumericalType(arguments[0], opstring, 0, scope.description);
  1166. ensureNumericalType(arguments[1], opstring, 1, scope.description);
  1167. return arguments[0]^arguments[1];
  1168. } else if (opstring == "exp") {
  1169. arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
  1170. assert(arguments.size() == 2);
  1171. ensureNumericalType(arguments[0], opstring, 0, scope.description);
  1172. ensureNumericalType(arguments[1], opstring, 1, scope.description);
  1173. // TODO implement
  1174. STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "exp operation is not yet implemented");
  1175. } else if (opstring == "log") {
  1176. arguments = parseBinaryExpressionArguments(expressionStructure, opstring, scope, returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
  1177. assert(arguments.size() == 2);
  1178. ensureNumericalType(arguments[0], opstring, 0, scope.description);
  1179. ensureNumericalType(arguments[1], opstring, 1, scope.description);
  1180. // TODO implement
  1181. STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "log operation is not yet implemented");
  1182. } else if (opstring == "aa") {
  1183. STORM_LOG_THROW(expressionStructure.count("exp") == 1, storm::exceptions::InvalidJaniException, "Array access operator requires exactly one exp (at " + scope.description + ").");
  1184. storm::expressions::Expression exp = parseExpression(expressionStructure.at("exp"), scope.refine("'exp' of array access operator"), returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
  1185. STORM_LOG_THROW(expressionStructure.count("index") == 1, storm::exceptions::InvalidJaniException, "Array access operator requires exactly one index (at " + scope.description + ").");
  1186. storm::expressions::Expression index = parseExpression(expressionStructure.at("index"), scope.refine("index of array access operator"), returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
  1187. ensureArrayType(exp, opstring, 0, scope.description);
  1188. ensureIntegerType(index, opstring, 1, scope.description);
  1189. return std::make_shared<storm::expressions::ArrayAccessExpression>(exp.getManager(), exp.getType().getElementType(), exp.getBaseExpressionPointer(), index.getBaseExpressionPointer())->toExpression();
  1190. } else if (opstring == "av") {
  1191. STORM_LOG_THROW(expressionStructure.count("elements") == 1, storm::exceptions::InvalidJaniException, "Array value operator requires exactly one 'elements' (at " + scope.description + ").");
  1192. std::vector<std::shared_ptr<storm::expressions::BaseExpression const>> elements;
  1193. storm::expressions::Type commonType;
  1194. bool first = true;
  1195. for (auto const& element : expressionStructure.at("elements")) {
  1196. elements.push_back(parseExpression(element, scope.refine("element " + std::to_string(elements.size()) + " of array value expression"), returnNoneInitializedOnUnknownOperator, auxiliaryVariables).getBaseExpressionPointer());
  1197. if (first) {
  1198. commonType = elements.back()->getType();
  1199. first = false;
  1200. } else if (!(commonType == elements.back()->getType())) {
  1201. if (commonType.isIntegerType() && elements.back()->getType().isRationalType()) {
  1202. commonType = elements.back()->getType();
  1203. } else {
  1204. STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException, "Incompatible element types " << commonType << " and " << elements.back()->getType() << " of array value expression at " << scope.description);
  1205. }
  1206. }
  1207. }
  1208. return std::make_shared<storm::expressions::ValueArrayExpression>(*expressionManager, expressionManager->getArrayType(commonType), elements)->toExpression();
  1209. } else if (opstring == "ac") {
  1210. STORM_LOG_THROW(expressionStructure.count("length") == 1, storm::exceptions::InvalidJaniException, "Array access operator requires exactly one length (at " + scope.description + ").");
  1211. storm::expressions::Expression length = parseExpression(expressionStructure.at("length"), scope.refine("index of array constructor expression"), returnNoneInitializedOnUnknownOperator, auxiliaryVariables);
  1212. ensureIntegerType(length, opstring, 1, scope.description);
  1213. STORM_LOG_THROW(expressionStructure.count("var") == 1, storm::exceptions::InvalidJaniException, "Array access operator requires exactly one var (at " + scope.description + ").");
  1214. std::string indexVarName = getString(expressionStructure.at("var"), "Field 'var' of Array access operator (at " + scope.description + ").");
  1215. STORM_LOG_THROW(auxiliaryVariables.find(indexVarName) == auxiliaryVariables.end(), storm::exceptions::InvalidJaniException, "Index variable " << indexVarName << " is already defined as an auxiliary variable (at " + scope.description + ").");
  1216. auto newAuxVars = auxiliaryVariables;
  1217. storm::expressions::Variable indexVar = expressionManager->declareFreshIntegerVariable(false, "ac_" + indexVarName);
  1218. newAuxVars.emplace(indexVarName, indexVar);
  1219. STORM_LOG_THROW(expressionStructure.count("exp") == 1, storm::exceptions::InvalidJaniException, "Array constructor operator requires exactly one exp (at " + scope.description + ").");
  1220. storm::expressions::Expression exp = parseExpression(expressionStructure.at("exp"), scope.refine("exp of array constructor"), returnNoneInitializedOnUnknownOperator, newAuxVars);
  1221. return std::make_shared<storm::expressions::ConstructorArrayExpression>(*expressionManager, expressionManager->getArrayType(exp.getType()), length.getBaseExpressionPointer(), indexVar, exp.getBaseExpressionPointer())->toExpression();
  1222. } else if (opstring == "call") {
  1223. STORM_LOG_THROW(expressionStructure.count("function") == 1, storm::exceptions::InvalidJaniException, "Function call operator requires exactly one function (at " + scope.description + ").");
  1224. std::string functionName = getString(expressionStructure.at("function"), "in function call operator (at " + scope.description + ").");
  1225. storm::jani::FunctionDefinition const* functionDefinition;
  1226. if (scope.localFunctions != nullptr && scope.localFunctions->count(functionName) > 0) {
  1227. functionDefinition = scope.localFunctions->at(functionName);
  1228. } else if (scope.globalFunctions != nullptr && scope.globalFunctions->count(functionName) > 0){
  1229. functionDefinition = scope.globalFunctions->at(functionName);
  1230. } else {
  1231. STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException, "Function call operator calls unknown function '" + functionName + "' (at " + scope.description + ").");
  1232. }
  1233. STORM_LOG_THROW(expressionStructure.count("args") == 1, storm::exceptions::InvalidJaniException, "Function call operator requires exactly one args (at " + scope.description + ").");
  1234. std::vector<std::shared_ptr<storm::expressions::BaseExpression const>> args;
  1235. if (expressionStructure.count("args") > 0) {
  1236. STORM_LOG_THROW(expressionStructure.count("args") == 1, storm::exceptions::InvalidJaniException, "Function call operator requires exactly one args (at " + scope.description + ").");
  1237. for (auto const& arg : expressionStructure.at("args")) {
  1238. args.push_back(parseExpression(arg, scope.refine("argument " + std::to_string(args.size()) + " of function call expression"), returnNoneInitializedOnUnknownOperator, auxiliaryVariables).getBaseExpressionPointer());
  1239. }
  1240. }
  1241. return std::make_shared<storm::expressions::FunctionCallExpression>(*expressionManager, functionDefinition->getType(), functionName, args)->toExpression();
  1242. } else if (unsupportedOpstrings.count(opstring) > 0) {
  1243. STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException, "Opstring " + opstring + " is not supported by storm");
  1244. } else {
  1245. if(returnNoneInitializedOnUnknownOperator) {
  1246. return storm::expressions::Expression();
  1247. }
  1248. STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException, "Unknown operator " << opstring << " in " << scope.description << ".");
  1249. }
  1250. }
  1251. STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException, "No supported operator declaration found for complex expressions as " << expressionStructure.dump() << " in " << scope.description << ".");
  1252. }
  1253. STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException, "No supported expression found at " << expressionStructure.dump() << " in " << scope.description << ".");
  1254. // Silly warning suppression.
  1255. return storm::expressions::Expression();
  1256. }
  1257. void JaniParser::parseActions(json const& actionStructure, storm::jani::Model& parentModel) {
  1258. std::set<std::string> actionNames;
  1259. for(auto const& actionEntry : actionStructure) {
  1260. STORM_LOG_THROW(actionEntry.count("name") == 1, storm::exceptions::InvalidJaniException, "Actions must have exactly one name.");
  1261. std::string actionName = getString(actionEntry.at("name"), "name of action");
  1262. STORM_LOG_THROW(actionNames.count(actionName) == 0, storm::exceptions::InvalidJaniException, "Action with name " + actionName + " already exists.");
  1263. parentModel.addAction(storm::jani::Action(actionName));
  1264. actionNames.emplace(actionName);
  1265. }
  1266. }
  1267. storm::jani::Automaton JaniParser::parseAutomaton(json const &automatonStructure, storm::jani::Model const& parentModel, Scope const& globalScope) {
  1268. STORM_LOG_THROW(automatonStructure.count("name") == 1, storm::exceptions::InvalidJaniException, "Each automaton must have a name");
  1269. std::string name = getString(automatonStructure.at("name"), " the name field for automaton");
  1270. Scope scope = globalScope.refine(name);
  1271. storm::jani::Automaton automaton(name, expressionManager->declareIntegerVariable("_loc_" + name));
  1272. uint64_t varDeclCount = automatonStructure.count("variables");
  1273. STORM_LOG_THROW(varDeclCount < 2, storm::exceptions::InvalidJaniException, "Automaton '" << name << "' has more than one list of variables");
  1274. VariablesMap localVars;
  1275. scope.localVars = &localVars;
  1276. if (varDeclCount > 0) {
  1277. bool requireInitialValues = automatonStructure.count("restrict-initial") == 0;
  1278. for(auto const& varStructure : automatonStructure.at("variables")) {
  1279. std::shared_ptr<storm::jani::Variable> var = parseVariable(varStructure, requireInitialValues, scope.refine("variables[" + std::to_string(localVars.size()) + "] of automaton " + name), name + VARIABLE_AUTOMATON_DELIMITER);
  1280. assert(localVars.count(var->getName()) == 0);
  1281. localVars.emplace(var->getName(), &automaton.addVariable(*var));
  1282. }
  1283. }
  1284. uint64_t funDeclCount = automatonStructure.count("functions");
  1285. STORM_LOG_THROW(funDeclCount < 2, storm::exceptions::InvalidJaniException, "Automaton '" << name << "' has more than one list of functions");
  1286. FunctionsMap localFuns;
  1287. scope.localFunctions = &localFuns;
  1288. if (funDeclCount > 0) {
  1289. // We require two passes through the function definitions array to allow referring to functions before they were defined.
  1290. std::vector<storm::jani::FunctionDefinition> dummyFunctionDefinitions;
  1291. for (auto const& funStructure : automatonStructure.at("functions")) {
  1292. // Skip parsing of function body
  1293. dummyFunctionDefinitions.push_back(parseFunctionDefinition(funStructure, scope.refine("functions[" + std::to_string(localFuns.size()) + "] of automaton " + name), true));
  1294. }
  1295. // Store references to the dummy function definitions. This needs to happen in a separate loop since otherwise, references to FunDefs can be invalidated after calling dummyFunctionDefinitions.push_back
  1296. for (auto const& funDef : dummyFunctionDefinitions) {
  1297. bool unused = localFuns.emplace(funDef.getName(), &funDef).second;
  1298. STORM_LOG_THROW(unused, storm::exceptions::InvalidJaniException, "Multiple definitions of functions with the name " << funDef.getName() << " in " << scope.description);
  1299. }
  1300. for (auto const& funStructure : automatonStructure.at("functions")) {
  1301. // Actually parse the function body
  1302. storm::jani::FunctionDefinition funDef = parseFunctionDefinition(funStructure, scope.refine("functions[" + std::to_string(localFuns.size()) + "] of automaton " + name),
  1303. false, name + VARIABLE_AUTOMATON_DELIMITER);
  1304. assert(localFuns.count(funDef.getName()) == 1);
  1305. localFuns[funDef.getName()] = &automaton.addFunctionDefinition(funDef);
  1306. }
  1307. }
  1308. STORM_LOG_THROW(automatonStructure.count("locations") > 0, storm::exceptions::InvalidJaniException, "Automaton '" << name << "' does not have locations.");
  1309. std::unordered_map<std::string, uint64_t> locIds;
  1310. for(auto const& locEntry : automatonStructure.at("locations")) {
  1311. STORM_LOG_THROW(locEntry.count("name") == 1, storm::exceptions::InvalidJaniException, "Locations for automaton '" << name << "' must have exactly one name");
  1312. std::string locName = getString(locEntry.at("name"), "location of automaton " + name);
  1313. STORM_LOG_THROW(locIds.count(locName) == 0, storm::exceptions::InvalidJaniException, "Location with name '" + locName + "' already exists in automaton '" + name + "'");
  1314. STORM_LOG_THROW(locEntry.count("invariant") == 0, storm::exceptions::InvalidJaniException, "Invariants in locations as in '" + locName + "' in automaton '" + name + "' are not supported");
  1315. //STORM_LOG_THROW(locEntry.count("invariant") > 0 && !supportsInvariants(parentModel.getModelType()), storm::exceptions::InvalidJaniException, "Invariants are not supported in the model type " + to_string(parentModel.getModelType()));
  1316. std::vector<storm::jani::Assignment> transientAssignments;
  1317. if(locEntry.count("transient-values") > 0) {
  1318. for(auto const& transientValueEntry : locEntry.at("transient-values")) {
  1319. STORM_LOG_THROW(transientValueEntry.count("ref") == 1, storm::exceptions::InvalidJaniException, "Transient values in location " << locName << " need exactly one ref that is assigned to");
  1320. STORM_LOG_THROW(transientValueEntry.count("value") == 1, storm::exceptions::InvalidJaniException, "Transient values in location " << locName << " need exactly one assigned value");
  1321. storm::jani::LValue lValue = parseLValue(transientValueEntry.at("ref"), scope.refine("LHS of assignment in location " + locName));
  1322. STORM_LOG_THROW(lValue.isTransient(), storm::exceptions::InvalidJaniException, "Assigned non-transient variable " << lValue << " in location " + locName + " (automaton: '" + name + "')");
  1323. storm::expressions::Expression rhs = parseExpression(transientValueEntry.at("value"), scope.refine("Assignment of lValue in location " + locName));
  1324. transientAssignments.emplace_back(lValue, rhs);
  1325. }
  1326. }
  1327. uint64_t id = automaton.addLocation(storm::jani::Location(locName, transientAssignments));
  1328. locIds.emplace(locName, id);
  1329. }
  1330. STORM_LOG_THROW(automatonStructure.count("initial-locations") == 1, storm::exceptions::InvalidJaniException, "Automaton '" << name << "' does not have initial locations.");
  1331. for(json const& initLocStruct : automatonStructure.at("initial-locations")) {
  1332. automaton.addInitialLocation(getString(initLocStruct, "Initial locations for automaton '" + name + "'."));
  1333. }
  1334. STORM_LOG_THROW(automatonStructure.count("restrict-initial") < 2, storm::exceptions::InvalidJaniException, "Automaton '" << name << "' has multiple initial value restrictions");
  1335. storm::expressions::Expression initialValueRestriction = expressionManager->boolean(true);
  1336. if(automatonStructure.count("restrict-initial") > 0) {
  1337. STORM_LOG_THROW(automatonStructure.at("restrict-initial").count("exp") == 1, storm::exceptions::InvalidJaniException, "Automaton '" << name << "' needs an expression inside the initial restricion");
  1338. initialValueRestriction = parseExpression(automatonStructure.at("restrict-initial").at("exp"), scope.refine("Initial value restriction"));
  1339. }
  1340. automaton.setInitialStatesRestriction(initialValueRestriction);
  1341. STORM_LOG_THROW(automatonStructure.count("edges") > 0, storm::exceptions::InvalidJaniException, "Automaton '" << name << "' must have a list of edges");
  1342. for (auto const& edgeEntry : automatonStructure.at("edges")) {
  1343. // source location
  1344. STORM_LOG_THROW(edgeEntry.count("location") == 1, storm::exceptions::InvalidJaniException, "Each edge in automaton '" << name << "' must have a source");
  1345. std::string sourceLoc = getString(edgeEntry.at("location"), "source location for edge in automaton '" + name + "'");
  1346. STORM_LOG_THROW(locIds.count(sourceLoc) == 1, storm::exceptions::InvalidJaniException, "Source of edge has unknown location '" << sourceLoc << "' in automaton '" << name << "'.");
  1347. // action
  1348. STORM_LOG_THROW(edgeEntry.count("action") < 2, storm::exceptions::InvalidJaniException, "Edge from " << sourceLoc << " in automaton " << name << " has multiple actions");
  1349. std::string action = storm::jani::Model::SILENT_ACTION_NAME; // def is tau
  1350. if(edgeEntry.count("action") > 0) {
  1351. action = getString(edgeEntry.at("action"), "action name in edge from '" + sourceLoc + "' in automaton '" + name + "'");
  1352. // TODO check if action is known
  1353. assert(action != "");
  1354. }
  1355. // rate
  1356. STORM_LOG_THROW(edgeEntry.count("rate") < 2, storm::exceptions::InvalidJaniException, "Edge from '" << sourceLoc << "' in automaton '" << name << "' has multiple rates");
  1357. storm::expressions::Expression rateExpr;
  1358. if(edgeEntry.count("rate") > 0) {
  1359. STORM_LOG_THROW(edgeEntry.at("rate").count("exp") == 1, storm::exceptions::InvalidJaniException, "Rate in edge from '" << sourceLoc << "' in automaton '" << name << "' must have a defing expression.");
  1360. rateExpr = parseExpression(edgeEntry.at("rate").at("exp"), scope.refine("rate expression in edge from '" + sourceLoc));
  1361. STORM_LOG_THROW(rateExpr.hasNumericalType(), storm::exceptions::InvalidJaniException, "Rate '" << rateExpr << "' has not a numerical type");
  1362. STORM_LOG_THROW(rateExpr.containsVariables() || rateExpr.evaluateAsRational() > storm::utility::zero<storm::RationalNumber>(), storm::exceptions::InvalidJaniException, "Only positive rates are allowed but rate '" << rateExpr << " was found.");
  1363. }
  1364. // guard
  1365. STORM_LOG_THROW(edgeEntry.count("guard") <= 1, storm::exceptions::InvalidJaniException, "Guard can be given at most once in edge from '" << sourceLoc << "' in automaton '" << name << "'");
  1366. storm::expressions::Expression guardExpr = expressionManager->boolean(true);
  1367. if (edgeEntry.count("guard") == 1) {
  1368. STORM_LOG_THROW(edgeEntry.at("guard").count("exp") == 1, storm::exceptions::InvalidJaniException, "Guard in edge from '" + sourceLoc + "' in automaton '" + name + "' must have one expression");
  1369. guardExpr = parseExpression(edgeEntry.at("guard").at("exp"), scope.refine("guard expression in edge from '" + sourceLoc));
  1370. STORM_LOG_THROW(guardExpr.hasBooleanType(), storm::exceptions::InvalidJaniException, "Guard " << guardExpr << " does not have Boolean type.");
  1371. }
  1372. assert(guardExpr.isInitialized());
  1373. std::shared_ptr<storm::jani::TemplateEdge> templateEdge = std::make_shared<storm::jani::TemplateEdge>(guardExpr);
  1374. // edge assignments
  1375. if (edgeEntry.count("assignments") > 0) {
  1376. STORM_LOG_THROW(edgeEntry.count("assignments") == 1, storm::exceptions::InvalidJaniException, "Multiple edge assignments in edge from '" + sourceLoc + "' in automaton '" + name + "'.");
  1377. for (auto const& assignmentEntry : edgeEntry.at("assignments")) {
  1378. // ref
  1379. STORM_LOG_THROW(assignmentEntry.count("ref") == 1, storm::exceptions::InvalidJaniException, "Assignment in edge from '" << sourceLoc << "' in automaton '" << name << "' must have one ref field");
  1380. storm::jani::LValue lValue = parseLValue(assignmentEntry.at("ref"), scope.refine("Assignment variable in edge from '" + sourceLoc + "' in automaton '" + name + "'"));
  1381. // value
  1382. STORM_LOG_THROW(assignmentEntry.count("value") == 1, storm::exceptions::InvalidJaniException, "Assignment in edge from '" << sourceLoc << "' in automaton '" << name << "' must have one value field");
  1383. storm::expressions::Expression assignmentExpr = parseExpression(assignmentEntry.at("value"), scope.refine("assignment in edge from '" + sourceLoc + "' in automaton '" + name + "'"));
  1384. // TODO check types
  1385. // index
  1386. int64_t assignmentIndex = 0; // default.
  1387. if(assignmentEntry.count("index") > 0) {
  1388. assignmentIndex = getSignedInt(assignmentEntry.at("index"), "assignment index in edge from '" + sourceLoc + "' in automaton '" + name + "'");
  1389. }
  1390. templateEdge->getAssignments().add(storm::jani::Assignment(lValue, assignmentExpr, assignmentIndex));
  1391. }
  1392. }
  1393. // destinations
  1394. STORM_LOG_THROW(edgeEntry.count("destinations") == 1, storm::exceptions::InvalidJaniException, "A single list of destinations must be given in edge from '" << sourceLoc << "' in automaton '" << name << "'");
  1395. std::vector<std::pair<uint64_t, storm::expressions::Expression>> destinationLocationsAndProbabilities;
  1396. for(auto const& destEntry : edgeEntry.at("destinations")) {
  1397. // target location
  1398. STORM_LOG_THROW(edgeEntry.count("location") == 1, storm::exceptions::InvalidJaniException, "Each destination in edge from '" << sourceLoc << "' in automaton '" << name << "' must have a target location");
  1399. std::string targetLoc = getString(destEntry.at("location"), "target location for edge from '" + sourceLoc + "' in automaton '" + name + "'");
  1400. STORM_LOG_THROW(locIds.count(targetLoc) == 1, storm::exceptions::InvalidJaniException, "Target of edge has unknown location '" << targetLoc << "' in automaton '" << name << "'.");
  1401. // probability
  1402. storm::expressions::Expression probExpr;
  1403. unsigned probDeclCount = destEntry.count("probability");
  1404. STORM_LOG_THROW(probDeclCount < 2, storm::exceptions::InvalidJaniException, "Destination in edge from '" << sourceLoc << "' to '" << targetLoc << "' in automaton '" << name << "' has multiple probabilites");
  1405. if(probDeclCount == 0) {
  1406. probExpr = expressionManager->rational(1.0);
  1407. } else {
  1408. STORM_LOG_THROW(destEntry.at("probability").count("exp") == 1, storm::exceptions::InvalidJaniException, "Destination in edge from '" << sourceLoc << "' to '" << targetLoc << "' in automaton '" << name << "' must have a probability expression.");
  1409. probExpr = parseExpression(destEntry.at("probability").at("exp"), scope.refine("probability expression in edge from '" + sourceLoc + "' to '" + targetLoc + "' in automaton '" + name + "'"));
  1410. }
  1411. assert(probExpr.isInitialized());
  1412. STORM_LOG_THROW(probExpr.hasNumericalType(), storm::exceptions::InvalidJaniException, "Probability expression " << probExpr << " does not have a numerical type." );
  1413. // assignments
  1414. std::vector<storm::jani::Assignment> assignments;
  1415. unsigned assignmentDeclCount = destEntry.count("assignments");
  1416. STORM_LOG_THROW(assignmentDeclCount < 2, storm::exceptions::InvalidJaniException, "Destination in edge from '" << sourceLoc << "' to '" << targetLoc << "' in automaton '" << name << "' has multiple assignment lists");
  1417. if (assignmentDeclCount > 0) {
  1418. for (auto const& assignmentEntry : destEntry.at("assignments")) {
  1419. // ref
  1420. STORM_LOG_THROW(assignmentEntry.count("ref") == 1, storm::exceptions::InvalidJaniException, "Assignment in edge from '" << sourceLoc << "' to '" << targetLoc << "' in automaton '" << name << "' must have one ref field");
  1421. storm::jani::LValue lValue = parseLValue(assignmentEntry.at("ref"), scope.refine("Assignment variable in edge from '" + sourceLoc + "' to '" + targetLoc + "' in automaton '" + name + "'"));
  1422. // value
  1423. STORM_LOG_THROW(assignmentEntry.count("value") == 1, storm::exceptions::InvalidJaniException, "Assignment in edge from '" << sourceLoc << "' to '" << targetLoc << "' in automaton '" << name << "' must have one value field");
  1424. storm::expressions::Expression assignmentExpr = parseExpression(assignmentEntry.at("value"), scope.refine("assignment in edge from '" + sourceLoc + "' to '" + targetLoc + "' in automaton '" + name + "'"));
  1425. // TODO check types
  1426. // index
  1427. int64_t assignmentIndex = 0; // default.
  1428. if(assignmentEntry.count("index") > 0) {
  1429. assignmentIndex = getSignedInt(assignmentEntry.at("index"), "assignment index in edge from '" + sourceLoc + "' to '" + targetLoc + "' in automaton '" + name + "'");
  1430. }
  1431. assignments.emplace_back(lValue, assignmentExpr, assignmentIndex);
  1432. }
  1433. }
  1434. destinationLocationsAndProbabilities.emplace_back(locIds.at(targetLoc), probExpr);
  1435. templateEdge->addDestination(storm::jani::TemplateEdgeDestination(assignments));
  1436. }
  1437. automaton.addEdge(storm::jani::Edge(locIds.at(sourceLoc), parentModel.getActionIndex(action), rateExpr.isInitialized() ? boost::optional<storm::expressions::Expression>(rateExpr) : boost::none, templateEdge, destinationLocationsAndProbabilities));
  1438. }
  1439. return automaton;
  1440. }
  1441. std::vector<storm::jani::SynchronizationVector> parseSyncVectors(json const& syncVectorStructure) {
  1442. std::vector<storm::jani::SynchronizationVector> syncVectors;
  1443. // TODO add error checks
  1444. for (auto const& syncEntry : syncVectorStructure) {
  1445. std::vector<std::string> inputs;
  1446. for (auto const& syncInput : syncEntry.at("synchronise")) {
  1447. if(syncInput.is_null()) {
  1448. inputs.push_back(storm::jani::SynchronizationVector::NO_ACTION_INPUT);
  1449. } else {
  1450. inputs.push_back(syncInput);
  1451. }
  1452. }
  1453. std::string syncResult = syncEntry.at("result");
  1454. syncVectors.emplace_back(inputs, syncResult);
  1455. }
  1456. return syncVectors;
  1457. }
  1458. std::shared_ptr<storm::jani::Composition> JaniParser::parseComposition(json const &compositionStructure) {
  1459. if(compositionStructure.count("automaton")) {
  1460. return std::shared_ptr<storm::jani::AutomatonComposition>(new storm::jani::AutomatonComposition(compositionStructure.at("automaton").get<std::string>()));
  1461. }
  1462. STORM_LOG_THROW(compositionStructure.count("elements") == 1, storm::exceptions::InvalidJaniException, "Elements of a composition must be given, got " << compositionStructure.dump());
  1463. if (compositionStructure.at("elements").size() == 1 && compositionStructure.count("syncs") == 0) {
  1464. // We might have an automaton.
  1465. STORM_LOG_THROW(compositionStructure.at("elements").back().count("automaton") == 1, storm::exceptions::InvalidJaniException, "Automaton must be given in composition");
  1466. if (compositionStructure.at("elements").back().at("automaton").is_string()) {
  1467. std::string name = compositionStructure.at("elements").back().at("automaton");
  1468. // TODO check whether name exist?
  1469. return std::shared_ptr<storm::jani::AutomatonComposition>(new storm::jani::AutomatonComposition(name));
  1470. }
  1471. STORM_LOG_THROW(false, storm::exceptions::InvalidJaniException, "Trivial nesting parallel composition is not yet supported");
  1472. }
  1473. std::vector<std::shared_ptr<storm::jani::Composition>> compositions;
  1474. for (auto const& elemDecl : compositionStructure.at("elements")) {
  1475. if(!allowRecursion) {
  1476. STORM_LOG_THROW(elemDecl.count("automaton") == 1, storm::exceptions::InvalidJaniException, "Automaton must be given in the element");
  1477. }
  1478. compositions.push_back(parseComposition(elemDecl));
  1479. }
  1480. STORM_LOG_THROW(compositionStructure.count("syncs") < 2, storm::exceptions::InvalidJaniException, "Sync vectors can be given at most once");
  1481. std::vector<storm::jani::SynchronizationVector> syncVectors;
  1482. if (compositionStructure.count("syncs") > 0) {
  1483. syncVectors = parseSyncVectors(compositionStructure.at("syncs"));
  1484. }
  1485. return std::shared_ptr<storm::jani::Composition>(new storm::jani::ParallelComposition(compositions, syncVectors));
  1486. }
  1487. }
  1488. }