The Gaudi Framework  master (82fdf313)
Loading...
Searching...
No Matches
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
25namespace gp = Gaudi::Parsers;
26
27static 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}
37static 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}
47static 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}
59static 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 }
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
150static std::string SignString( gp::Node::NodeType type ) {
151 switch ( type ) {
152 case gp::Node::kEqual: {
153 return "=";
154 }
155
157 return "+=";
158 }
159
161 return "-=";
162 }
163 default: {
164 assert( false );
165 break;
166 }
167 }
168 return "unknown";
169}
170static 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
222static 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
243static 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
259static 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
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
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
345bool 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
365bool 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}
bool Unreference(gp::Catalog &catalog, gp::Messages *messages)
Definition Analyzer.cpp:345
Property * Find(std::string_view client, std::string_view name)
Definition Catalog.cpp:47
bool Add(Property *property)
Definition Catalog.cpp:33
void AddInfo(std::string_view info)
Definition Messages.h:24
void AddError(std::string_view error)
Definition Messages.h:28
std::string ToString() const
Definition Position.cpp:14
static PositionalPropertyValueException CouldNotFindUnit(const Position &position, const std::string &name)
static PositionalPropertyValueException CouldNotFindProperty(const Position &position, const std::string &name)
void setDumpFile(std::string dump_file)
void setIsPrintOptions(bool is_print_options)
void setIsPrintTree(bool is_print_tree)
void setIsPrint(bool is_print)
const Position & DefinedPosition() const
Definition Property.cpp:19
bool HasDefinedPosition() const
Definition Property.h:44
PropertyValue & property_value()
Definition Property.h:32
VectorOfStrings & Vector()
std::map< std::string, std::string, std::less<> > MapOfStrings
const Position & position() const
bool Find(std::string_view name, ValueWithPosition &result) const
Definition Units.cpp:28
bool Add(std::string name, double value)
Definition Units.cpp:15
bool ParseUnits(const Position &from, std::string_view filename, std::string_view search_path, IncludedFiles *included, Messages *messages, Node *root)
Definition Parser.cpp:116
bool Parse(std::string_view filename, std::string_view search_path, IncludedFiles *included, Messages *messages, Node *root)
Definition Parser.cpp:105
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
AttribStringParser::Iterator begin(const AttribStringParser &parser)
GAUDI_API StatusCode resolveEnv(const std::string &var, std::string &res, int recusions=124)
std::string value
Definition Node.h:58
Position position
Definition Node.h:60
std::vector< Node > children
Definition Node.h:59
NodeType type
Definition Node.h:57