FLAME  devel
 All Classes Functions Variables Typedefs Enumerations Pages
config.cpp
1 
2 #include <iostream>
3 #include <sstream>
4 #include <set>
5 
6 #include <flame/config.h>
7 #include <flame/util.h>
8 
9 #include "glps_parser.h"
10 
12  :values(new values_t)
13 {}
14 
16  :values(O.values)
17  ,implicit_values(O.implicit_values)
18 {}
19 
20 Config&
22 {
23  if(this!=&O) {
24  values = O.values;
25  implicit_values = O.implicit_values;
26  }
27  return *this;
28 }
29 
31 void Config::_cow()
32 {
33  if(!values.unique()) {
34  Config::values_pointer U(new values_t(*values)); // copy
35  U.swap(values);
36  }
37 }
38 
39 bool
40 Config::tryGetAny(const std::string& name, value_t& ret) const
41 {
42  values_t::const_iterator it=values->find(name);
43  if(it!=values->end()) {
44  ret = it->second;
45  return true;
46  }
47  if(implicit_values) {
48  it = implicit_values->find(name);
49  if(it!=implicit_values->end()) {
50  ret = it->second;
51  return true;
52  }
53  }
54  return false;
55 }
56 
57 const Config::value_t&
58 Config::getAny(const std::string& name) const
59 {
60  values_t::const_iterator it=values->find(name);
61  if(it!=values->end()) return it->second;
62  if(implicit_values) {
63  it = implicit_values->find(name);
64  if(it!=implicit_values->end()) return it->second;
65  }
66  throw key_error(SB()<<"Missing parameter '"<<name<<"'");
67 }
68 void
69 Config::setAny(const std::string& name, const value_t& val)
70 {
71  _cow();
72  (*values)[name] = val;
73 }
74 
75 void
76 Config::swapAny(const std::string& name, value_t& val)
77 {
78  _cow();
79  {
80  values_t::iterator it = values->find(name);
81  if(it!=values->end()) {
82  it->second.swap(val);
83  return;
84  }
85  }
86  std::pair<values_t::iterator, bool> ret = values->insert(std::make_pair(name,value_t()));
87  assert(ret.second);
88  ret.first->second.swap(val);
89 }
90 
92 {
93  Config ret;
94  // return new Config with empty 'values' and 'implicit_values' containing our 'implicit_values'+'values'
95  if(values->empty()) {
96  ret.implicit_values = implicit_values;
97  } else if(!implicit_values || implicit_values->empty()) {
98  ret.implicit_values = values; // _cow() makes this safe
99  } else {
100  values_pointer ptr(new values_t(*values)); // copy
101  ptr->insert(implicit_values->begin(),
102  implicit_values->end());
103  // note that insert() will not overwrite existing keys
104  ret.implicit_values = ptr;
105  }
106  return ret;
107 }
108 
109 void Config::push_scope()
110 {
111  flatten();
112  implicit_values = values; // _cow() makes this safe
113  values.reset(new values_t);
114 }
115 
116 void Config::flatten()
117 {
118  if(implicit_values) {
119  values->insert(implicit_values->begin(),
120  implicit_values->end());
121  implicit_values.reset();
122  // note that insert() will not overwrite existing keys
123  }
124 }
125 
126 namespace {
127 struct show_value : public boost::static_visitor<void>
128 {
129  unsigned indent;
130  std::ostream& strm;
131  const std::string& name;
132  show_value(std::ostream& s, const std::string& n, unsigned ind=0)
133  : indent(ind), strm(s), name(n) {}
134 
135  void operator()(double v) const
136  {
137  unsigned i=indent;
138  while(i--)
139  strm.put(' ');
140  strm << name << " = " << v << "\n";
141  }
142 
143  void operator()(const std::string& v) const
144  {
145  doindent();
146  strm << name << " = \"" << v << "\"\n";
147  }
148 
149  void operator()(const std::vector<double>& v) const
150  {
151  doindent();
152  strm << name << " = [";
153  for(size_t i=0, N=v.size(); i<N; i++)
154  {
155  if(i!=0)
156  strm << ", ";
157  strm << v[i];
158  }
159  strm << "]\n";
160  }
161 
162  void operator()(const Config::vector_t& v) const
163  {
164  doindent();
165  strm << name << " = [\n";
166  for(size_t i=0, N=v.size(); i<N; i++)
167  {
168  doindent(2);
169  strm << "[" << i << "] = {\n";
170  v[i].show(strm, indent+4);
171  doindent(2);
172  strm << "},\n";
173  }
174  doindent();
175  strm << "]\n";
176  }
177 
178  void doindent(unsigned extra=0) const
179  {
180  unsigned i=indent+extra;
181  while(i--)
182  strm.put(' ');
183  }
184 };
185 }
186 
187 void
188 Config::show(std::ostream& strm, unsigned indent) const
189 {
190  for(Config::values_t::const_iterator it=values->begin(), end=values->end();
191  it!=end; ++it)
192  {
193  boost::apply_visitor(show_value(strm, it->first, indent), it->second);
194  }
195 }
196 
197 namespace {
198 // store variable definitions in parser context
199 struct store_ctxt_var : public boost::static_visitor<void>
200 {
201  const std::string& name;
202  parse_context& ctxt;
203  store_ctxt_var(parse_context& c, const std::string& n)
204  :name(n), ctxt(c)
205  {}
206 #define VISIT(TYPE, E) \
207  void operator()(TYPE v) const { \
208  ctxt.vars.push_back(parse_var(name.c_str(), expr_t(E, v))); \
209  ctxt.var_idx[name] = ctxt.vars.size()-1; \
210  }
211  VISIT(double, glps_expr_number)
212  VISIT(const std::string&, glps_expr_string)
213  VISIT(const std::vector<double>&, glps_expr_vector)
214 #undef VISIT
215  void operator()(const Config::vector_t&) const
216  {
217  // ignore
218  }
219 };
220 
221 void assign_expr_to_Config(Config& conf, const std::string& name, const expr_t& expr)
222 {
223  switch(expr.etype)
224  {
225  case glps_expr_number:
226  conf.set<double>(name, boost::get<double>(expr.value));
227  break;
228  case glps_expr_string:
229  conf.set<std::string>(name, boost::get<std::string>(expr.value));
230  break;
231  case glps_expr_vector:
232  conf.set<std::vector<double> >(name, boost::get<std::vector<double> >(expr.value));
233  break;
234  case glps_expr_config: {
235  boost::shared_ptr<Config> pconf(boost::get<boost::shared_ptr<Config> >(expr.value));
236  std::vector<Config> cvect;
237  if(pconf)
238  cvect.push_back(*pconf);
239  conf.set<std::vector<Config> >(name, cvect);
240  }
241  break;
242  default:
243  throw std::logic_error("Context contained unresolved/illegal variable");
244  }
245 }
246 }
247 
248 struct GLPSParser::Pvt {
249  typedef Config::values_t values_t;
250  values_t vars;
251  std::ostream *printer;
252 
253  Pvt() :printer(&std::cerr) {}
254 
255  void fill_vars(parse_context& ctxt)
256  {
257  for(values_t::const_iterator it=vars.begin(), end=vars.end(); it!=end; ++it)
258  {
259  // fill in ctxt.vars and ctxt.var_idx
260  boost::apply_visitor(store_ctxt_var(ctxt, it->first), it->second);
261  }
262  }
263 
264  Config* fill_context(parse_context& ctxt)
265  {
266  std::auto_ptr<Config> ret(new Config);
267  ret->reserve(ctxt.vars.size()+2);
268 
269  // copy ctxt.vars to top level Config
270  for(parse_context::vars_t::iterator it=ctxt.vars.begin(), end=ctxt.vars.end();
271  it!=end; ++it)
272  {
273  assign_expr_to_Config(*ret, it->name, it->expr);
274  }
275 
276  if(ctxt.line.size()==0)
277  throw std::runtime_error("No beamlines defined by this file");
278 
279  parse_line *line = NULL;
280 
281  {
282  // find the magic "USE" element. eg "USE: linename;"
283  parse_context::map_idx_t::const_iterator it=ctxt.element_idx.find("USE");
284  if(it!=ctxt.element_idx.end()) {
285  parse_element &elem = ctxt.elements[it->second];
286  parse_context::map_idx_t::const_iterator lit = ctxt.line_idx.find(elem.etype);
287 
288  if(lit!=ctxt.line_idx.end()) {
289  line = &ctxt.line[lit->second];
290  } else {
291  std::ostringstream strm;
292  strm<<"\"USE: "<<elem.etype<<";\" references undefined beamline";
293  throw std::runtime_error(strm.str());
294  }
295  } else {
296  // no magic USE, default to last line
297  line = &ctxt.line.back();
298  }
299  }
300 
301  assert(line);
302 
303  if(line->names.size()==0) {
304  std::ostringstream strm;
305  strm<<"Beamline '"<<line->label<<"' has no elements";
306  throw std::runtime_error(strm.str());
307  }
308 
309  Config::vector_t elements;
310  elements.resize(line->names.size());
311 
312  // copy in elements
313  size_t i = 0;
314  for(strlist_t::list_t::const_iterator it=line->names.begin(), end=line->names.end();
315  it!=end; ++it)
316  {
317  Config next(ret->new_scope()); // inhiert global scope
318  const parse_element& elem = ctxt.elements[ctxt.element_idx[*it]];
319 
320  next.reserve(elem.props.size()+2);
321 
322  // push elements properties
323  for(kvlist_t::map_t::const_iterator itx=elem.props.begin(), endx=elem.props.end();
324  itx!=endx; ++itx)
325  {
326  assign_expr_to_Config(next, itx->first, itx->second);
327  }
328 
329  // special properties
330  assert(!elem.etype.empty() && !elem.label.empty());
331  next.set<std::string>("type", elem.etype);
332  next.set<std::string>("name", elem.label);
333  elements[i++].swap(next);
334  }
335 
336  ret->swap<std::string>("name", line->label);
337  ret->swap<Config::vector_t>("elements", elements);
338 
339  return ret.release();
340  }
341 };
342 
344  :priv(new Pvt)
345 {}
346 
347 GLPSParser::~GLPSParser() {}
348 
349 void
350 GLPSParser::setVar(const std::string& name, const Config::value_t& v)
351 {
352  priv->vars[name] = v;
353 }
354 
355 void
356 GLPSParser::setPrinter(std::ostream* strm)
357 {
358  priv->printer = strm;
359 }
360 
361 Config*
362 GLPSParser::parse_file(const char *fname)
363 {
364  boost::filesystem::path fpath;
365  if(fname) {
366  fpath = boost::filesystem::canonical(fname).parent_path();
367  } else {
368  fpath = boost::filesystem::current_path();
369  }
370 
371  FILE *fp;
372  bool closeme = fname!=NULL && strcmp(fname,"-")!=0;
373  if(closeme)
374  fp = fopen(fname, "r");
375  else
376  fp = stdin;
377  if(!fp) {
378  std::ostringstream strm;
379  strm<<"Failed to open file for parsing '"<<fname<<"'";
380  throw std::runtime_error(strm.str());
381  }
382  try{
383  Config *ret = parse_file(fp, fpath.native().c_str());
384  if(closeme) fclose(fp);
385  return ret;
386  }catch(...){
387  if(closeme) fclose(fp);
388  throw;
389  }
390 }
391 
392 Config*
393 GLPSParser::parse_file(FILE *fp, const char *path)
394 {
395  parse_context ctxt(path);
396  ctxt.printer = priv->printer;
397  priv->fill_vars(ctxt);
398  ctxt.parse(fp);
399  return priv->fill_context(ctxt);
400 }
401 
402 Config*
403 GLPSParser::parse_byte(const char* s, size_t len, const char *path)
404 {
405  parse_context ctxt(path);
406  ctxt.printer = priv->printer;
407  priv->fill_vars(ctxt);
408  ctxt.parse(s, len);
409  return priv->fill_context(ctxt);
410 }
411 
412 Config*
413 GLPSParser::parse_byte(const std::string& s, const char *path)
414 {
415  parse_context ctxt(path);
416  ctxt.printer = priv->printer;
417  priv->fill_vars(ctxt);
418  ctxt.parse(s);
419  return priv->fill_context(ctxt);
420 }
421 
422 namespace {
423 // show the properties of a GLPS element
424 struct glps_show_props : public boost::static_visitor<void>
425 {
426  std::ostream& strm;
427  const std::string& name;
428  glps_show_props(std::ostream& s, const std::string& n) :strm(s), name(n) {}
429 
430  void operator()(double v) const
431  {
432  strm<<", "<<name<<" = "<<v;
433  }
434 
435  void operator()(const std::string& v) const
436  {
437  strm<<", "<<name<<" = \""<<v<<"\"";
438  }
439 
440  void operator()(const std::vector<double>& v) const
441  {
442  strm <<", " << name << " = [";
443  for(size_t i=0, N=v.size(); i<N; i++)
444  {
445  if(i!=0)
446  strm << ", ";
447  strm << v[i];
448  }
449  strm << "]";
450  }
451 
452  void operator()(const Config::vector_t& v) const
453  {
454  // ignore
455  }
456 };
457 // show the base GLPS Config (variables and the elements array)
458 struct glps_show : public boost::static_visitor<void>
459 {
460  std::ostream& strm;
461  const std::string& name;
462  glps_show(std::ostream& s, const std::string& n) :strm(s), name(n) {}
463 
464  void operator()(double v) const
465  {
466  strm<<name<<" = "<<v<<";\n";
467  }
468 
469  void operator()(const std::string& v) const
470  {
471  strm<<name<<" = \""<<v<<"\";\n";
472  }
473 
474  void operator()(const std::vector<double>& v) const
475  {
476  strm << name << " = [";
477  for(size_t i=0, N=v.size(); i<N; i++)
478  {
479  if(i!=0)
480  strm << ", ";
481  strm << v[i];
482  }
483  strm << "];\n";
484  }
485 
486  void operator()(const Config::vector_t& v) const
487  {
488  if(name!="elements") {
489  // The GLPS format Only supports nested beamline definitions
490  strm << "# "<<name<<" = [... skipped ...];\n";
491  return;
492  }
493  }
494 };
495 }
496 
497 void GLPSPrint(std::ostream& strm, const Config& conf)
498 {
499  // print variables
500  for(Config::const_iterator it=conf.begin(), end=conf.end();
501  it!=end; ++it)
502  {
503  boost::apply_visitor(glps_show(strm, it->first), it->second);
504  }
505 
506  const Config::vector_t *v;
507  try{
508  v = &conf.get<Config::vector_t>("elements");
509  }catch(key_error&){
510  strm<<"# Missing beamline element list\n";
511  return;
512  }catch(boost::bad_get&){
513  strm<<"# 'elements' is not a beamline element list\n";
514  return;
515  }
516 
517  std::vector<std::string> line;
518  line.reserve(v->size());
519 
520  std::set<std::string> eshown;
521 
522  // print element definitions
523  for(Config::vector_t::const_iterator it=v->begin(), end=v->end();
524  it!=end; ++it)
525  {
526  bool ok = true;
527  try {
528  const std::string& name=it->get<std::string>("name");
529  const std::string& type=it->get<std::string>("type");
530  if(name.empty() || type.empty())
531  throw std::runtime_error("Element missing 'name' and/or 'type'");
532  line.push_back(name);
533  // only show element definition once
534  if(eshown.find(name)!=eshown.end())
535  continue;
536  strm<<name<<": "<<type;
537  eshown.insert(name);
538  }catch(key_error&){
539  ok=false;
540  }catch(boost::bad_get&){
541  ok=false;
542  }
543  if(!ok)
544  strm<<"# <malformed element>";
545 
546  for(Config::const_iterator itx=it->begin(), endx=it->end();
547  itx!=endx; ++itx)
548  {
549  if(itx->first=="name" || itx->first=="type")
550  continue;
551  boost::apply_visitor(glps_show_props(strm, itx->first), itx->second);
552  }
553 
554  strm<<";\n";
555  }
556 
557  std::string lname(conf.get<std::string>("name", "default"));
558  strm<<lname<<": LINE = (";
559 
560  bool first=true;
561  for(std::vector<std::string>::const_iterator it=line.begin(), end=line.end();
562  it!=end; ++it)
563  {
564  if(!first)
565  strm<<", ";
566  first = false;
567  strm<<*it;
568  }
569 
570  strm<<");\nUSE: "<<lname<<";\n";
571 }
bool tryGetAny(const std::string &name, value_t &ret) const
Definition: config.cpp:40
Config()
New empty config.
Definition: config.cpp:11
void setVar(const std::string &name, const Config::value_t &v)
Pre-define variable.
Definition: config.cpp:350
const value_t & getAny(const std::string &name) const
Definition: config.cpp:58
STL namespace.
void setPrinter(std::ostream *)
Set output for lexer/parser error messages.
Definition: config.cpp:356
void set(const std::string &name, typename boost::call_traits< typename detail::is_config_value< T >::type >::param_type val)
Definition: config.h:175
Config & operator=(const Config &)
Assignment.
Definition: config.cpp:21
GLPSParser()
Construct an empty parser context.
Definition: config.cpp:343
Associative configuration container.
Definition: config.h:66
void show(std::ostream &, unsigned indent=0) const
Print listing of inner scope.
Definition: config.cpp:188
Config * parse_file(const char *fname)
Open and parse a file.
Definition: config.cpp:362
void setAny(const std::string &name, const value_t &val)
Definition: config.cpp:69
boost::variant< double, std::vector< double >, std::string, std::vector< Config > > value_t
An individual value (double, double[], string, or Config[])
Definition: config.h:75
const_iterator end() const
one after the last element
Definition: config.h:220
detail::RT< T >::type get(const std::string &name) const
Definition: config.h:123
Config new_scope() const
Create a new inner scope.
Definition: config.cpp:91
void swapAny(const std::string &name, value_t &val)
Exchange a single parameter untyped.
Definition: config.cpp:76
values_t::const_iterator const_iterator
const_iterator
Definition: config.h:215
Config * parse_byte(const char *s, size_t len, const char *path=NULL)
Parse from byte buffer.
Definition: config.cpp:403
const_iterator begin() const
The first element.
Definition: config.h:218