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