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