All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
Analyzer.cpp
Go to the documentation of this file.
1 // ============================================================================
2 // STD:
3 // ============================================================================
4 #include <iostream>
5 #include <memory>
6 // ============================================================================
7 // BOOST:
8 // ============================================================================
9 #include <boost/format.hpp>
10 // ============================================================================
11 #include "Analyzer.h"
12 #include "Parser.h"
13 #include "Messages.h"
14 #include "Catalog.h"
15 #include "Units.h"
16 #include "PragmaOptions.h"
17 #include "Node.h"
18 #include "IncludedFiles.h"
19 #include "PropertyName.h"
20 #include "PropertyValue.h"
21 // ============================================================================
23 // ============================================================================
24 namespace gp = Gaudi::Parsers;
25 // ============================================================================
26 static bool IncludeNode(gp::Node* node,
27  const std::string& search_path,
28  gp::IncludedFiles* included, gp::Messages* messages) {
29  gp::Node include_root;
30  bool status = gp::Parse(node->position, node->value, search_path, included,
31  messages, &include_root);
32  if (!status) return false;
33  node->value = include_root.value; // Save absolute file path
34  node->children.reserve( node->children.size() + include_root.children.size());
35  std::copy( std::begin(include_root.children), std::end(include_root.children),
36  std::back_inserter(node->children) );
37  return true;
38 }
39 // ============================================================================
40 static bool UnitsNode(gp::Node* node,
41  const std::string& search_path,
42  gp::IncludedFiles* included, gp::Messages* messages) {
43  gp::Node units_root;
44  bool status = gp::ParseUnits(node->position, node->value, search_path,
45  included, messages, &units_root);
46  if (!status) return false;
47  node->value = units_root.value; // Save absolute file path
48  node->children.reserve( node->children.size() + units_root.children.size());
49  std::copy( std::begin(units_root.children), std::end(units_root.children),
50  std::back_inserter(node->children) );
51  return true;
52 }
53 // ============================================================================
54 static std::unique_ptr<gp::PropertyName> GetPropertyName(const gp::Node* node) {
55  if (node->children.size() == 1) {
56  return std::unique_ptr<gp::PropertyName>(new gp::PropertyName(node->children[0].value,
57  node->position));
58  }else {
59  std::string delim;
60  std::string client;
61  for(unsigned int i=0; i < (node->children.size() - 1); ++i) {
62  client += delim+node->children[i].value;
63  delim = '.';
64  }
65  return std::unique_ptr<gp::PropertyName>(new gp::PropertyName(client,
66  node->children[node->children.size() - 1].value, node->position));
67  }
68 }
69 // ============================================================================
70 static std::unique_ptr<gp::PropertyValue> GetPropertyValue(const gp::Node* node,
71  gp::Catalog* catalog,
72  gp::Units* units) {
74  switch (node->type) {
75  // ------------------------------------------------------------------------
76  case gp::Node::kReal: {
77  // Example:
78  // <real value="10" line="3" column="7">
79  // <identifier value="m" line="3" column="10"/>
80  // </real>
81  //
82  if (node->children.size() == 1) {
83  // Unit is presented
84  std::string unit_name = node->children[0].value;
85  double unit_value = 0;
86  if (units->Find(unit_name, unit_value)) {
87  // We have found a unit
88  double val = std::stod(node->value);
89  value.reset(new gp::PropertyValue(std::to_string(val * unit_value)));
90  }else {
91  // Unit not found
92  throw
93  gp::PositionalPropertyValueException::CouldNotFindUnit(
94  node->children[0].position, unit_name);
95  }
96  }else {
97  value.reset(new gp::PropertyValue(node->value));
98  }
99  break;
100  }
101  // ------------------------------------------------------------------------
102  case gp::Node::kString: {
103  //TODO,C++14: use std::quoted
104  value.reset(new gp::PropertyValue('"'+node->value+'"'));
105  break;
106  }
107  // ------------------------------------------------------------------------
108  case gp::Node::kBool: {
109  value.reset(new gp::PropertyValue(node->value));
110  break;
111  }
112  // ------------------------------------------------------------------------
113  case gp::Node::kVector: {
114  std::vector<std::string> result; result.reserve(node->children.size());
115  std::transform( std::begin(node->children), std::end( node->children),
116  std::back_inserter(result), [&](const gp::Node& child) {
117  return GetPropertyValue(&child, catalog, units)->ToString();
118  });
119  value.reset(new gp::PropertyValue( std::move(result) ));
120  break;
121  }
122  // ------------------------------------------------------------------------
123  case gp::Node::kMap: {
125  for(const auto& child : node->children) {
126  auto kvalue = GetPropertyValue(&child.children[0], catalog, units);
127  auto vvalue = GetPropertyValue(&child.children[1], catalog, units);
128  result.emplace( kvalue->ToString(), vvalue->ToString() );
129  }
130  value.reset(new gp::PropertyValue( std::move(result) ));
131  break;
132  }
133  // ------------------------------------------------------------------------
134  case gp::Node::kProperty: {
135  auto property = GetPropertyName(node);
136  gp::Property* exists = catalog->Find(property->client(), property->property());
137  if ( exists ) {
138  value.reset(new gp::PropertyValue(exists->property_value()));
139  } else {
140  throw
141  gp::PositionalPropertyValueException::CouldNotFindProperty(
142  node->position,property->ToString());
143  }
144  break;
145  }
146  case gp::Node::kPropertyRef: {
147  auto property = GetPropertyName(node);
148  // Save a property reference as vector [clientname, property]
150  reference.push_back(property->client());
151  reference.push_back(property->property());
152 
153  value.reset(new gp::PropertyValue(std::move(reference),property->position(),
154  true));
155  break;
156  }
157  // ------------------------------------------------------------------------
158  default: {
159  assert(false);
160  break;
161  }
162  }
163  return value;
164 }
165 
166 // ============================================================================
167 static std::string SignString(gp::Node::NodeType type) {
168  switch (type) {
169  case gp::Node::kEqual : {
170  return "=";
171  }
172 
173  case gp::Node::kPlusEqual : {
174  return "+=";
175  }
176 
177  case gp::Node::kMinusEqual : {
178  return "-=";
179  }
180  default: {
181  assert(false);
182  break;
183  }
184  }
185  return "unknown";
186 
187 }
188 // ============================================================================
189 static bool AssignNode(const gp::Node* node,
190  gp::Messages* messages, gp::Catalog* catalog, gp::Units* units,
191  bool is_print) {
192 // ----------------------------------------------------------------------------
194 // ----------------------------------------------------------------------------
195  auto property = GetPropertyName(&node->children[0]);
196  try {
197  value = GetPropertyValue(&node->children[2], catalog, units);
198  }catch(const gp::PositionalPropertyValueException& ex){
199  messages->AddError(ex.position(), ex.what());
200  return false;
201  }
202  // ------------------------------------------------------------------------
203  bool reassign = false;
204  gp::Property* exists = catalog->Find(property->client(), property->property());
205 // ----------------------------------------------------------------------------
206  if ( exists ) {
207  // ----------------------------------------------------------------------
208  // If property already exists:
209  // ----------------------------------------------------------------------
210  try {
211  if (node->children[1].type == gp::Node::kEqual) {
212  std::string message = str(boost::format("Reassignment of option '%1%' .")
213  % property->FullName());
214  if (exists->HasDefinedPosition()) {
215  message += " Previously defined at " +
216  exists->DefinedPosition().ToString() + ".";
217  }
218  reassign = true;
219  // INFO: we don't need this warning
220  // messages->AddWarning(node->position, message);
221  }else if (node->children[1].type == gp::Node::kPlusEqual) {
222  *exists += *value;
223  }else if (node->children[1].type == gp::Node::kMinusEqual) {
224  *exists -= *value;
225  }
226  }catch(const gp::PropertyValueException& ex) {
227  std::string message = ex.what();
228  if (exists->HasDefinedPosition()) {
229  message += " Previously defined at "+exists->DefinedPosition().
230  ToString()+".";
231  }
232  messages->AddError(node->position, message);
233  return false;
234  }
235  }
236 // ----------------------------------------------------------------------------
237  bool result = true;
238  if ( !exists || reassign) {
239  result = catalog->Add(new gp::Property(*property, *value));
240  }
241 
242  if (result && is_print) { /*;%|72t|%2% %3%*/
243  std::string message = str(boost::format("%1% %2% %3%")
244  % property->FullName()
245  % SignString(node->children[1].type)
246  % value->ToString());
247  messages->AddInfo(node->position, message);
248  }
249  return result;
250 }
251 // ============================================================================
252 static bool UnitNode(const gp::Node* node,
253  gp::Messages* messages, gp::Units* units, bool is_print) {
254  // --------------------------------------------------------------------------
255  double left = std::stod(node->children[0].value);
256  std::string name = node->children[1].value;
257  double right = std::stod(node->children[2].value);
258  // --------------------------------------------------------------------------
259  gp::Units::Container::mapped_type exists;
260  if (units->Find(name, exists)) {
261  std::string message =
262  str(boost::format("Unit '%1%' already defined") % name);
263  if (exists.second.Exists()) {
264  message += " at "+exists.second.ToString();
265  }
266  messages->AddError(node->children[1].position, message);
267  return false;
268  }
269  // --------------------------------------------------------------------------
270  bool result = units->Add(name, right / left, node->children[1].position);
271  if (result && is_print) {
272  std::string message = str(boost::format("%1% %2% = %3%")
273  % left
274  % name
275  % right);
276  messages->AddInfo(node->position, message);
277  }
278  return result;
279 }
280 // ============================================================================
281 static bool ConditionNode(gp::Node* node,
282  gp::Catalog* catalog, gp::Node** next) {
283 // ----------------------------------------------------------------------------
284  auto property_name = GetPropertyName(&node->children[0]);
285  // --------------------------------------------------------------------------
286  bool is_defined = (nullptr != catalog->Find(property_name->client(),
287  property_name->property()));
288  // --------------------------------------------------------------------------
289  if ((is_defined && (node->children[1].type == gp::Node::kIfdef))
290  || (!is_defined && (node->children[1].type == gp::Node::kIfndef))
291  ) {
292  *next = &node->children[1];
293  }else if (node->children.size()>2){
294  *next = &node->children[2];
295  } else{
296  *next = nullptr;
297  }
298  return true;
299 }
300 // ============================================================================
301 static bool Analyze(gp::Node* node,
302  const std::string& search_path, gp::IncludedFiles* included,
303  gp::Messages* messages, gp::Catalog* catalog, gp::Units* units,
304  gp::PragmaOptions* pragma) {
305 // ----------------------------------------------------------------------------
306  bool result = true;
307  bool local_result = true;
308  bool skip_childs = true;
309  gp::Node* next_root = node;
310  // ------------------------------------------------------------------------
311  switch (node->type) {
312  // ------------------------------------------------------------------------
313  case gp::Node::kRoot: {
314  skip_childs = false;
315  break;
316  }
317  // ----------------------------------------------------------------------
318  case gp::Node::kInclude: {
319  local_result = IncludeNode(node, search_path, included, messages);
320  skip_childs = false;
321  break;
322  }
323  // ----------------------------------------------------------------------
324  case gp::Node::kUnits: {
325  local_result = UnitsNode(node, search_path, included, messages);
326  skip_childs = false;
327  break;
328  }
329  // ----------------------------------------------------------------------
330  case gp::Node::kAssign: {
331  local_result = AssignNode(node, messages, catalog, units,
332  pragma->is_print());
333  break;
334  }
335  // ----------------------------------------------------------------------
336  case gp::Node::kUnit: {
337  local_result = UnitNode(node, messages, units, pragma->is_print());
338  break;
339  }
340  // ----------------------------------------------------------------------
341  case gp::Node::kCondition: {
342  local_result = ConditionNode(node, catalog, &next_root);
343  skip_childs = false;
344  break;
345  }
346 
347  case gp::Node::kPrintOptions: {
348  pragma->setIsPrintOptions(true);
349  break;
350  }
351 
352  case gp::Node::kPrintOn : {
353  pragma->setIsPrint(true);
354  break;
355  }
356 
357  case gp::Node::kPrintOff : {
358  pragma->setIsPrint(false);
359  break;
360  }
361 
362  case gp::Node::kPrintTree : {
363  pragma->setIsPrintTree(true);
364  break;
365  }
366 
367  case gp::Node::kDumpFile : {
368  std::string file="";
369  if(System::resolveEnv(node->value, file)) {
370  pragma->setDumpFile(file);
371  } else {
372  pragma->setDumpFile(node->value);
373  }
374  break;
375  }
376  // ----------------------------------------------------------------------
377  default: {
378  break;
379  }
380  }
381  if (result) result = local_result;
382 
383  if (!skip_childs && next_root) {
384  for(auto& child : next_root->children) {
385  local_result =
386  Analyze(&child, search_path, included, messages, catalog, units,
387  pragma);
388  if (result) result = local_result;
389  }
390  }
391  return result;
392 }
393 
394 bool Unreference(gp::Catalog& catalog, gp::Messages* messages) {
395  bool unreference_result = true;
396  for(auto& client : catalog) {
397  for (auto& current : client.second ) {
398  if (current.IsReference()) {
399  gp::PropertyValue& value = current.property_value();
400  const std::vector<std::string>& names = value.Vector();
401  gp::Property* property = catalog.Find(names[0], names[1]);
402  if (!property) {
403  messages->AddError(value.position(),
404  "Could not unreference " + current.ValueAsString());
405  unreference_result = false;
406  }else{
407  value = property->property_value();
408  }
409  }
410  }
411  }
412  return unreference_result;
413 }
414 
415 // ============================================================================
417  const std::string& search_path, Messages* messages, Catalog* catalog,
418  Units* units, PragmaOptions* pragma, Node* root) {
419  // Extract Path
420  IncludedFiles included;
421  bool result = Parse(filename, search_path, &included, messages, root);
422  if (!result) return false;
423 
424  bool result1 = Analyze(root, search_path, &included, messages, catalog, units,
425  pragma);
426  bool result2 = Unreference(*catalog, messages);
427  return result1 && result2;
428 }
429 
430 // ============================================================================
T copy(T...args)
GAUDI_API std::string format(const char *,...)
MsgStream format utility "a la sprintf(...)".
Definition: MsgStream.cpp:119
GAUDI_API StatusCode resolveEnv(const std::string &var, std::string &res, int recusions=124)
Definition: Environment.cpp:51
bool ParseUnits(const Position &from, const std::string &filename, const std::string &search_path, IncludedFiles *included, Messages *messages, Node *root)
Definition: Parser.cpp:142
T stod(T...args)
T to_string(T...args)
Gaudi::Details::PropertyBase * property(const std::string &name) const
T left(T...args)
T end(T...args)
bool ReadOptions(const std::string &filename, const std::string &search_path, Messages *messages, Catalog *catalog, Units *units, PragmaOptions *pragma, Node *root)
Parse and analyze filename, save all messages and properties.
Definition: Analyzer.cpp:416
Gaudi::Details::PropertyBase Property
backward compatibility hack for old Property base class
Definition: PropertyFwd.h:28
STL class.
T push_back(T...args)
bool Parse(const std::string &filename, const std::string &search_path, IncludedFiles *included, Messages *messages, Node *root)
Definition: Parser.cpp:125
T reset(T...args)
T move(T...args)
STL class.
T begin(T...args)
T back_inserter(T...args)
T emplace(T...args)
T transform(T...args)
bool Unreference(gp::Catalog &catalog, gp::Messages *messages)
Definition: Analyzer.cpp:394
TO * reference(FROM *from)
Definition: KeyedObject.cpp:20
T reserve(T...args)