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, const bool lattice=true)
265  {
266  std::unique_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(lattice){
277  if(ctxt.line.size()==0)
278  throw std::runtime_error("No beamlines defined by this file");
279 
280  parse_line *line = NULL;
281 
282  {
283  // find the magic "USE" element. eg "USE: linename;"
284  parse_context::map_idx_t::const_iterator it=ctxt.element_idx.find("USE");
285  if(it!=ctxt.element_idx.end()) {
286  parse_element &elem = ctxt.elements[it->second];
287  parse_context::map_idx_t::const_iterator lit = ctxt.line_idx.find(elem.etype);
288 
289  if(lit!=ctxt.line_idx.end()) {
290  line = &ctxt.line[lit->second];
291  } else {
292  std::ostringstream strm;
293  strm<<"\"USE: "<<elem.etype<<";\" references undefined beamline";
294  throw std::runtime_error(strm.str());
295  }
296  } else {
297  // no magic USE, default to last line
298  line = &ctxt.line.back();
299  }
300  }
301 
302  assert(line);
303 
304  if(line->names.size()==0) {
305  std::ostringstream strm;
306  strm<<"Beamline '"<<line->label<<"' has no elements";
307  throw std::runtime_error(strm.str());
308  }
309 
310  Config::vector_t elements;
311  elements.resize(line->names.size());
312 
313  // copy in elements
314  size_t i = 0;
315  for(strlist_t::list_t::const_iterator it=line->names.begin(), end=line->names.end();
316  it!=end; ++it)
317  {
318  Config next(ret->new_scope()); // inhiert global scope
319  const parse_element& elem = ctxt.elements[ctxt.element_idx[*it]];
320 
321  next.reserve(elem.props.size()+2);
322 
323  // push elements properties
324  for(kvlist_t::map_t::const_iterator itx=elem.props.begin(), endx=elem.props.end();
325  itx!=endx; ++itx)
326  {
327  assign_expr_to_Config(next, itx->first, itx->second);
328  }
329 
330  // special properties
331  assert(!elem.etype.empty() && !elem.label.empty());
332  next.set<std::string>("type", elem.etype);
333  next.set<std::string>("name", elem.label);
334  elements[i++].swap(next);
335  }
336 
337  ret->swap<std::string>("name", line->label);
338  ret->swap<Config::vector_t>("elements", elements);
339  }
340 
341  return ret.release();
342  }
343 };
344 
346  :priv(new Pvt)
347 {}
348 
349 GLPSParser::~GLPSParser() {}
350 
351 void
352 GLPSParser::setVar(const std::string& name, const Config::value_t& v)
353 {
354  priv->vars[name] = v;
355 }
356 
357 void
358 GLPSParser::setPrinter(std::ostream* strm)
359 {
360  priv->printer = strm;
361 }
362 
363 Config*
364 GLPSParser::parse_file(const char *fname, const bool have_lattice)
365 {
366  boost::filesystem::path fpath;
367  if(fname) {
368  fpath = boost::filesystem::canonical(fname).parent_path();
369  } else {
370  fpath = boost::filesystem::current_path();
371  }
372 
373  FILE *fp;
374  bool closeme = fname!=NULL && strcmp(fname,"-")!=0;
375  if(closeme)
376  fp = fopen(fname, "r");
377  else
378  fp = stdin;
379  if(!fp) {
380  std::ostringstream strm;
381  strm<<"Failed to open file for parsing '"<<fname<<"'";
382  throw std::runtime_error(strm.str());
383  }
384  try{
385  Config *ret = parse_file(have_lattice, fp, fpath.native().c_str());
386  if(closeme) fclose(fp);
387  return ret;
388  }catch(...){
389  if(closeme) fclose(fp);
390  throw;
391  }
392 }
393 
394 Config*
395 GLPSParser::parse_file(const bool have_lattice, FILE *fp, const char *path)
396 {
397  parse_context ctxt(path);
398  ctxt.printer = priv->printer;
399  priv->fill_vars(ctxt);
400  ctxt.parse(fp);
401  return priv->fill_context(ctxt, have_lattice);
402 }
403 
404 Config*
405 GLPSParser::parse_byte(const char* s, size_t len, const char *path)
406 {
407  parse_context ctxt(path);
408  ctxt.printer = priv->printer;
409  priv->fill_vars(ctxt);
410  ctxt.parse(s, len);
411  return priv->fill_context(ctxt);
412 }
413 
414 Config*
415 GLPSParser::parse_byte(const std::string& s, const char *path)
416 {
417  parse_context ctxt(path);
418  ctxt.printer = priv->printer;
419  priv->fill_vars(ctxt);
420  ctxt.parse(s);
421  return priv->fill_context(ctxt);
422 }
423 
424 namespace {
425 // show the properties of a GLPS element
426 struct glps_show_props : public boost::static_visitor<void>
427 {
428  std::ostream& strm;
429  const std::string& name;
430  glps_show_props(std::ostream& s, const std::string& n) :strm(s), name(n) {}
431 
432  void operator()(double v) const
433  {
434  strm<<", "<<name<<" = "<<v;
435  }
436 
437  void operator()(const std::string& v) const
438  {
439  strm<<", "<<name<<" = \""<<v<<"\"";
440  }
441 
442  void operator()(const std::vector<double>& v) const
443  {
444  strm <<", " << name << " = [";
445  for(size_t i=0, N=v.size(); i<N; i++)
446  {
447  if(i!=0)
448  strm << ", ";
449  strm << v[i];
450  }
451  strm << "]";
452  }
453 
454  void operator()(const Config::vector_t& v) const
455  {
456  // ignore
457  }
458 };
459 // show the base GLPS Config (variables and the elements array)
460 struct glps_show : public boost::static_visitor<void>
461 {
462  std::ostream& strm;
463  const std::string& name;
464  glps_show(std::ostream& s, const std::string& n) :strm(s), name(n) {}
465 
466  void operator()(double v) const
467  {
468  strm<<name<<" = "<<v<<";\n";
469  }
470 
471  void operator()(const std::string& v) const
472  {
473  strm<<name<<" = \""<<v<<"\";\n";
474  }
475 
476  void operator()(const std::vector<double>& v) const
477  {
478  strm << name << " = [";
479  for(size_t i=0, N=v.size(); i<N; i++)
480  {
481  if(i!=0)
482  strm << ", ";
483  strm << v[i];
484  }
485  strm << "];\n";
486  }
487 
488  void operator()(const Config::vector_t& v) const
489  {
490  if(name!="elements") {
491  // The GLPS format Only supports nested beamline definitions
492  strm << "# "<<name<<" = [... skipped ...];\n";
493  return;
494  }
495  }
496 };
497 }
498 
499 void GLPSPrint(std::ostream& strm, const Config& conf)
500 {
501  // print variables
502  for(Config::const_iterator it=conf.begin(), end=conf.end();
503  it!=end; ++it)
504  {
505  boost::apply_visitor(glps_show(strm, it->first), it->second);
506  }
507 
508  const Config::vector_t *v;
509  try{
510  v = &conf.get<Config::vector_t>("elements");
511  }catch(key_error&){
512  strm<<"# Missing beamline element list\n";
513  return;
514  }catch(boost::bad_get&){
515  strm<<"# 'elements' is not a beamline element list\n";
516  return;
517  }
518 
519  std::vector<std::string> line;
520  line.reserve(v->size());
521 
522  std::set<std::string> eshown;
523 
524  // print element definitions
525  for(Config::vector_t::const_iterator it=v->begin(), end=v->end();
526  it!=end; ++it)
527  {
528  bool ok = true;
529  try {
530  const std::string& name=it->get<std::string>("name");
531  const std::string& type=it->get<std::string>("type");
532  if(name.empty() || type.empty())
533  throw std::runtime_error("Element missing 'name' and/or 'type'");
534  line.push_back(name);
535  // only show element definition once
536  if(eshown.find(name)!=eshown.end())
537  continue;
538  strm<<name<<": "<<type;
539  eshown.insert(name);
540  }catch(key_error&){
541  ok=false;
542  }catch(boost::bad_get&){
543  ok=false;
544  }
545  if(!ok)
546  strm<<"# <malformed element>";
547 
548  for(Config::const_iterator itx=it->begin(), endx=it->end();
549  itx!=endx; ++itx)
550  {
551  if(itx->first=="name" || itx->first=="type")
552  continue;
553  boost::apply_visitor(glps_show_props(strm, itx->first), itx->second);
554  }
555 
556  strm<<";\n";
557  }
558 
559  std::string lname(conf.get<std::string>("name", "default"));
560  strm<<lname<<": LINE = (";
561 
562  bool first=true;
563  for(std::vector<std::string>::const_iterator it=line.begin(), end=line.end();
564  it!=end; ++it)
565  {
566  if(!first)
567  strm<<", ";
568  first = false;
569  strm<<*it;
570  }
571 
572  strm<<");\nUSE: "<<lname<<";\n";
573 }
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:352
const value_t & getAny(const std::string &name) const
Definition: config.cpp:58
Config * parse_file(const char *fname, const bool have_lattice=true)
Open and parse a file.
Definition: config.cpp:364
void setPrinter(std::ostream *)
Set output for lexer/parser error messages.
Definition: config.cpp:358
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:345
Associative configuration container.
Definition: config.h:66
void show(std::ostream &, unsigned indent=0) const
Print listing of inner scope.
Definition: config.cpp:188
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:405
const_iterator begin() const
The first element.
Definition: config.h:218