Teuchos - Trilinos Tools Package  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Teuchos_YamlParser.cpp
1 // @HEADER
2 // *****************************************************************************
3 // Teuchos: Common Tools Package
4 //
5 // Copyright 2004 NTESS and the Teuchos contributors.
6 // SPDX-License-Identifier: BSD-3-Clause
7 // *****************************************************************************
8 // @HEADER
9 
10 #include <iostream>
11 #include <iomanip>
12 #include <ios>
13 #include <sstream>
14 #include <cctype>
15 #include <fstream>
16 
20 #include "Teuchos_TwoDArray.hpp"
21 
22 #include "Teuchos_Reader.hpp"
23 
24 #ifdef HAVE_TEUCHOSPARAMETERLIST_YAMLCPP
25 #include "yaml-cpp/yaml.h"
26 #endif // HAVE_TEUCHOSPARAMETERLIST_YAMLCPP
27 #include "Teuchos_YAML.hpp"
28 
29 
30 namespace Teuchos {
31 
32 #ifdef HAVE_TEUCHOSPARAMETERLIST_YAMLCPP
33 
34 /* see https://github.com/jbeder/yaml-cpp/issues/261
35  there are times when we want to insist that a parameter
36  value be interpreted as a string despite it being parseable
37  as a number.
38  the standard way to do this in YAML is to put the number in quotes,
39  i.e. '1e-3' instead of 1e-3.
40  however, the usual YAML::Node::as<T> system doesn't respect quoting
41  when trying to cast to numbers.
42  so, this is our own version of as<T>, called quoted_as<T>, using
43  the Tag workaround suggested in the issue linked above. */
44 
45 template <typename T>
46 struct QuotedAs {
47  static T eval(::YAML::Node const& node) {
48  // this "!" tag apparently denotes that the value was quoted
49  if (node.Tag() == "!") {
50  throw std::runtime_error("quoted_as from quoted string to number");
51  }
52  return node.as<T>();
53  }
54 };
55 
56 template <>
57 struct QuotedAs<std::string> {
58  // only a cast to string will succeed if quoted
59  static std::string eval(::YAML::Node const& node) { return node.as<std::string>(); }
60 };
61 
62 template <typename T>
63 static T quoted_as(::YAML::Node const& node) { return QuotedAs<T>::eval(node); }
64 
65 template<typename T>
66 Teuchos::Array<T> getYamlArray(const ::YAML::Node& node)
67 {
69  for(::YAML::const_iterator it = node.begin(); it != node.end(); it++)
70  {
71  arr.push_back(quoted_as<T>(*it));
72  }
73  return arr;
74 }
75 
76 bool checkYamlTwoDArrayIsRagged(const ::YAML::Node& node)
77 {
78  bool ragged = false;
79  for (::YAML::const_iterator it = node.begin(); it != node.end(); ++it)
80  {
81  if (it->size() != node.begin()->size())
82  {
83  ragged=true;
84  }
85  }
86  return ragged;
87 }
88 
89 template<typename T> Teuchos::TwoDArray<T> getYamlTwoDArray(const ::YAML::Node& node)
90 {
93  arr.resizeRows(node.size());
94  arr.resizeCols(node.begin()->size());
95  i = 0;
96  for (::YAML::const_iterator rit = node.begin(); rit != node.end(); ++rit)
97  {
98  j = 0;
99  for (::YAML::const_iterator cit = rit->begin(); cit != rit->end(); ++cit)
100  {
101  arr(i, j) = quoted_as<T>(*cit);
102  ++j;
103  }
104  ++i;
105  }
106  return arr;
107 }
108 
109 int getYamlArrayDim(const ::YAML::Node& node)
110 {
111  int ndim = 0;
112  if (node.Type() == ::YAML::NodeType::Sequence)
113  {
114  ++ndim;
115  if (node.begin()->Type() == ::YAML::NodeType::Sequence)
116  {
117  ++ndim;
118  if (node.begin()->begin()->Type() == ::YAML::NodeType::Sequence)
119  {
120  ++ndim;
121  }
122  }
123  }
124  return ndim;
125 }
126 
127 template <typename tarray_t, typename T>
128 tarray_t getYaml2DRaggedArray(::YAML::Node node, int ndim, std::string key)
129 {
130  tarray_t base_arr;
131  if (ndim == 2) {
132  Teuchos::Array<T> sub_arr;
133  for (::YAML::const_iterator it1 = node.begin(); it1 != node.end(); ++it1) {
134  for (::YAML::const_iterator it2 = it1->begin(); it2 != it1->end(); ++it2) {
135  sub_arr.push_back(quoted_as<T>(*it2));
136  } base_arr.push_back(sub_arr);
137  sub_arr.clear();
138  }
139  }
140  else
141  {
142  throw YamlSequenceError(std::string("MDArray \"" + key + "\" must have dim 2."));
143  }
144  return base_arr;
145 }
146 
147 // This handles the requested use case of a list of 2D arrays; further nesting would require a getYaml4DArray() function,
148 // which could be straightforwardly implemented along the lines of the below function.
149 
150 template <typename tarray_t, typename T>
151 tarray_t getYaml3DArray(::YAML::Node node, int ndim, std::string key)
152 {
153  tarray_t base_arr;
154  if (ndim == 3) {
156  Teuchos::Array<T> sub_sub_arr;
157  for (::YAML::const_iterator it1 = node.begin(); it1 != node.end(); ++it1) {
158  for (::YAML::const_iterator it2 = it1->begin(); it2 != it1->end(); ++it2) {
159  for (::YAML::const_iterator it3 = it2->begin(); it3 != it2->end(); ++it3) {
160  sub_sub_arr.push_back(quoted_as<T>(*it3));
161  } sub_arr.push_back(sub_sub_arr);
162  sub_sub_arr.clear();
163  } base_arr.push_back(sub_arr);
164  sub_arr.clear();
165  }
166  }
167  else
168  {
169  throw YamlSequenceError(std::string("MDArray \"" + key + "\" must have dim 3."));
170  }
171  return base_arr;
172 }
173 
174 template <typename T>
175 void safe_set_entry(ParameterList& list, std::string const& name_in, T const& entry_in) {
176  TEUCHOS_TEST_FOR_EXCEPTION(list.isParameter(name_in), ParserFail,
177  "Parameter \"" << name_in << "\" already exists in list \"" << list.name() << "\"\n");
178  list.set(name_in, entry_in);
179 }
180 #endif // HAVE_TEUCHOSPARAMETERLIST_YAMLCPP
181 
182 std::string remove_trailing_whitespace(std::string const& in) {
183  std::size_t new_end = 0;
184  for (std::size_t ri = 0; ri < in.size(); ++ri) {
185  std::size_t i = in.size() - 1 - ri;
186  if (in[i] != ' ' && in[i] != '\t') {
187  new_end = i + 1;
188  break;
189  }
190  }
191  return in.substr(0, new_end);
192 }
193 
194 std::string remove_trailing_whitespace_and_newlines(std::string const& in) {
195  std::size_t new_end = 0;
196  for (std::size_t ri = 0; ri < in.size(); ++ri) {
197  std::size_t i = in.size() - 1 - ri;
198  if (in[i] != ' ' && in[i] != '\t' && in[i] != '\n' && in[i] != '\r') {
199  new_end = i + 1;
200  break;
201  }
202  }
203  return in.substr(0, new_end);
204 }
205 
206 template <typename T>
207 bool is_parseable_as(std::string const& text) {
208  std::istringstream ss(text);
209  T val;
210  ss >> std::noskipws >> val;
211  return ss.eof() && !ss.fail();
212 }
213 
214 template <>
215 bool is_parseable_as<int>(std::string const& text) {
216  std::istringstream ss(text);
217  using LL = long long;
218  LL val;
219  ss >> std::noskipws >> val;
220  return ss.eof() && !ss.fail() &&
221  (val >= LL(std::numeric_limits<int>::min())) &&
222  (val <= LL(std::numeric_limits<int>::max()));
223 }
224 
225 template <typename T>
226 T parse_as(std::string const& text) {
227  std::istringstream ss(text);
228  T value;
229  ss >> value;
230  return value;
231 }
232 
233 // http://en.cppreference.com/w/cpp/string/byte/tolower
234 static char my_tolower(char ch)
235 {
236  return std::tolower(static_cast<unsigned char>(ch));
237 }
238 
239 // http://en.cppreference.com/w/cpp/string/byte/isdigit
240 static bool my_isdigit(char ch)
241 {
242  return std::isdigit(static_cast<unsigned char>(ch));
243 }
244 
245 template <>
246 bool is_parseable_as<bool>(std::string const& text) {
247  std::string lower;
248  for (std::size_t i = 0; i < text.size(); ++i) {
249  lower.push_back(my_tolower(text[i]));
250  }
251  return lower == "true" || lower == "yes" ||
252  lower == "false" || lower == "no";
253 }
254 
255 template <>
256 bool parse_as<bool>(std::string const& text) {
257  std::string lower;
258  for (std::size_t i = 0; i < text.size(); ++i) {
259  lower.push_back(my_tolower(text[i]));
260  }
261  return !(lower == "false" || lower == "no");
262 }
263 
264 struct PLPair {
265  std::string key;
266  ParameterEntry value;
267 };
268 
269 struct Scalar {
270  enum Source {
271  RAW,
272  DQUOTED,
273  SQUOTED,
274  BLOCK
275  };
276  /* order matters, a higher type should be convertible to a lower type */
277  enum Type {
278  STRING = 0,
279  DOUBLE = 1,
280  LONG_LONG = 2,
281  INT = 3,
282  BOOL = 4
283  };
284  int source;
285  int tag_type;
286  std::string text;
287  int infer_type() const {
288  if (tag_type != -1) {
289  return tag_type;
290  }
291  if (source != RAW) {
292  return STRING;
293  }
294  if (is_parseable_as<bool>(text)) {
295  return BOOL;
296  }
297  if (is_parseable_as<int>(text)) {
298  return INT;
299  }
300  if (is_parseable_as<long long>(text)) {
301  return LONG_LONG;
302  }
303  if (is_parseable_as<double>(text)) {
304  return DOUBLE;
305  }
306  return STRING;
307  }
308 };
309 
310 bool operator==(Scalar const&, Scalar const&) { return false; }
311 std::ostream& operator<<(std::ostream& os, Scalar const&) { return os; }
312 
313 void safe_set_entry(ParameterList& list, std::string const& name_in, ParameterEntry const& entry_in) {
314  TEUCHOS_TEST_FOR_EXCEPTION(list.isParameter(name_in), ParserFail,
315  "Parameter \"" << name_in << "\" already exists in list \"" << list.name() << "\"\n");
316  list.setEntry(name_in, entry_in);
317 }
318 
319 namespace YAMLParameterList {
320 
321 class Reader : public Teuchos::Reader {
322  public:
323  Reader():Teuchos::Reader(Teuchos::YAML::ask_reader_tables()) {}
324  virtual ~Reader() {}
325  protected:
326  enum {
327  TRIM_NORMAL,
328  TRIM_DASH
329  };
330  virtual void at_shift(any& result_any, int token, std::string& text) {
331  using std::swap;
332  switch (token) {
333  case Teuchos::YAML::TOK_NEWLINE: {
334  std::string& result = make_any_ref<std::string>(result_any);
335  swap(result, text);
336  break;
337  }
338  case Teuchos::YAML::TOK_SPACE:
339  case Teuchos::YAML::TOK_OTHER: {
340  result_any = text.at(0);
341  break;
342  }
343  }
344  }
345  virtual void at_reduce(any& result_any, int prod, std::vector<any>& rhs) {
346  using std::swap;
347  switch (prod) {
348  case Teuchos::YAML::PROD_DOC:
349  case Teuchos::YAML::PROD_DOC2: {
350  std::size_t offset = prod == Teuchos::YAML::PROD_DOC2 ? 1 : 0;
351  TEUCHOS_ASSERT(rhs.at(offset).has_value());
352  swap(result_any, rhs.at(offset));
353  TEUCHOS_ASSERT(result_any.type() == typeid(ParameterList));
354  break;
355  }
356  case Teuchos::YAML::PROD_TOP_BMAP: {
357  TEUCHOS_ASSERT(rhs.at(0).has_value());
358  TEUCHOS_ASSERT(rhs.at(0).type() == typeid(PLPair));
359  PLPair& pair = any_ref_cast<PLPair>(rhs.at(0));
360  any& pair_rhs_any = pair.value.getAny(/* set isUsed = */ false);
361  result_any = pair_rhs_any;
362  break;
363  }
364  case Teuchos::YAML::PROD_TOP_FIRST: {
365  if (rhs.at(0).type() == typeid(ParameterList)) {
366  swap(result_any, rhs.at(0));
367  }
368  break;
369  }
370  case Teuchos::YAML::PROD_TOP_NEXT: {
371  if (rhs.at(1).type() == typeid(ParameterList)) {
372  TEUCHOS_TEST_FOR_EXCEPTION(rhs.at(0).has_value(), ParserFail,
373  "Can't specify multiple top-level ParameterLists in one YAML file!\n");
374  swap(result_any, rhs.at(1));
375  } else {
376  swap(result_any, rhs.at(0));
377  }
378  break;
379  }
380  case Teuchos::YAML::PROD_BMAP_FIRST:
381  case Teuchos::YAML::PROD_FMAP_FIRST: {
382  TEUCHOS_ASSERT(rhs.at(0).type() == typeid(PLPair));
383  map_first_item(result_any, rhs.at(0));
384  TEUCHOS_ASSERT(result_any.type() == typeid(ParameterList));
385  break;
386  }
387  case Teuchos::YAML::PROD_BMAP_NEXT: {
388  map_next_item(result_any, rhs.at(0), rhs.at(1));
389  break;
390  }
391  case Teuchos::YAML::PROD_FMAP_NEXT: {
392  map_next_item(result_any, rhs.at(0), rhs.at(3));
393  break;
394  }
395  case Teuchos::YAML::PROD_BMAP_SCALAR:
396  case Teuchos::YAML::PROD_FMAP_SCALAR:
397  case Teuchos::YAML::PROD_FMAP_FMAP:
398  case Teuchos::YAML::PROD_FMAP_FSEQ: {
399  int scalar_type = interpret_tag(rhs.at(3));
400  map_item(result_any, rhs.at(0), rhs.at(4), scalar_type);
401  break;
402  }
403  case Teuchos::YAML::PROD_BMAP_BSCALAR: {
404  map_item(result_any, rhs.at(0), rhs.at(3), Scalar::STRING);
405  break;
406  }
407  case Teuchos::YAML::PROD_BMAP_BVALUE: {
408  map_item(result_any, rhs.at(0), rhs.at(4));
409  break;
410  }
411  case Teuchos::YAML::PROD_BVALUE_EMPTY: {
412  result_any = ParameterList();
413  break;
414  }
415  case Teuchos::YAML::PROD_BVALUE_BMAP:
416  case Teuchos::YAML::PROD_BVALUE_BSEQ: {
417  swap(result_any, rhs.at(1));
418  break;
419  }
420  case Teuchos::YAML::PROD_BMAP_FMAP: {
421  map_item(result_any, rhs.at(0), rhs.at(4));
422  break;
423  }
424  case Teuchos::YAML::PROD_BMAP_FSEQ: {
425  TEUCHOS_ASSERT(rhs.at(4).type() == typeid(Array<Scalar>) ||
426  rhs.at(4).type() == typeid(Array<Array<Scalar>>));
427  int scalar_type = interpret_tag(rhs.at(3));
428  map_item(result_any, rhs.at(0), rhs.at(4), scalar_type);
429  break;
430  }
431  case Teuchos::YAML::PROD_BSEQ_FIRST: {
432  seq_first_item(result_any, rhs.at(0));
433  break;
434  }
435  case Teuchos::YAML::PROD_BSEQ_NEXT: {
436  seq_next_item(result_any, rhs.at(0), rhs.at(1));
437  break;
438  }
439  case Teuchos::YAML::PROD_BSEQ_SCALAR: {
440  swap(result_any, rhs.at(3));
441  Scalar& scalar = any_ref_cast<Scalar>(result_any);
442  scalar.tag_type = interpret_tag(rhs.at(2));
443  break;
444  }
445  case Teuchos::YAML::PROD_BSEQ_BSCALAR: {
446  swap(result_any, rhs.at(2));
447  break;
448  }
449  case Teuchos::YAML::PROD_BSEQ_BMAP:
450  case Teuchos::YAML::PROD_BSEQ_BMAP_TRAIL:
451  case Teuchos::YAML::PROD_BSEQ_FMAP: {
452  throw ParserFail("Can't interpret a map inside a sequence as a Teuchos Parameter");
453  }
454  case Teuchos::YAML::PROD_BSEQ_BSEQ: {
455  swap(result_any, rhs.at(3));
456  break;
457  }
458  case Teuchos::YAML::PROD_BSEQ_BSEQ_TRAIL: {
459  swap(result_any, rhs.at(4));
460  break;
461  }
462  case Teuchos::YAML::PROD_BSEQ_FSEQ: {
463  swap(result_any, rhs.at(3));
464  break;
465  }
466  case Teuchos::YAML::PROD_FMAP: {
467  swap(result_any, rhs.at(2));
468  break;
469  }
470  case Teuchos::YAML::PROD_FMAP_EMPTY: {
471  result_any = ParameterList();
472  break;
473  }
474  case Teuchos::YAML::PROD_FSEQ: {
475  swap(result_any, rhs.at(2));
476  TEUCHOS_ASSERT(result_any.type() == typeid(Array<Scalar>) ||
477  result_any.type() == typeid(Array<Array<Scalar>>));
478  break;
479  }
480  case Teuchos::YAML::PROD_FSEQ_EMPTY: {
481  result_any = Array<Scalar>();
482  break;
483  }
484  case Teuchos::YAML::PROD_FSEQ_FIRST: {
485  seq_first_item(result_any, rhs.at(0));
486  break;
487  }
488  case Teuchos::YAML::PROD_FSEQ_NEXT: {
489  seq_next_item(result_any, rhs.at(0), rhs.at(3));
490  break;
491  }
492  case Teuchos::YAML::PROD_FSEQ_SCALAR: {
493  swap(result_any, rhs.at(1));
494  Scalar& scalar = any_ref_cast<Scalar>(result_any);
495  scalar.tag_type = interpret_tag(rhs.at(0));
496  break;
497  }
498  case Teuchos::YAML::PROD_FSEQ_FSEQ:
499  case Teuchos::YAML::PROD_FSEQ_FMAP: {
500  swap(result_any, rhs.at(1));
501  break;
502  }
503  case Teuchos::YAML::PROD_SCALAR_QUOTED:
504  case Teuchos::YAML::PROD_MAP_SCALAR_QUOTED: {
505  swap(result_any, rhs.at(0));
506  break;
507  }
508  case Teuchos::YAML::PROD_SCALAR_RAW:
509  case Teuchos::YAML::PROD_MAP_SCALAR_RAW: {
510  Scalar& scalar = make_any_ref<Scalar>(result_any);
511  TEUCHOS_ASSERT(rhs.at(0).has_value());
512  scalar.text = any_ref_cast<std::string>(rhs.at(0));
513  scalar.text += any_ref_cast<std::string>(rhs.at(1));
514  if (prod == Teuchos::YAML::PROD_MAP_SCALAR_RAW) {
515  scalar.text += any_ref_cast<std::string>(rhs.at(2));
516  }
517  scalar.text = remove_trailing_whitespace(scalar.text);
518  scalar.source = Scalar::RAW;
519  scalar.tag_type = -1;
520  break;
521  }
522  case Teuchos::YAML::PROD_SCALAR_HEAD_OTHER:
523  case Teuchos::YAML::PROD_SCALAR_HEAD_DOT:
524  case Teuchos::YAML::PROD_SCALAR_HEAD_DASH:
525  case Teuchos::YAML::PROD_SCALAR_HEAD_DOT_DOT: {
526  std::size_t offset;
527  if (prod == Teuchos::YAML::PROD_SCALAR_HEAD_OTHER) offset = 0;
528  else if (prod == Teuchos::YAML::PROD_SCALAR_HEAD_DOT_DOT) offset = 2;
529  else offset = 1;
530  char second = any_cast<char>(rhs.at(offset));
531  std::string& result = make_any_ref<std::string>(result_any);
532  if (prod == Teuchos::YAML::PROD_SCALAR_HEAD_DOT) result += '.';
533  else if (prod == Teuchos::YAML::PROD_SCALAR_HEAD_DASH) result += '-';
534  else if (prod == Teuchos::YAML::PROD_SCALAR_HEAD_DOT_DOT) result += "..";
535  result += second;
536  break;
537  }
538  case Teuchos::YAML::PROD_SCALAR_DQUOTED:
539  case Teuchos::YAML::PROD_SCALAR_SQUOTED: {
540  std::string& first = any_ref_cast<std::string>(rhs.at(1));
541  std::string& rest = any_ref_cast<std::string>(rhs.at(2));
542  Scalar& scalar = make_any_ref<Scalar>(result_any);
543  scalar.text += first;
544  scalar.text += rest;
545  if (prod == Teuchos::YAML::PROD_SCALAR_DQUOTED) {
546  scalar.source = Scalar::DQUOTED;
547  } else if (prod == Teuchos::YAML::PROD_SCALAR_SQUOTED) {
548  scalar.source = Scalar::SQUOTED;
549  }
550  scalar.tag_type = -1;
551  break;
552  }
553  case Teuchos::YAML::PROD_MAP_SCALAR_ESCAPED_EMPTY: {
554  result_any = std::string();
555  break;
556  }
557  case Teuchos::YAML::PROD_MAP_SCALAR_ESCAPED_NEXT: {
558  swap(result_any, rhs.at(0));
559  std::string& str = any_ref_cast<std::string>(result_any);
560  str += ',';
561  str += any_ref_cast<std::string>(rhs.at(2));
562  break;
563  }
564  case Teuchos::YAML::PROD_TAG: {
565  swap(result_any, rhs.at(2));
566  break;
567  }
568  case Teuchos::YAML::PROD_BSCALAR: {
569  std::size_t parent_indent_level =
570  this->symbol_indentation_stack.at(
571  this->symbol_indentation_stack.size() - 5);
572  std::string& header = any_ref_cast<std::string>(rhs.at(0));
573  std::string& leading_empties_or_comments =
574  any_ref_cast<std::string>(rhs.at(2));
575  std::string& rest = any_ref_cast<std::string>(rhs.at(4));
576  std::string& content = make_any_ref<std::string>(result_any);
577  std::string comment;
578  handle_block_scalar(
579  parent_indent_level,
580  header, leading_empties_or_comments, rest,
581  content, comment);
582  break;
583  }
584  case Teuchos::YAML::PROD_BSCALAR_FIRST: {
585  swap(result_any, rhs.at(0));
586  break;
587  }
588  // all these cases reduce to concatenating two strings
589  case Teuchos::YAML::PROD_BSCALAR_NEXT:
590  case Teuchos::YAML::PROD_BSCALAR_LINE:
591  case Teuchos::YAML::PROD_DESCAPE_NEXT:
592  case Teuchos::YAML::PROD_SESCAPE_NEXT: {
593  swap(result_any, rhs.at(0));
594  std::string& str = any_ref_cast<std::string>(result_any);
595  str += any_ref_cast<std::string>(rhs.at(1));
596  break;
597  }
598  case Teuchos::YAML::PROD_BSCALAR_INDENT: {
599  swap(result_any, rhs.at(1));
600  break;
601  }
602  case Teuchos::YAML::PROD_BSCALAR_HEADER_LITERAL:
603  case Teuchos::YAML::PROD_BSCALAR_HEADER_FOLDED: {
604  std::string& result = make_any_ref<std::string>(result_any);
605  if (prod == Teuchos::YAML::PROD_BSCALAR_HEADER_LITERAL) {
606  result += "|";
607  } else {
608  result += ">";
609  }
610  std::string& rest = any_ref_cast<std::string>(rhs.at(1));
611  result += rest;
612  break;
613  }
614  case Teuchos::YAML::PROD_DESCAPE: {
615  std::string& str = make_any_ref<std::string>(result_any);
616  std::string& rest = any_ref_cast<std::string>(rhs.at(2));
617  str += any_cast<char>(rhs.at(1));
618  str += rest;
619  break;
620  }
621  case Teuchos::YAML::PROD_SESCAPE: {
622  std::string& str = make_any_ref<std::string>(result_any);
623  std::string& rest = any_ref_cast<std::string>(rhs.at(2));
624  str += '\'';
625  str += rest;
626  break;
627  }
628  case Teuchos::YAML::PROD_OTHER_FIRST:
629  case Teuchos::YAML::PROD_SPACE_PLUS_FIRST: {
630  std::string& str = make_any_ref<std::string>(result_any);
631  str.push_back(any_cast<char>(rhs.at(0)));
632  break;
633  }
634  case Teuchos::YAML::PROD_SCALAR_TAIL_SPACE:
635  case Teuchos::YAML::PROD_SCALAR_TAIL_OTHER:
636  case Teuchos::YAML::PROD_DESCAPED_DQUOTED:
637  case Teuchos::YAML::PROD_DQUOTED_COMMON:
638  case Teuchos::YAML::PROD_SQUOTED_COMMON:
639  case Teuchos::YAML::PROD_ANY_COMMON:
640  case Teuchos::YAML::PROD_COMMON_SPACE:
641  case Teuchos::YAML::PROD_COMMON_OTHER:
642  case Teuchos::YAML::PROD_BSCALAR_HEAD_OTHER: {
643  swap(result_any, rhs.at(0));
644  break;
645  }
646  // all these cases reduce to appending a character
647  case Teuchos::YAML::PROD_DQUOTED_NEXT:
648  case Teuchos::YAML::PROD_SQUOTED_NEXT:
649  case Teuchos::YAML::PROD_ANY_NEXT:
650  case Teuchos::YAML::PROD_SCALAR_TAIL_NEXT:
651  case Teuchos::YAML::PROD_SPACE_STAR_NEXT:
652  case Teuchos::YAML::PROD_SPACE_PLUS_NEXT:
653  case Teuchos::YAML::PROD_BSCALAR_HEAD_NEXT: {
654  TEUCHOS_TEST_FOR_EXCEPTION(!rhs.at(0).has_value(), ParserFail,
655  "leading characters in " << prod << ": any was empty\n");
656  swap(result_any, rhs.at(0));
657  std::string& str = any_ref_cast<std::string>(result_any);
658  str += any_cast<char>(rhs.at(1));
659  break;
660  }
661  case Teuchos::YAML::PROD_DQUOTED_EMPTY:
662  case Teuchos::YAML::PROD_SQUOTED_EMPTY:
663  case Teuchos::YAML::PROD_ANY_EMPTY:
664  case Teuchos::YAML::PROD_DESCAPE_EMPTY:
665  case Teuchos::YAML::PROD_SESCAPE_EMPTY:
666  case Teuchos::YAML::PROD_SCALAR_TAIL_EMPTY:
667  case Teuchos::YAML::PROD_SPACE_STAR_EMPTY:
668  case Teuchos::YAML::PROD_BSCALAR_HEAD_EMPTY: {
669  result_any = std::string();
670  break;
671  }
672  case Teuchos::YAML::PROD_DESCAPED_DQUOT:
673  case Teuchos::YAML::PROD_SQUOTED_DQUOT:
674  case Teuchos::YAML::PROD_ANY_DQUOT: {
675  result_any = '"';
676  break;
677  }
678  case Teuchos::YAML::PROD_DESCAPED_SLASH:
679  case Teuchos::YAML::PROD_SQUOTED_SLASH:
680  case Teuchos::YAML::PROD_ANY_SLASH: {
681  result_any = '\\';
682  break;
683  }
684  case Teuchos::YAML::PROD_SCALAR_TAIL_SQUOT:
685  case Teuchos::YAML::PROD_DQUOTED_SQUOT:
686  case Teuchos::YAML::PROD_ANY_SQUOT: {
687  result_any = '\'';
688  break;
689  }
690  case Teuchos::YAML::PROD_COMMON_COLON: {
691  result_any = ':';
692  break;
693  }
694  case Teuchos::YAML::PROD_SCALAR_TAIL_DOT:
695  case Teuchos::YAML::PROD_COMMON_DOT: {
696  result_any = '.';
697  break;
698  }
699  case Teuchos::YAML::PROD_SCALAR_TAIL_DASH:
700  case Teuchos::YAML::PROD_COMMON_DASH:
701  case Teuchos::YAML::PROD_BSCALAR_HEAD_DASH: {
702  result_any = '-';
703  break;
704  }
705  case Teuchos::YAML::PROD_COMMON_PIPE: {
706  result_any = '|';
707  break;
708  }
709  case Teuchos::YAML::PROD_COMMON_LSQUARE: {
710  result_any = '[';
711  break;
712  }
713  case Teuchos::YAML::PROD_COMMON_RSQUARE: {
714  result_any = ']';
715  break;
716  }
717  case Teuchos::YAML::PROD_COMMON_LCURLY: {
718  result_any = '{';
719  break;
720  }
721  case Teuchos::YAML::PROD_COMMON_RCURLY: {
722  result_any = '}';
723  break;
724  }
725  case Teuchos::YAML::PROD_COMMON_RANGLE: {
726  result_any = '>';
727  break;
728  }
729  case Teuchos::YAML::PROD_COMMON_COMMA: {
730  result_any = ',';
731  break;
732  }
733  case Teuchos::YAML::PROD_COMMON_PERCENT: {
734  result_any = '%';
735  break;
736  }
737  case Teuchos::YAML::PROD_COMMON_EXCL: {
738  result_any = '!';
739  break;
740  }
741  }
742  }
743  void map_first_item(any& result_any, any& first_item) {
744  ParameterList& list = make_any_ref<ParameterList>(result_any);
745  TEUCHOS_ASSERT(first_item.has_value());
746  PLPair& pair = any_ref_cast<PLPair>(first_item);
747  safe_set_entry(list, pair.key, pair.value);
748  }
749  void map_next_item(any& result_any, any& items, any& next_item) {
750  using std::swap;
751  swap(result_any, items);
752  ParameterList& list = any_ref_cast<ParameterList>(result_any);
753  PLPair& pair = any_ref_cast<PLPair>(next_item);
754  safe_set_entry(list, pair.key, pair.value);
755  }
756  void map_item(any& result_any, any& key_any, any& value_any, int scalar_type = -1) {
757  using std::swap;
758  PLPair& result = make_any_ref<PLPair>(result_any);
759  {
760  std::string& key = any_ref_cast<Scalar>(key_any).text;
761  swap(result.key, key);
762  }
763  resolve_map_value(value_any, scalar_type);
764  if (value_any.type() == typeid(bool)) {
765  bool value = any_cast<bool>(value_any);
766  result.value = ParameterEntry(value);
767  } else if (value_any.type() == typeid(int)) {
768  int value = any_cast<int>(value_any);
769  result.value = ParameterEntry(value);
770  } else if (value_any.type() == typeid(long long)) {
771  long long value = any_cast<long long>(value_any);
772  result.value = ParameterEntry(value);
773  } else if (value_any.type() == typeid(double)) {
774  double value = any_cast<double>(value_any);
775  result.value = ParameterEntry(value);
776  } else if (value_any.type() == typeid(std::string)) {
777  std::string& value = any_ref_cast<std::string >(value_any);
778  result.value = ParameterEntry(value);
779  } else if (value_any.type() == typeid(Array<int>)) {
780  Array<int>& value = any_ref_cast<Array<int> >(value_any);
781  result.value = ParameterEntry(value);
782  } else if (value_any.type() == typeid(Array<long long>)) {
783  Array<long long>& value = any_ref_cast<Array<long long> >(value_any);
784  result.value = ParameterEntry(value);
785  } else if (value_any.type() == typeid(Array<double>)) {
786  Array<double>& value = any_ref_cast<Array<double> >(value_any);
787  result.value = ParameterEntry(value);
788  } else if (value_any.type() == typeid(Array<std::string>)) {
789  Array<std::string>& value = any_ref_cast<Array<std::string> >(value_any);
790  result.value = ParameterEntry(value);
791  } else if (value_any.type() == typeid(TwoDArray<int>)) {
792  TwoDArray<int>& value = any_ref_cast<TwoDArray<int> >(value_any);
793  result.value = ParameterEntry(value);
794  } else if (value_any.type() == typeid(TwoDArray<long long>)) {
795  TwoDArray<long long>& value = any_ref_cast<TwoDArray<long long> >(value_any);
796  result.value = ParameterEntry(value);
797  } else if (value_any.type() == typeid(TwoDArray<double>)) {
798  TwoDArray<double>& value = any_ref_cast<TwoDArray<double> >(value_any);
799  result.value = ParameterEntry(value);
800  } else if (value_any.type() == typeid(TwoDArray<std::string>)) {
801  TwoDArray<std::string>& value = any_ref_cast<TwoDArray<std::string> >(value_any);
802  result.value = ParameterEntry(value);
803  } else if (value_any.type() == typeid(ParameterList)) {
804  ParameterList& value = any_ref_cast<ParameterList>(value_any);
805  ParameterList& result_pl = result.value.setList();
806  swap(result_pl, value);
807  result_pl.setName(result.key);
808  } else {
809  std::string msg = "unexpected YAML map value type ";
810  msg += value_any.type().name();
811  msg += " for key \"";
812  msg += result.key;
813  msg += "\"\n";
814  throw ParserFail(msg);
815  }
816  }
817  void resolve_map_value(any& value_any, int scalar_type = -1) const {
818  if (value_any.type() == typeid(Scalar)) {
819  Scalar& scalar_value = any_ref_cast<Scalar>(value_any);
820  if (scalar_type == -1) {
821  scalar_type = scalar_value.infer_type();
822  }
823  if (scalar_type == Scalar::BOOL) {
824  value_any = parse_as<bool>(scalar_value.text);
825  } else if (scalar_type == Scalar::INT) {
826  value_any = parse_as<int>(scalar_value.text);
827  } else if (scalar_type == Scalar::LONG_LONG) {
828  value_any = parse_as<long long>(scalar_value.text);
829  } else if (scalar_type == Scalar::DOUBLE) {
830  value_any = parse_as<double>(scalar_value.text);
831  } else {
832  value_any = scalar_value.text;
833  }
834  } else if (value_any.type() == typeid(Array<Scalar>)) {
835  Array<Scalar>& scalars = any_ref_cast<Array<Scalar> >(value_any);
836  if (scalar_type == -1) {
837  if (scalars.size() == 0) {
838  throw ParserFail("implicitly typed arrays can't be empty\n"
839  "(need to determine their element type)\n");
840  }
841  /* Teuchos::Array uses std::vector but doesn't account for std::vector<bool>,
842  so it can't store bools */
843  scalar_type = Scalar::INT;
844  for (Teuchos_Ordinal i = 0; i < scalars.size(); ++i) {
845  scalar_type = std::min(scalar_type, scalars[i].infer_type());
846  }
847  }
848  if (scalar_type == Scalar::INT) {
849  Array<int> result(scalars.size());
850  for (Teuchos_Ordinal i = 0; i < scalars.size(); ++i) {
851  result[i] = parse_as<int>(scalars[i].text);
852  }
853  value_any = result;
854  } else if (scalar_type == Scalar::LONG_LONG) {
855  Array<long long> result(scalars.size());
856  for (Teuchos_Ordinal i = 0; i < scalars.size(); ++i) {
857  result[i] = parse_as<long long>(scalars[i].text);
858  }
859  value_any = result;
860  } else if (scalar_type == Scalar::DOUBLE) {
861  Array<double> result(scalars.size());
862  for (Teuchos_Ordinal i = 0; i < scalars.size(); ++i) {
863  result[i] = parse_as<double>(scalars[i].text);
864  }
865  value_any = result;
866  } else if (scalar_type == Scalar::STRING) {
867  Array<std::string> result(scalars.size());
868  for (Teuchos_Ordinal i = 0; i < scalars.size(); ++i) {
869  result[i] = scalars[i].text;
870  }
871  value_any = result;
872  }
873  } else if (value_any.type() == typeid(Array<Array<Scalar>>)) {
874  Array<Array<Scalar>>& scalars = any_ref_cast<Array<Array<Scalar>> >(value_any);
875  if (scalar_type == -1) {
876  if (scalars.size() == 0) {
877  throw ParserFail("implicitly typed 2D arrays can't be empty\n"
878  "(need to determine their element type)\n");
879  }
880  /* Teuchos::Array uses std::vector but doesn't account for std::vector<bool>,
881  so it can't store bools */
882  scalar_type = Scalar::INT;
883  for (Teuchos_Ordinal i = 0; i < scalars.size(); ++i) {
884  if (scalars[0].size() == 0) {
885  throw ParserFail("implicitly typed 2D arrays can't have empty rows\n"
886  "(need to determine their element type)\n");
887  }
888  if (scalars[i].size() != scalars[0].size()) {
889  throw ParserFail("2D array: sub-arrays are different sizes");
890  }
891  for (Teuchos_Ordinal j = 0; j < scalars[i].size(); ++j) {
892  int item_type = scalars[i][j].infer_type();
893  scalar_type = std::min(scalar_type, item_type);
894  }
895  }
896  }
897  if (scalar_type == Scalar::INT) {
898  TwoDArray<int> result(scalars.size(), scalars[0].size());
899  for (Teuchos_Ordinal i = 0; i < scalars.size(); ++i) {
900  for (Teuchos_Ordinal j = 0; j < scalars[0].size(); ++j) {
901  result(i, j) = parse_as<int>(scalars[i][j].text);
902  }
903  }
904  value_any = result;
905  } else if (scalar_type == Scalar::LONG_LONG) {
906  TwoDArray<long long> result(scalars.size(), scalars[0].size());
907  for (Teuchos_Ordinal i = 0; i < scalars.size(); ++i) {
908  for (Teuchos_Ordinal j = 0; j < scalars[0].size(); ++j) {
909  result(i, j) = parse_as<long long>(scalars[i][j].text);
910  }
911  }
912  value_any = result;
913  } else if (scalar_type == Scalar::DOUBLE) {
914  TwoDArray<double> result(scalars.size(), scalars[0].size());
915  for (Teuchos_Ordinal i = 0; i < scalars.size(); ++i) {
916  for (Teuchos_Ordinal j = 0; j < scalars[0].size(); ++j) {
917  result(i, j) = parse_as<double>(scalars[i][j].text);
918  }
919  }
920  value_any = result;
921  } else if (scalar_type == Scalar::STRING) {
922  TwoDArray<std::string> result(scalars.size(), scalars[0].size());
923  for (Teuchos_Ordinal i = 0; i < scalars.size(); ++i) {
924  for (Teuchos_Ordinal j = 0; j < scalars[0].size(); ++j) {
925  result(i, j) = scalars[i][j].text;
926  }
927  }
928  value_any = result;
929  }
930  }
931  }
932  int interpret_tag(any& tag_any) {
933  if (tag_any.type() != typeid(std::string)) return -1;
934  std::string& text = any_ref_cast<std::string>(tag_any);
935  if (text.find("bool") != std::string::npos) return Scalar::BOOL;
936  else if (text.find("int") != std::string::npos) return Scalar::INT;
937  else if (text.find("double") != std::string::npos) return Scalar::DOUBLE;
938  else if (text.find("string") != std::string::npos) return Scalar::STRING;
939  else {
940  std::string msg = "Unable to parse type tag \"";
941  msg += text;
942  msg += "\"\n";
943  throw ParserFail(msg);
944  }
945  }
946  void seq_first_item(any& result_any, any& first_any) {
947  using std::swap;
948  if (first_any.type() == typeid(Scalar)) {
949  Array<Scalar>& a = make_any_ref<Array<Scalar> >(result_any);
950  Scalar& v = any_ref_cast<Scalar>(first_any);
951  a.push_back(Scalar());
952  swap(a.back(), v);
953  } else if (first_any.type() == typeid(Array<Scalar>)) {
954  Array<Array<Scalar>>& a = make_any_ref<Array<Array<Scalar>> >(result_any);
955  Array<Scalar>& v = any_ref_cast<Array<Scalar> >(first_any);
956  a.push_back(Array<Scalar>());
957  swap(a.back(), v);
958  } else {
959  throw Teuchos::ParserFail(
960  "bug in YAMLParameterList::Reader: unexpected type for first sequence item");
961  }
962  }
963  void seq_next_item(any& result_any, any& items, any& next_item) {
964  using std::swap;
965  swap(result_any, items);
966  if (result_any.type() == typeid(Array<Scalar>)) {
967  Array<Scalar>& a = any_ref_cast<Array<Scalar> >(result_any);
968  Scalar& val = any_ref_cast<Scalar>(next_item);
969  a.push_back(Scalar());
970  swap(a.back(), val);
971  } else if (result_any.type() == typeid(Array<Array<Scalar>>)) {
972  Array<Array<Scalar>>& a = any_ref_cast<Array<Array<Scalar>> >(result_any);
973  Array<Scalar>& v = any_ref_cast<Array<Scalar> >(next_item);
974  a.push_back(Array<Scalar>());
975  swap(a.back(), v);
976  } else {
977  throw Teuchos::ParserFail(
978  "bug in YAMLParameterList::Reader: unexpected type for next sequence item");
979  }
980  }
981  /* block scalars are a super complicated mess, this function handles that mess */
982  void handle_block_scalar(
983  std::size_t parent_indent_level,
984  std::string const& header,
985  std::string const& leading_empties_or_comments,
986  std::string const& rest,
987  std::string& content,
988  std::string& comment) {
989  /* read the header, resulting in: block style, chomping indicator, and indentation indicator */
990  char style;
991  char chomping_indicator;
992  std::size_t indentation_indicator = 0;
993  style = header[0];
994  std::stringstream ss(header.substr(1,std::string::npos));
995  if (header.size() > 1 && my_isdigit(header[1])) {
996  ss >> indentation_indicator;
997  // indentation indicator is given as a relative number, but we need it in absolute terms
998  indentation_indicator += parent_indent_level;
999  }
1000  if (!(ss >> chomping_indicator)) chomping_indicator = '\0';
1001  /* get information about newlines, indentation level, and comment from
1002  the leading_empties_or_comments string */
1003  std::size_t first_newline = leading_empties_or_comments.find_first_of("\r\n");
1004  std::string newline;
1005  if (first_newline > 0 && leading_empties_or_comments[first_newline - 1] == '\r') {
1006  newline = "\r\n";
1007  } else {
1008  newline = "\n";
1009  }
1010  std::size_t keep_beg = first_newline + 1 - newline.size();
1011  if (leading_empties_or_comments[0] == '#') {
1012  comment = leading_empties_or_comments.substr(1, keep_beg);
1013  }
1014  // according to the YAML spec, a tab is content, not indentation
1015  std::size_t content_beg = leading_empties_or_comments.find_first_not_of("\r\n ");
1016  if (content_beg == std::string::npos) content_beg = leading_empties_or_comments.size();
1017  std::size_t newline_before_content = leading_empties_or_comments.rfind("\n", content_beg);
1018  std::size_t num_indent_spaces = (content_beg - newline_before_content) - 1;
1019  /* indentation indicator overrides the derived level of indentation, in case the
1020  user wants to keep some of that indentation as content */
1021  if (indentation_indicator > 0) {
1022  TEUCHOS_TEST_FOR_EXCEPTION(num_indent_spaces < indentation_indicator,
1024  "Indentation indicator " << indentation_indicator << " > leading spaces " << num_indent_spaces);
1025  num_indent_spaces = indentation_indicator;
1026  }
1027  /* prepend the content from the leading_empties_or_comments to the rest */
1028  content = leading_empties_or_comments.substr(keep_beg, std::string::npos);
1029  content += rest;
1030  /* per Trilinos issue #2090, there can be trailing comments after the block
1031  scalar which are less indented than it, but they will be included in the
1032  final NEWLINE token.
1033  this code removes all contiguous trailing lines which are less indented
1034  than the content.
1035  */
1036  while (true) {
1037  auto last_newline = content.find_last_of("\n", content.size() - 2);
1038  if (last_newline == std::string::npos) break;
1039  std::size_t num_spaces = 0;
1040  for (auto ispace = last_newline + 1;
1041  ispace < content.size() && content[ispace] == ' ';
1042  ++ispace) {
1043  ++num_spaces;
1044  }
1045  if (num_spaces >= num_indent_spaces) break;
1046  content.erase(content.begin() + last_newline + 1, content.end());
1047  }
1048  /* remove both indentation and newlines as dictated by header information */
1049  std::size_t unindent_pos = 0;
1050  while (true) {
1051  std::size_t next_newline = content.find_first_of("\n", unindent_pos);
1052  if (next_newline == std::string::npos) break;
1053  std::size_t start_cut = next_newline + 1;
1054  /* folding block scalars remove newlines */
1055  if (style == '>') start_cut -= newline.size();
1056  std::size_t end_cut = next_newline + 1;
1057  /* the actual amount of indentation in the content varies, start by
1058  marking it all for removal */
1059  while (end_cut < content.size() && content[end_cut] == ' ') {
1060  ++end_cut;
1061  }
1062  /* but don't remove more than the actual indent number */
1063  end_cut = std::min(next_newline + 1 + num_indent_spaces, end_cut);
1064  /* cut this (newline?)+indentation out of the content */
1065  content = content.substr(0, start_cut) +
1066  content.substr(end_cut, std::string::npos);
1067  unindent_pos = start_cut;
1068  }
1069  if (chomping_indicator != '+') {
1070  content = remove_trailing_whitespace_and_newlines(content);
1071  if (chomping_indicator != '-') content += newline;
1072  }
1073  if (style == '|') {
1074  // if not already, remove the leading newline
1075  content = content.substr(newline.size(), std::string::npos);
1076  }
1077  }
1078 };
1079 
1080 } // end namespace YAMLParameterList
1081 
1082 /* Helper functions */
1083 
1084 void updateParametersFromYamlFile(const std::string& yamlFileName,
1085  const Teuchos::Ptr<Teuchos::ParameterList>& paramList)
1086 {
1087  //load the YAML file in as a new param list
1088  Teuchos::RCP<Teuchos::ParameterList> updated = YAMLParameterList::parseYamlFile(yamlFileName);
1089  if (paramList->name() == "ANONYMOUS") {
1090  paramList->setName(updated->name());
1091  }
1092  //now update the original list (overwriting values with same key)
1093  paramList->setParameters(*updated);
1094 }
1095 
1096 void updateParametersFromYamlCString(const char* const data,
1097  const Teuchos::Ptr<Teuchos::ParameterList>& paramList,
1098  bool overwrite)
1099 {
1100  Teuchos::RCP<Teuchos::ParameterList> updated = YAMLParameterList::parseYamlText(data, "CString");
1101  if(overwrite)
1102  {
1103  if (paramList->name() == "ANONYMOUS") {
1104  paramList->setName(updated->name());
1105  }
1106  paramList->setParameters(*updated);
1107  }
1108  else
1109  {
1110  paramList->setParametersNotAlreadySet(*updated);
1111  }
1112 }
1113 
1114 void updateParametersFromYamlString(const std::string& yamlData,
1115  const Teuchos::Ptr<Teuchos::ParameterList>& paramList,
1116  bool overwrite,
1117  const std::string& name)
1118 {
1119  Teuchos::RCP<Teuchos::ParameterList> updated = YAMLParameterList::parseYamlText(yamlData, name);
1120  if(overwrite)
1121  {
1122  if (paramList->name() == "ANONYMOUS") {
1123  paramList->setName(updated->name());
1124  }
1125  paramList->setParameters(*updated);
1126  }
1127  else
1128  {
1129  paramList->setParametersNotAlreadySet(*updated);
1130  }
1131 }
1132 
1133 Teuchos::RCP<Teuchos::ParameterList> getParametersFromYamlFile(const std::string& yamlFileName)
1134 {
1135  return YAMLParameterList::parseYamlFile(yamlFileName);
1136 }
1137 
1138 Teuchos::RCP<Teuchos::ParameterList> getParametersFromYamlString(const std::string& yamlStr)
1139 {
1140  std::stringstream ss(yamlStr);
1141  return YAMLParameterList::parseYamlStream(ss);
1142 }
1143 
1144 void writeParameterListToYamlOStream(
1145  const ParameterList &paramList,
1146  std::ostream &yamlOut
1147  )
1148 {
1149  YAMLParameterList::writeYamlStream(yamlOut, paramList);
1150 }
1151 
1152 void writeParameterListToYamlFile(
1153  const ParameterList &paramList,
1154  const std::string &yamlFileName
1155  )
1156 {
1157  YAMLParameterList::writeYamlFile(yamlFileName, paramList);
1158 }
1159 
1160 std::string convertXmlToYaml(const std::string& xmlFileName)
1161 {
1162  //load the parameter list from xml
1163  Teuchos::RCP<Teuchos::ParameterList> toConvert = Teuchos::getParametersFromXmlFile(xmlFileName);
1164  //replace the file extension ".xml" with ".yaml", or append it if there was no extension
1165  std::string yamlFileName;
1166  if(xmlFileName.find(".xml") == std::string::npos)
1167  {
1168  yamlFileName = xmlFileName + ".yaml";
1169  }
1170  else
1171  {
1172  yamlFileName = xmlFileName.substr(0, xmlFileName.length() - 4) + ".yaml";
1173  }
1174  YAMLParameterList::writeYamlFile(yamlFileName, *toConvert);
1175  return yamlFileName;
1176 }
1177 
1178 void convertXmlToYaml(const std::string& xmlFileName, const std::string& yamlFileName)
1179 {
1180  Teuchos::RCP<Teuchos::ParameterList> toConvert = Teuchos::getParametersFromXmlFile(xmlFileName);
1181  YAMLParameterList::writeYamlFile(yamlFileName, *toConvert);
1182 }
1183 
1184 void convertXmlToYaml(std::istream& xmlStream, std::ostream& yamlStream)
1185 {
1186  //read xmlStream into a string until EOF
1187  std::istreambuf_iterator<char> begin(xmlStream);
1188  std::istreambuf_iterator<char> end;
1189  std::string xmlString(begin, end);
1190  Teuchos::RCP<Teuchos::ParameterList> toConvert = Teuchos::getParametersFromXmlString(xmlString);
1191  YAMLParameterList::writeYamlStream(yamlStream, *toConvert);
1192 }
1193 
1194 namespace YAMLParameterList
1195 {
1196 
1197 Teuchos::RCP<Teuchos::ParameterList> parseYamlText(const std::string& text, const std::string& name)
1198 {
1199 #ifdef HAVE_TEUCHOSPARAMETERLIST_YAMLCPP
1200  auto yaml_input = ::YAML::LoadAll(text); // std::vector<::YAML::Node>
1201  return readParams(yaml_input);
1202 #else
1203  any result;
1204  Teuchos::YAMLParameterList::Reader reader;
1205  reader.read_string(result, text, name);
1206  ParameterList& pl = any_ref_cast<ParameterList>(result);
1207  return Teuchos::rcp(new ParameterList(pl));
1208 #endif // HAVE_TEUCHOSPARAMETERLIST_YAMLCPP
1209 }
1210 
1211 Teuchos::RCP<Teuchos::ParameterList> parseYamlFile(const std::string& yamlFile)
1212 {
1213 #ifdef HAVE_TEUCHOSPARAMETERLIST_YAMLCPP
1214  auto yaml_input = ::YAML::LoadAllFromFile(yamlFile);
1215  return readParams(yaml_input);
1216 #else
1217  any result;
1218  Teuchos::YAMLParameterList::Reader reader;
1219  reader.read_file(result, yamlFile);
1220  ParameterList& pl = any_ref_cast<ParameterList>(result);
1221  return Teuchos::rcp(new ParameterList(pl));
1222 #endif // HAVE_TEUCHOSPARAMETERLIST_YAMLCPP
1223 }
1224 
1225 Teuchos::RCP<Teuchos::ParameterList> parseYamlStream(std::istream& yaml)
1226 {
1227 #ifdef HAVE_TEUCHOSPARAMETERLIST_YAMLCPP
1228  auto yaml_input = ::YAML::LoadAll(yaml);
1229  return readParams(yaml_input);
1230 #else
1231  any result;
1232  Teuchos::YAMLParameterList::Reader reader;
1233  reader.read_stream(result, yaml, "parseYamlStream");
1234  ParameterList& pl = any_ref_cast<ParameterList>(result);
1235  return Teuchos::rcp(new ParameterList(pl));
1236 #endif // HAVE_TEUCHOSPARAMETERLIST_YAMLCPP
1237 }
1238 
1239 // The following three functions (readParams, processMapNode, and processKeyValueNode)
1240 // were previously removed from Trilinos in PR 1779 (Teuchos: use Parser, not yaml-cpp, to read YAML PL).
1241 
1242 #ifdef HAVE_TEUCHOSPARAMETERLIST_YAMLCPP
1243 
1244 Teuchos::RCP<Teuchos::ParameterList> readParams(std::vector<::YAML::Node>& lists)
1245 {
1246  Teuchos::RCP<Teuchos::ParameterList> pl = rcp(new Teuchos::ParameterList); //pl is the root ParameterList to be returned
1247  // If there is exactly one element in "lists", assume it is the anonymous top-level parameter list
1248  // If there are more than one, place them all in the anonymous top-level list
1249  for(size_t i = 0; i < lists.size(); i++)
1250  {
1251  processMapNode(lists[i], *pl, true);
1252  }
1253  return pl;
1254 }
1255 
1256 void processMapNode(const ::YAML::Node& node, Teuchos::ParameterList& parent, bool topLevel)
1257 {
1258  if (node.Type() != ::YAML::NodeType::Map)
1259  {
1260  throw YamlStructureError("All top-level elements of the YAML file must be maps.");
1261  }
1262  if (topLevel)
1263  {
1264  parent.setName(node.begin()->first.as<std::string>());
1265  processMapNode(node.begin()->second, parent);
1266  }
1267  else
1268  {
1269  for (::YAML::const_iterator i = node.begin(); i != node.end(); i++)
1270  {
1271  // make sure the key type is a string
1272  if(i->first.Type() != ::YAML::NodeType::Scalar)
1273  {
1274  throw YamlKeyError("Keys must be YAML scalars (int, double, or string)");
1275  }
1276  // if this conversion fails and throws for any reason (shouldn't), let the caller handle it
1277  const std::string key = quoted_as<std::string>(i->first);
1278  processKeyValueNode(key, i->second, parent, topLevel);
1279  }
1280  }
1281 }
1282 
1283 void processKeyValueNode(const std::string& key, const ::YAML::Node& node, Teuchos::ParameterList& parent, bool topLevel)
1284 {
1285  // node (value) type can be a map (for nested param lists),
1286  // a scalar (int, double, string), or a sequence of doubles (vector<double>)
1287  if(node.Type() == ::YAML::NodeType::Scalar)
1288  {
1289  try
1290  {
1291  safe_set_entry<int>(parent, key, quoted_as<int>(node));
1292  }
1293  catch(...)
1294  {
1295  try
1296  {
1297  safe_set_entry<long long>(parent, key, quoted_as<long long>(node));
1298  }
1299  catch(...)
1300  {
1301  try
1302  {
1303  safe_set_entry<double>(parent, key, quoted_as<double>(node));
1304  }
1305  catch(...)
1306  {
1307  try
1308  {
1309  bool raw_bool = quoted_as<bool>(node);
1310 
1311  /* yaml-cpp parses ON/OFF as a bool, but the in-house parser does not.
1312  To preserve backwards compatibility, make sure the string passes
1313  the in-house parser's is_parseable_as<bool> function (which protects
1314  against the ON/OFF case).
1315  Otherwise, a failure is observed in YAML_ConvertFromXML unit test.*/
1316 
1317  std::string raw_string = quoted_as<std::string>(node);
1318  if (is_parseable_as<bool>(raw_string))
1319  {
1320  safe_set_entry<bool>(parent, key, raw_bool);
1321  }
1322  else
1323  {
1324  safe_set_entry<std::string>(parent, key, raw_string);
1325  }
1326  }
1327  catch(...)
1328  {
1329  safe_set_entry<std::string>(parent, key, quoted_as<std::string>(node));
1330  }
1331  }
1332  }
1333  }
1334  }
1335  else if(node.Type() == ::YAML::NodeType::Map)
1336  {
1337  if(topLevel)
1338  {
1339  processMapNode(node, parent);
1340  }
1341  else
1342  {
1343  Teuchos::ParameterList& sublist = parent.sublist(key);
1344  processMapNode(node, sublist);
1345  }
1346  }
1347  else if(node.Type() == ::YAML::NodeType::Sequence)
1348  {
1349  int ndim = getYamlArrayDim(node);
1350  if (ndim == 1)
1351  {
1352  ::YAML::Node const& first_value = *(node.begin());
1353  try
1354  {
1355  quoted_as<int>(first_value);
1356  safe_set_entry<Teuchos::Array<int>>(parent, key, getYamlArray<int>(node));
1357  }
1358  catch(...)
1359  {
1360  try
1361  {
1362  quoted_as<double>(first_value);
1363  safe_set_entry<Teuchos::Array<double>>(parent, key, getYamlArray<double>(node));
1364  }
1365  catch(...)
1366  {
1367  try
1368  {
1369  quoted_as<std::string>(first_value);
1370  safe_set_entry<Teuchos::Array<std::string>>(parent, key, getYamlArray<std::string>(node));
1371  }
1372  catch(...)
1373  {
1374  throw YamlSequenceError(std::string("Array \"") + key + "\" must contain int, double, bool or string");
1375  }
1376  }
1377  }
1378  }
1379  else if (ndim == 2)
1380  {
1381  bool is_ragged = checkYamlTwoDArrayIsRagged(node);
1382  ::YAML::Node const& first_value = *(node.begin()->begin());
1383  try
1384  {
1385  quoted_as<int>(first_value);
1386  using arr_t = Teuchos::Array<Teuchos::Array<int>>;
1387  if (is_ragged) {
1388  safe_set_entry<arr_t>(parent, key, getYaml2DRaggedArray<arr_t, int>(node, ndim, key));
1389  } else {
1390  safe_set_entry<Teuchos::TwoDArray<int>>(parent, key, getYamlTwoDArray<int>(node));
1391  }
1392  }
1393  catch(...)
1394  {
1395  try
1396  {
1397  quoted_as<double>(first_value);
1399  if (is_ragged) {
1400  safe_set_entry<arr_t>(parent, key, getYaml2DRaggedArray<arr_t, double>(node, ndim, key));
1401  } else {
1402  safe_set_entry<Teuchos::TwoDArray<double>>(parent, key, getYamlTwoDArray<double>(node));
1403  }
1404  }
1405  catch(...)
1406  {
1407  try
1408  {
1409  quoted_as<std::string>(first_value);
1411  if (is_ragged) {
1412  safe_set_entry<arr_t>(parent, key, getYaml2DRaggedArray<arr_t, std::string>(node, ndim, key));
1413  } else {
1414  safe_set_entry<Teuchos::TwoDArray<std::string>>(parent, key, getYamlTwoDArray<std::string>(node));
1415  }
1416  }
1417  catch(...)
1418  {
1419  throw YamlSequenceError(std::string("TwoDArray \"") + key + "\" must contain int, double, bool or string");
1420  }
1421  }
1422  }
1423  }
1424  else if (ndim == 3)
1425  {
1426  ::YAML::Node const& first_value = *(node.begin()->begin()->begin());
1427  try
1428  {
1429  quoted_as<int>(first_value);
1431  safe_set_entry<arr_t>(parent, key, getYaml3DArray<arr_t, int>(node, ndim, key));
1432  }
1433  catch(...)
1434  {
1435  try
1436  {
1437  quoted_as<double>(first_value);
1439  safe_set_entry<arr_t>(parent, key, getYaml3DArray<arr_t, double>(node, ndim, key));
1440 
1441  }
1442  catch(...)
1443  {
1444  try
1445  {
1446  quoted_as<std::string>(first_value);
1448  safe_set_entry<arr_t>(parent, key, getYaml3DArray<arr_t, std::string>(node, ndim, key));
1449 
1450  }
1451  catch(...)
1452  {
1453  throw YamlSequenceError(std::string("3DArray \"") + key + "\" must contain int, double, bool or string");
1454  }
1455  }
1456  }
1457  }
1458  }
1459  else if(node.Type() == ::YAML::NodeType::Null)
1460  {
1461  // treat NULL as empty sublist (not an error)
1462  parent.sublist(key);
1463  }
1464  else
1465  {
1466  // Undefined
1467  throw YamlUndefinedNodeError("Value type in a key-value pair must be one of: int, double, string, array, sublist.");
1468  }
1469 }
1470 
1471 #endif // HAVE_TEUCHOSPARAMETERLIST_YAMLCPP
1472 
1473 void writeYamlStream(std::ostream& yaml, const Teuchos::ParameterList& pl)
1474 {
1475  // warn the user if floats/doubles with integer values will be printed incorrectly
1476  std::ios_base::fmtflags flags = yaml.flags();
1477  // make temporary stringstream to test flags
1478  std::ostringstream testStream;
1479  testStream.flags(flags);
1480  double testVal = 1;
1481  testStream << testVal;
1482  bool popFlags = false;
1483  if(testStream.str() == "1")
1484  {
1485  // must add showpoint to flags while writing yaml
1486  // this will always disambiguate (double) n and n, even if stream precision is 0
1487  // prints as "n.0000" where the number of trailing zeros is the stream precision
1488  // note: in YAML, "5." is a double but not an int
1489  std::cout << "Warning: yaml stream format flags would confuse double with integer value with int.\n";
1490  std::cout << "Setting std::ios::showpoint on the stream to fix this (will restore flags when done)\n";
1491  std::ios_base::fmtflags flagsCopy = flags;
1492  flagsCopy |= std::ios::showpoint;
1493  popFlags = true;
1494  }
1495  yaml << "%YAML 1.1\n---\n";
1496  yaml << pl.name() << ':';
1497  if(pl.numParams() == 0)
1498  {
1499  yaml << " { }\n";
1500  }
1501  else
1502  {
1503  writeParameterList(pl, yaml, 2);
1504  }
1505  yaml << "...\n";
1506  // restore flags
1507  if(popFlags)
1508  {
1509  yaml.flags(flags);
1510  }
1511 }
1512 
1513 void writeYamlFile(const std::string& yamlFile, const Teuchos::ParameterList& pl)
1514 {
1515  std::ofstream yaml(yamlFile.c_str());
1516  /* set default floating-point style:
1517  1. 17 decimal places to ensure the value remains the same
1518  2. scientific: this prevents floating-point values that happen
1519  to be integers from being printed as integers, because YAML
1520  will then read that value back typed as an integer.
1521  */
1522  yaml << std::scientific << std::setprecision(17);
1523  writeYamlStream(yaml, pl);
1524 }
1525 
1526 void writeParameterList(const Teuchos::ParameterList& pl, std::ostream& yaml, int indentLevel)
1527 {
1528  if(pl.begin() == pl.end())
1529  {
1530  yaml << "{ }\n";
1531  }
1532  else
1533  {
1534  yaml << '\n';
1535  for(PLIter it = pl.begin(); it != pl.end(); it++)
1536  {
1537  writeParameter(pl.name(it), pl.entry(it), yaml, indentLevel);
1538  }
1539  }
1540 }
1541 
1542 template <typename T>
1543 struct YamlWrite {
1544  static void write(T const& x, std::ostream& stream) {
1545  stream << x;
1546  }
1547 };
1548 
1549 template <>
1550 struct YamlWrite<double> {
1551  static void write(double const& x, std::ostream& stream) {
1552  generalWriteDouble(x, stream);
1553  }
1554 };
1555 
1556 template <>
1557 struct YamlWrite<std::string> {
1558  static void write(std::string const& x, std::ostream& stream) {
1559  generalWriteString(x, stream);
1560  }
1561 };
1562 
1563 template <typename T>
1564 void writeYamlTwoDArray(Teuchos::TwoDArray<T> const& arr, std::ostream& stream)
1565 {
1566  typename Teuchos::TwoDArray<T>::size_type i, j;
1567  stream << '[';
1568  for (i = 0; i < arr.getNumRows(); ++i)
1569  {
1570  if (i) stream << ", ";
1571  stream << '[';
1572  for (j = 0; j < arr.getNumCols(); ++j)
1573  {
1574  if (j) stream << ", ";
1575  YamlWrite<T>::write(arr(i, j), stream);
1576  }
1577  stream << ']';
1578  }
1579  stream << ']';
1580 }
1581 
1582 void writeParameter(const std::string& paramName, const Teuchos::ParameterEntry& entry, std::ostream& yaml, int indentLevel)
1583 {
1584  for(int i = 0; i < indentLevel; i++)
1585  {
1586  yaml << ' ';
1587  }
1588  generalWriteString(paramName, yaml);
1589  yaml << ": ";
1590  if(entry.isList())
1591  {
1592  writeParameterList(Teuchos::getValue<Teuchos::ParameterList>(entry), yaml, indentLevel + 2);
1593  return;
1594  }
1595  else if(entry.isArray())
1596  {
1597  yaml << '[';
1598  if(entry.isType<Teuchos::Array<int> >())
1599  {
1600  Teuchos::Array<int>& arr = Teuchos::getValue<Teuchos::Array<int> >(entry);
1601  for(int i = 0; i < arr.size(); i++)
1602  {
1603  yaml << arr[i];
1604  if(i != arr.size() - 1)
1605  yaml << ", ";
1606  }
1607  }
1608  if(entry.isType<Teuchos::Array<long long> >())
1609  {
1610  Teuchos::Array<long long>& arr = Teuchos::getValue<Teuchos::Array<long long> >(entry);
1611  for(int i = 0; i < arr.size(); i++)
1612  {
1613  yaml << arr[i];
1614  if(i != arr.size() - 1)
1615  yaml << ", ";
1616  }
1617  }
1618  else if(entry.isType<Teuchos::Array<double> >())
1619  {
1620  Teuchos::Array<double>& arr = Teuchos::getValue<Teuchos::Array<double> >(entry);
1621  for(int i = 0; i < arr.size(); i++)
1622  {
1623  generalWriteDouble(arr[i], yaml);
1624  if(i != arr.size() - 1)
1625  yaml << ", ";
1626  }
1627  }
1628  else if(entry.isType<Teuchos::Array<std::string> >())
1629  {
1630  Teuchos::Array<std::string>& arr = Teuchos::getValue<Teuchos::Array<std::string> >(entry);
1631  for(int i = 0; i < arr.size(); i++)
1632  {
1633  generalWriteString(arr[i], yaml);
1634  if(i != arr.size() - 1)
1635  yaml << ", ";
1636  }
1637  }
1638  yaml << ']';
1639  }
1640  else if(entry.isTwoDArray())
1641  {
1642  if(entry.isType<Teuchos::TwoDArray<int> >())
1643  {
1644  writeYamlTwoDArray<int>(
1645  Teuchos::getValue<Teuchos::TwoDArray<int> >(entry), yaml);
1646  }
1648  {
1649  writeYamlTwoDArray<long long>(
1650  Teuchos::getValue<Teuchos::TwoDArray<long long> >(entry), yaml);
1651  }
1652  else if(entry.isType<Teuchos::TwoDArray<double> >())
1653  {
1654  writeYamlTwoDArray<double>(
1655  Teuchos::getValue<Teuchos::TwoDArray<double> >(entry), yaml);
1656  }
1657  else if(entry.isType<Teuchos::TwoDArray<std::string> >())
1658  {
1659  writeYamlTwoDArray<std::string>(
1660  Teuchos::getValue<Teuchos::TwoDArray<std::string> >(entry), yaml);
1661  }
1662  }
1663  else if(entry.isType<int>())
1664  {
1665  yaml << Teuchos::getValue<int>(entry);
1666  }
1667  else if(entry.isType<long long>())
1668  {
1669  yaml << Teuchos::getValue<long long>(entry);
1670  }
1671  else if(entry.isType<double>())
1672  {
1673  generalWriteDouble(Teuchos::getValue<double>(entry), yaml);
1674  }
1675  else if(entry.isType<std::string>())
1676  {
1677  std::string& str = Teuchos::getValue<std::string>(entry);
1678  if(strchr(str.c_str(), '\n'))
1679  {
1680  yaml << "|";
1681  // if the content has leading spaces, automatic indentation
1682  // detection would fail, in which case we must emit
1683  // an indentation indicator
1684  std::size_t first_non_newline_pos = str.find_first_not_of("\r\n");
1685  if (first_non_newline_pos != std::string::npos &&
1686  str[first_non_newline_pos] == ' ') {
1687  yaml << "2";
1688  }
1689  if (str[str.size() - 1] != '\n') yaml << "-";
1690  yaml << "\n";
1691  //for each line, apply indent then print the line verbatim
1692  size_t index = 0;
1693  while(true)
1694  {
1695  size_t next = str.find('\n', index);
1696  for(int i = 0; i < indentLevel + 2; i++)
1697  {
1698  yaml << ' ';
1699  }
1700  if(next == std::string::npos)
1701  {
1702  yaml << str.substr(index, std::string::npos);
1703  break;
1704  }
1705  else
1706  {
1707  yaml << str.substr(index, next - index) << '\n';
1708  }
1709  index = next + 1;
1710  }
1711  }
1712  else
1713  {
1714  generalWriteString(str, yaml);
1715  }
1716  }
1717  else if(entry.isType<bool>())
1718  {
1719  yaml << (Teuchos::getValue<bool>(entry) ? "true" : "false");
1720  }
1721  yaml << '\n';
1722 }
1723 
1724 void generalWriteString(const std::string& str, std::ostream& yaml)
1725 {
1726  // default to single quoting
1727  if(stringNeedsQuotes(str))
1728  {
1729  yaml << '\'';
1730  for (std::size_t i = 0; i < str.size(); ++i) {
1731  if (str[i] == '\'') yaml << "''";
1732  else yaml << str[i];
1733  }
1734  yaml << '\'';
1735  }
1736  else
1737  {
1738  yaml << str;
1739  }
1740 }
1741 
1742 void generalWriteDouble(double d, std::ostream& yaml)
1743 {
1744  yaml << d;
1745 }
1746 
1747 static bool containsSpecialCharacters(std::string const& s) {
1748  char const* const control_chars = ":'{}[],&*#?|<>=!%@\\";
1749  return s.find_first_of(control_chars) != std::string::npos;
1750 }
1751 
1752 bool stringNeedsQuotes(const std::string& s)
1753 {
1754  return s.empty() ||
1755  containsSpecialCharacters(s) ||
1756  is_parseable_as<bool>(s) ||
1757  is_parseable_as<int>(s) ||
1758  is_parseable_as<long long>(s) ||
1759  is_parseable_as<double>(s);
1760 }
1761 
1762 } //namespace YAMLParameterList
1763 
1764 } //namespace Teuchos
A thin wrapper around the Teuchos Array class that allows for 2 dimensional arrays.
const std::string & name() const
The name of this ParameterList.
Reader(ReaderTablesPtr tables_in)
Constructor: accepts an RCP to ReaderTables.
ConstIterator end() const
An iterator pointing beyond the last entry.
Functions to convert between ParameterList and YAML.
This object is held as the &quot;value&quot; in the Teuchos::ParameterList std::map.
#define TEUCHOS_TEST_FOR_EXCEPTION(throw_exception_test, Exception, msg)
Macro for throwing an exception with breakpointing to ease debugging.
bool isArray() const
Test if the type of data being contained is a Teuchos::Array.
Tries to create LALR(1) parser tables for a given grammar.
bool isType() const
Test the type of the data being contained.
Ordinal numParams() const
Get the number of stored parameters.
A TeuchosParser Language for a subset of YAML.
Simple helper functions that make it easy to read and write XML to and from a parameterlist.
A thin wrapper around the Array class which causes it to be interpreted as a 2D Array.
The main class for users to read text using TeuchosParser.
TEUCHOS_DEPRECATED RCP< T > rcp(T *p, Dealloc_T dealloc, bool owns_mem)
Deprecated.
size_type getNumCols() const
returns the number of columns in the TwoDArray.
void resizeCols(size_type numberOfCols)
Changes the number of rows in the matrix.
void resizeRows(size_type numberOfRows)
Changes the number of rows in the matrix.
ConstIterator begin() const
An iterator pointing to the first entry.
bool isList() const
Return whether or not the value itself is a list.
A list of parameters of arbitrary type.
ParameterList & setParameters(const ParameterList &source)
const ParameterEntry & entry(ConstIterator i) const
Access to ParameterEntry (i.e., returns i-&gt;second)
size_type getNumRows() const
returns the number of rows in the TwoDArray.
void push_back(const value_type &x)
ParameterList & setParametersNotAlreadySet(const ParameterList &source)
bool isTwoDArray() const
Test if the type of data being contained is a Teuchos::TwoDArray.
size_type size() const
ParameterList & sublist(const std::string &name, bool mustAlreadyExist=false, const std::string &docString="")
Creates an empty sublist and returns a reference to the sublist name. If the list already exists...
ParameterList & setName(const std::string &name)
Set the name of *this list.
#define TEUCHOS_ASSERT(assertion_test)
This macro is throws when an assert fails.
Simple helper functions that make it easy to read and write Yaml to and from a parameterlist.
Simple wrapper class for raw pointers to single objects where no persisting relationship exists...
Replacement for std::vector that is compatible with the Teuchos Memory Management classes...
Declares Teuchos::Reader.