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