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