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