MADARA  3.4.1
VariableNode.cpp
Go to the documentation of this file.
1 
2 #ifndef _MADARA_NO_KARL_
6 #include "VariableExpander.h"
8 
9 #include <string>
10 #include <sstream>
11 
14  : ComponentNode(context.get_logger()),
15  key_(key),
16  context_(context),
17  key_expansion_necessary_(false)
18 {
19  // this key requires expansion. We do the compilation and error checking here
20  // as the key shouldn't change, and this allows us to only have to do this
21  // once
22  if (key.find("{") != key.npos)
23  {
25  "madara::expression::VariableNode: "
26  "Variable %s requires variable expansion.\n",
27  key.c_str());
28 
30  int num_opens = 0;
31 
32  for (size_t i = 0; i < key.size(); ++i)
33  {
34  if (key[i] == '{')
35  {
36  markers_.push_back(i);
37  ++num_opens;
38  }
39  else if (key[i] == '}')
40  {
41  if (num_opens != 0)
42  {
43  markers_.push_back(i);
44  --num_opens;
45  }
46  else
47  {
48  std::stringstream buffer;
49  buffer << "madara::expression::VariableNode";
50  buffer << ": KARL COMPILE ERROR: matching braces not found in ";
51  buffer << key << "@" << i << "\n";
53  logger_, logger::LOG_ERROR, buffer.str().c_str());
54 
55  throw exceptions::KarlException(buffer.str());
56  }
57  }
58  }
59 
60  if (num_opens > 0)
61  {
62  std::stringstream buffer;
63  buffer << "madara::expression::VariableNode: ";
64  buffer << "KARL COMPILE ERROR: more opening braces than closers in ";
65  buffer << key << "\n";
66  madara_logger_ptr_log(logger_, logger::LOG_ERROR, buffer.str().c_str());
67 
68  throw exceptions::KarlException(buffer.str());
69  }
70  else if (num_opens < 0)
71  {
72  std::stringstream buffer;
73  buffer << "madara::expression::VariableNode: ";
74  buffer << "KARL COMPILE ERROR: more closing braces than openers in ";
75  buffer << key << "\n";
76  madara_logger_ptr_log(logger_, logger::LOG_ERROR, buffer.str().c_str());
77 
78  throw exceptions::KarlException(buffer.str());
79  }
80  }
81  // no variable expansion necessary. Create a hard link to the ref_->
82  // this will save us lots of clock cycles each variable access or
83  // mutation.
84  else
85  {
87  }
88 }
89 
91  size_t opener, size_t& closer) const
92 {
93  size_t i = opener + 1;
94  size_t start = markers_[opener] + 1;
95 
96  std::stringstream builder;
97 
99  "madara::expression::VariableNode:expand_opener: "
100  "key=%s, opener_index=%d, start=%d.\n",
101  key_.c_str(), (int)opener);
102 
103  for (; i < markers_.size(); ++i)
104  {
105  if (key_[markers_[i]] == '{')
106  {
107  // copy before opener (4)
108  if (start < markers_[i])
109  {
110  builder << key_.substr(start, markers_[i] - start);
112  "madara::expression::VariableNode:expand_opener: "
113  "%d-%d{: added %d chars.\n",
114  (int)start, (int)markers_[i], (int)(markers_[i] - start));
115  }
116 
117  size_t sub_opener = i;
118  builder << expand_opener(i, i);
120  "madara::expression::VariableNode:expand_opener: "
121  "get_record(expand_opener()) "
122  "expand_opener(%d, %d).\n",
123  (int)sub_opener, (int)i);
124 
125  // set next start to after [4]{[5]}*[3]
126  start = markers_[i] + 1;
127  }
128  else if (key_[markers_[i]] == '}')
129  {
130  // main action of this function {[2]}
131  closer = i;
132 
133  size_t end = markers_[closer];
134 
135  builder << *context_.get_record(key_.substr(start, end - start));
137  "madara::expression::VariableNode:expand_opener(%d,%d): "
138  "{%d-%d}: added %d chars.\n",
139  (int)opener, (int)closer, (int)start, (int)end, (int)(end - start));
140  break;
141  }
142  }
143 
144  std::string result = builder.str();
145 
147  "madara::expression::VariableNode:expand_opener(%d,%d): "
148  "return %s\n",
149  (int)opener, (int)closer, result.c_str());
150 
151  return result;
152 }
153 
155 {
156  if (key_expansion_necessary_)
157  {
159  "madara::expression::VariableNode:expand_key: "
160  "Variable %s requires variable expansion"
161  " (%d markers).\n",
162  key_.c_str(), (int)markers_.size());
163 
164  size_t i = 0;
165  size_t start = 0;
166 
167  // add the first token into a string builder
168  std::stringstream builder;
169 
170  for (; i < markers_.size(); ++i)
171  {
172  if (key_[markers_[i]] == '{')
173  {
174  // copy characters between expansions [1]{
175  if (start < markers_[i])
176  {
177  builder << key_.substr(start, markers_[i] - start);
179  "madara::expression::VariableNode:expand_key: "
180  "%d-%d{: added %d chars.\n",
181  (int)start, (int)markers_[i], (int)(markers_[i] - start));
182  }
183 
184  size_t opener = i;
185  builder << expand_opener(i, i);
187  "madara::expression::VariableNode:expand_key: "
188  "adding results of expand_opener(%d, %d).\n",
189  (int)opener, (int)i);
190 
191  start = markers_[i] + 1;
192  }
193  }
194 
195  // handle characters trailing }[3]
196  if (start < key_.size() && key_.size() - start > 0)
197  {
199  "madara::expression::VariableNode:expand_key: "
200  "substr add from index %d, %d chars.\n",
201  (int)start, (int)key_.size() - start);
202  builder << key_.substr(start, key_.size() - start);
203  }
204 
205  std::string result = builder.str();
206 
208  "madara::expression::VariableNode:expand_key: "
209  "return %s\n",
210  result.c_str());
211 
212  return result;
213  }
214  // if there was no need to expand the key, just return
215  // the key
216  else
217  return key_;
218 }
219 
221 {
222  visitor.visit(*this);
223 }
224 
226  const
227 {
228  if (ref_.is_valid())
229  return *ref_.get_record_unsafe();
230  else
231  return context_.get(expand_key());
232 }
233 
238  bool& can_change)
239 {
240  // a variable is one of very few nodes that can change over time and
241  // cannot be pruned
242  can_change = true;
243 
244  // we could call item(), but since it is virtual, it incurs unnecessary
245  // overhead.
246  if (ref_.is_valid())
247  return *ref_.get_record_unsafe();
248  else
249  return context_.get(expand_key());
250 }
251 
255 {
257  "madara::expression::VariableNode::evaluate: "
258  "Returning variable %s.\n",
259  key_.c_str());
260 
261  if (ref_.is_valid())
262  {
263  auto ret = ref_.get_record_unsafe();
264 
265  if (settings.exception_on_unitialized && !ret->exists ())
266  {
267  std::stringstream buffer;
268  buffer << "madara::expression::VariableNode::evaluate: ";
269  buffer << "ERROR: settings do not allow reads of unset vars and ";
270  buffer << ref_.get_name() << " is uninitialized";
271  throw exceptions::UninitializedException (buffer.str ());
272  }
273 
274  return *ret;
275  }
276  else
277  {
278  auto ret = context_.get(expand_key(), settings);
279 
280  if (settings.exception_on_unitialized && !ret.exists ())
281  {
282  std::stringstream buffer;
283  buffer << "madara::expression::VariableNode::evaluate: ";
284  buffer << "ERROR: settings do not allow reads of unset vars and ";
285  buffer << ref_.get_name() << " is uninitialized";
286  throw exceptions::UninitializedException (buffer.str ());
287  }
288 
289  return ret;
290  }
291 }
292 
294 {
295  return key_;
296 }
297 
301 {
302  int result = 0;
303 
304  knowledge::VariableReference ref = ref_;
305 
307  "madara::expression::VariableNode::set: "
308  "Attempting to set variable %s to a KnowledgeRecord parameter (%s).\n",
309  key_.c_str(), value.to_string().c_str());
310 
311  if (!ref.is_valid())
312  {
313  ref = context_.get_ref(key_, settings);
314  }
315 
316  if (ref.is_valid())
317  {
318  auto record = ref.get_record_unsafe();
319 
320  // notice that we assume the context is locked
321  // check if we have the appropriate write quality
322  if (!settings.always_overwrite && record->write_quality < record->quality)
323  {
324  result = -2;
325  }
326  else
327  {
328  // cheaper to read than write, so check to see if
329  // we actually need to update quality and status
330  if (record->write_quality != record->quality)
331  record->quality = record->write_quality;
332 
334  "madara::expression::VariableNode::set: "
335  "Setting variable %s with KnowledgeRecord assignment operator.\n",
336  key_.c_str());
337 
338  *record = value;
339 
340  context_.mark_and_signal(ref);
341  }
342  }
343 
344  return result;
345 }
346 
350 {
352  "madara::expression::VariableNode::set: "
353  "Attempting to set variable %s to an Integer parameter (%d).\n",
354  key_.c_str(), (int)value);
355 
356  return VariableNode::set(knowledge::KnowledgeRecord(value), settings);
357 }
358 
360  double value, const madara::knowledge::KnowledgeUpdateSettings& settings)
361 {
363  "madara::expression::VariableNode::set: "
364  "Attempting to set variable %s to a double parameter (%f).\n",
365  key_.c_str(), value);
366 
367  return VariableNode::set(knowledge::KnowledgeRecord(value), settings);
368 }
369 
372 {
374  "madara::expression::VariableNode::set: "
375  "Attempting to set variable %s to a string parameter (%s).\n",
376  key_.c_str(), value.c_str());
377 
378  return VariableNode::set(knowledge::KnowledgeRecord(value), settings);
379 }
380 
383 {
384  if (ref_.is_valid())
385  {
386  auto record = ref_.get_record_unsafe();
387 
388  // notice that we assume the context is locked
389  // check if we have the appropriate write quality
390  if (!settings.always_overwrite && record->write_quality < record->quality)
391  return *record;
392 
393  // cheaper to read than write, so check to see if
394  // we actually need to update quality and status
395  if (record->write_quality != record->quality)
396  record->quality = record->write_quality;
397 
398  --(*record);
399 
400  std::string expanded_key(expand_key());
401  context_.mark_and_signal(ref_);
402 
403  return *record;
404  }
405  else
406  return context_.dec(expand_key(), settings);
407 }
408 
411 {
412  if (ref_.is_valid())
413  {
414  auto record = ref_.get_record_unsafe();
415 
416  // notice that we assume the context is locked
417  // check if we have the appropriate write quality
418  if (!settings.always_overwrite && record->write_quality < record->quality)
419  return *record;
420 
421  // cheaper to read than write, so check to see if
422  // we actually need to update quality and status
423  if (record->write_quality != record->quality)
424  record->quality = record->write_quality;
425 
426  ++(*record);
427 
428  std::string expanded_key(expand_key());
429  context_.mark_and_signal(ref_);
430 
431  return *record;
432  }
433  else
434  return context_.inc(expand_key(), settings);
435 }
436 
437 #endif // _MADARA_NO_KARL_
#define madara_logger_ptr_log(loggering, level,...)
Fast version of the madara::logger::log method for Logger pointers.
Definition: Logger.h:41
const ThreadSafeContext * context_
An exception for unrecoverable KaRL compilation issues.
Definition: KarlException.h:21
An exception for attempting to access an invalid context1.
An abstract base class defines a simple abstract implementation of an expression tree node.
Definition: ComponentNode.h:37
logger::Logger * logger_
handle the context
Definition: ComponentNode.h:96
std::string expand_opener(size_t opener, size_t &closer) const
madara::knowledge::ThreadSafeContext & context_
Definition: VariableNode.h:109
const std::string & key(void) const
Return the variable key.
virtual madara::knowledge::KnowledgeRecord item(void) const
Return the item stored in the node.
virtual madara::knowledge::KnowledgeRecord evaluate(const madara::knowledge::KnowledgeUpdateSettings &settings)
Evaluates the node and its children.
VariableNode(const std::string &key, madara::knowledge::ThreadSafeContext &context)
Ctor.
virtual void accept(Visitor &visitor) const
Define the accept() operation used for the Visitor pattern.
bool key_expansion_necessary_
Expansion necessary.
Definition: VariableNode.h:112
std::vector< size_t > markers_
Definition: VariableNode.h:114
madara::knowledge::VariableReference ref_
Definition: VariableNode.h:107
int set(const madara::knowledge::KnowledgeRecord::Integer &value, const madara::knowledge::KnowledgeUpdateSettings &settings=knowledge::KnowledgeUpdateSettings())
Sets the value stored in the node.
madara::knowledge::KnowledgeRecord dec(const madara::knowledge::KnowledgeUpdateSettings &settings=knowledge::KnowledgeUpdateSettings())
Atomically decrement the variable.
madara::knowledge::KnowledgeRecord inc(const madara::knowledge::KnowledgeUpdateSettings &settings=knowledge::KnowledgeUpdateSettings())
Atomically increment the variable.
std::string expand_key(void) const
Expands the key (if necessary).
virtual madara::knowledge::KnowledgeRecord prune(bool &can_change)
Prune the tree of unnecessary nodes.
Abstract base class for all visitors to all classes that derive from ComponentNode.
Definition: Visitor.h:93
virtual void visit(const LeafNode &node)=0
Visit a LeafNode.
This class encapsulates an entry in a KnowledgeBase.
std::string to_string(const std::string &delimiter=", ") const
converts the value to a string.
uint32_t quality
priority of the update
bool exception_on_unitialized
throw an exception if reference is on uninitialized variable
Settings for applying knowledge updates.
bool always_overwrite
Toggle for always overwriting records, regardless of quality, clock values, etc.
This class stores variables and their values for use by any entity needing state information in a thr...
VariableReference get_ref(const std::string &key, const KnowledgeReferenceSettings &settings=KnowledgeReferenceSettings())
Atomically returns a reference to the variable.
Optimized reference to a variable within the knowledge base.
bool is_valid(void) const
Checks to see if the variable reference has been initialized.
KnowledgeRecord * get_record_unsafe(void) const
Returns a pointer to the variable's KnowledgeRecord Do not use this pointer unless you've locked the ...
constexpr string_t string