BPMN-OS
BPMN for optimization and simulation
Loading...
Searching...
No Matches
Expression.cpp
Go to the documentation of this file.
1#include "Expression.h"
6
7using namespace BPMNOS::Model;
8
9Expression::Expression(std::string expression, const AttributeRegistry& attributeRegistry, bool newTarget)
10 : Expression(attributeRegistry.limexHandle, expression, attributeRegistry, newTarget)
11{
12}
13
14Expression::Expression(const LIMEX::Handle<double>& handle, std::string expression,
15 const AttributeRegistry& attributeRegistry, bool newTarget)
16 : attributeRegistry(attributeRegistry)
17 , handle(handle)
18 , expression(expression)
19 , compiled(getExpression(expression))
20 , type(getType())
21{
22 if ( auto name = compiled.getTarget(); name.has_value() ) {
23 if ( name.value() == BPMNOS::Keyword::Undefined ) {
24 throw std::runtime_error("Expression: illegal assignment '" + expression +"'");
25 }
26 if ( !newTarget ) {
27 target = attributeRegistry[ name.value() ];
28 }
29 }
30
31 for ( auto& name : compiled.getVariables() ) {
32 if ( name != BPMNOS::Keyword::Undefined ) {
33 auto attribute = attributeRegistry[ name ];
34 inputs.insert(attribute);
35 variables.push_back(attribute);
36 }
37 }
38 for ( auto& name : compiled.getCollections() ) {
39 if ( name == BPMNOS::Keyword::Undefined ) {
40 throw std::runtime_error("Expression: illegal expression '" + expression +"'");
41 }
42 auto attribute = attributeRegistry[ name ];
43 inputs.insert(attribute);
44 collections.push_back(attribute);
45 }
46}
47
48LIMEX::Expression<double> Expression::getExpression(const std::string& input) const {
49 try {
50 return LIMEX::Expression<double>(encodeQuotedStrings(input), handle);
51 }
52 catch ( const std::exception& error ) {
53 throw std::runtime_error("Expression: illegal expression '" + input + "'.\n" + error.what());
54 }
55}
56
57Expression::Type Expression::getType() const {
58 auto& variableNames = compiled.getVariables();
59 assert( compiled.getRoot().operands.size() == 1 );
60 assert( compiled.getRoot().type == LIMEX::Type::group );
61
62 auto& root = compiled.getRoot();
63 assert( !root.operands.empty() );
64 auto& node = std::get< LIMEX::Node<double> >(root.operands[0]);
65
66 // check if any of the variables is named "undefined"
67 if ( std::find( variableNames.begin(), variableNames.end(), BPMNOS::Keyword::Undefined ) != variableNames.end() ) {
68 // only lhs == undefined, lhs != undefined, and lhs := undefined are allowed
69
70 if ( node.type == LIMEX::Type::assign ) {
71 assert( node.operands.size() == 1 );
72 if (
73 !std::holds_alternative< LIMEX::Node<double> >(node.operands[0]) ||
74 std::get< LIMEX::Node<double> >(node.operands[0]).type != LIMEX::Type::variable
75 ) {
76 throw std::runtime_error("Expression: illegal assignment '" + expression +"'");
77 }
78 return Type::UNASSIGN;
79 }
80
81 if ( node.type != LIMEX::Type::equal_to && node.type != LIMEX::Type::not_equal_to ) {
82 throw std::runtime_error("Expression: illegal expression '" + expression +"'");
83 }
84
85 assert( node.operands.size() == 2 );
86 assert( std::holds_alternative< LIMEX::Node<double> >(node.operands[0]) );
87 assert( std::holds_alternative< LIMEX::Node<double> >(node.operands[1]) );
88 auto& lhs = std::get< LIMEX::Node<double> >(node.operands[0]);
89 auto& rhs = std::get< LIMEX::Node<double> >(node.operands[1]);
90 assert( !lhs.operands.empty() );
91 assert( !rhs.operands.empty() );
92
93 if (
94 lhs.type != LIMEX::Type::variable ||
95 variableNames.at( std::get< size_t >(lhs.operands[0]) ) == BPMNOS::Keyword::Undefined ||
96 rhs.type != LIMEX::Type::variable ||
97 variableNames.at( std::get< size_t >(rhs.operands[0]) ) != BPMNOS::Keyword::Undefined
98 ) {
99 throw std::runtime_error("Expression: illegal comparison '" + expression +"'");
100 }
101
102 return node.type == LIMEX::Type::equal_to ? Type::IS_NULL : Type::IS_NOT_NULL;
103 }
104 // all variables must be defined
105 return ( (int)node.type >= (int)LIMEX::Type::assign ) ? Type::ASSIGN : Type::OTHER;
106}
107
109 assert( compiled.getRoot().operands.size() == 1 );
110 assert( compiled.getRoot().type == LIMEX::Type::group );
111 auto& root = compiled.getRoot();
112 auto& node = std::get< LIMEX::Node<double> >(root.operands[0]);
113 if ( node.type == LIMEX::Type::variable ) {
114 assert(inputs.size());
115 return *inputs.begin();
116 }
117 return nullptr;
118}
119
120template <typename DataType>
121std::optional<BPMNOS::number> Expression::execute(const BPMNOS::Values& status, const DataType& data, const BPMNOS::Values& globals) const {
122 if ( type == Type::UNASSIGN ) {
123 return std::nullopt;
124 }
125 if ( type == Type::IS_NULL ) {
126 assert(variables.size() == 1);
127 auto value = attributeRegistry.getValue(variables[0],status,data,globals);
128 return number( (double)!value.has_value() );
129 }
130 if ( type == Type::IS_NOT_NULL ) {
131 assert(variables.size() == 1);
132 auto value = attributeRegistry.getValue(variables[0],status,data,globals);
133 return number( (double)value.has_value() );
134 }
135
136 // collect variable values
137 std::vector< double > variableValues;
138 for ( auto attribute : variables ) {
139 auto value = attributeRegistry.getValue(attribute,status,data,globals);
140 if ( !value.has_value() ) {
141 // return nullopt because required attribute value is not given
142 return std::nullopt;
143 }
144 variableValues.push_back( (double)value.value() );
145 }
146
147 // collect values of all variables in collection
148 std::vector< std::vector< double > > collectionValues;
149 for ( auto attribute : collections ) {
150 collectionValues.push_back( {} );
151 auto collection = attributeRegistry.getValue(attribute,status,data,globals);
152 if ( !collection.has_value() ) {
153 // return nullopt because required collection is not given
154 return std::nullopt;
155 }
156 for ( auto value : collectionRegistry[(size_t)collection.value()] ) {
157 collectionValues.back().push_back( value );
158 }
159 }
160
161 try {
162 return number(compiled.evaluate(variableValues,collectionValues));
163 }
164 catch (const std::runtime_error& e) {
165 std::string arguments;
166 for ( auto attribute : variables ) {
167 if (attribute != variables.front()) arguments += ", ";
168 arguments += attribute->name + " = ";
169 auto value = attributeRegistry.getValue(attribute,status,data,globals);
170 assert( value.has_value() );
171 arguments += BPMNOS::to_string(value.value(),attribute->type);
172 }
173 throw std::runtime_error(std::format("Expression: failed to evaluate '{}' with {}\n{}", expression, arguments, e.what()));
174 }
175}
176
177template std::optional<BPMNOS::number> Expression::execute<BPMNOS::Values>(const BPMNOS::Values& status, const BPMNOS::Values& data, const BPMNOS::Values& globals) const;
178template std::optional<BPMNOS::number> Expression::execute<BPMNOS::SharedValues>(const BPMNOS::Values& status, const BPMNOS::SharedValues& data, const BPMNOS::Values& globals) const;
179
CollectionRegistry collectionRegistry
std::optional< BPMNOS::number > getValue(const Attribute *attribute, const Values &status, const Values &data, const Values &globals) const
Class representing a mathematical expression.
Definition Expression.h:17
const AttributeRegistry & attributeRegistry
Definition Expression.h:30
Expression(const std::string expression, const AttributeRegistry &attributeRegistry, bool newTarget=false)
Construct expression using attributeRegistry's limexHandle.
Definition Expression.cpp:9
const Attribute * isAttribute() const
Returns pointer to the attribute if and only if expression contains nothing else.
std::set< const Attribute * > inputs
Vector containing all input attributes and collections used by the expression.
Definition Expression.h:36
std::optional< const Attribute * > target
Definition Expression.h:35
const LIMEX::Expression< double > compiled
Definition Expression.h:33
std::vector< const Attribute * > variables
Vector containing all input attributes used by the expression.
Definition Expression.h:37
const std::string expression
Definition Expression.h:32
std::vector< const Attribute * > collections
Vector containing all input collections used by the expression.
Definition Expression.h:38
const LIMEX::Handle< double > & handle
Definition Expression.h:31
std::optional< BPMNOS::number > execute(const BPMNOS::Values &status, const DataType &data, const BPMNOS::Values &globals) const
const std::string Undefined
Definition Keywords.h:10
std::string to_string(number numericValue, const ValueType &type)
Converts a number to a string.
Definition Number.cpp:154
std::string encodeQuotedStrings(std::string text)
BPMNOS_NUMBER_TYPE number
Definition Number.h:50