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