Teuchos - Trilinos Tools Package Version of the Day
Loading...
Searching...
No Matches
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
30namespace 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
45template <typename T>
46struct 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
56template <>
57struct 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
62template <typename T>
63static T quoted_as(::YAML::Node const& node) { return QuotedAs<T>::eval(node); }
64
65template<typename T>
66Teuchos::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
76bool 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
89template<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
109int 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
127template <typename tarray_t, typename T>
128tarray_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
150template <typename tarray_t, typename T>
151tarray_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
174template <typename T>
175void 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
182std::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
194std::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
206template <typename T>
207bool 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
214template <>
215bool 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
225template <typename T>
226T 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
234static 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
240static bool my_isdigit(char ch)
241{
242 return std::isdigit(static_cast<unsigned char>(ch));
243}
244
245template <>
246bool 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
255template <>
256bool 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
264struct PLPair {
265 std::string key;
266 ParameterEntry value;
267};
268
269struct 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
310bool operator==(Scalar const&, Scalar const&) { return false; }
311std::ostream& operator<<(std::ostream& os, Scalar const&) { return os; }
312
313void 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
319namespace YAMLParameterList {
320
321class 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 {
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 {
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
1084void updateParametersFromYamlFile(const std::string& yamlFileName,
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
1096void updateParametersFromYamlCString(const char* const data,
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
1114void updateParametersFromYamlString(const std::string& yamlData,
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
1133Teuchos::RCP<Teuchos::ParameterList> getParametersFromYamlFile(const std::string& yamlFileName)
1134{
1135 return YAMLParameterList::parseYamlFile(yamlFileName);
1136}
1137
1138Teuchos::RCP<Teuchos::ParameterList> getParametersFromYamlString(const std::string& yamlStr)
1139{
1140 std::stringstream ss(yamlStr);
1141 return YAMLParameterList::parseYamlStream(ss);
1142}
1143
1144void writeParameterListToYamlOStream(
1145 const ParameterList &paramList,
1146 std::ostream &yamlOut
1147 )
1148{
1149 YAMLParameterList::writeYamlStream(yamlOut, paramList);
1150}
1151
1152void writeParameterListToYamlFile(
1153 const ParameterList &paramList,
1154 const std::string &yamlFileName
1155 )
1156{
1157 YAMLParameterList::writeYamlFile(yamlFileName, paramList);
1158}
1159
1160std::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
1178void 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
1184void 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
1194namespace YAMLParameterList
1195{
1196
1197Teuchos::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
1211Teuchos::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
1225Teuchos::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
1244Teuchos::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
1256void 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
1283void 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);
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
1473void 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
1513void 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
1526void 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
1542template <typename T>
1543struct YamlWrite {
1544 static void write(T const& x, std::ostream& stream) {
1545 stream << x;
1546 }
1547};
1548
1549template <>
1550struct YamlWrite<double> {
1551 static void write(double const& x, std::ostream& stream) {
1552 generalWriteDouble(x, stream);
1553 }
1554};
1555
1556template <>
1557struct YamlWrite<std::string> {
1558 static void write(std::string const& x, std::ostream& stream) {
1559 generalWriteString(x, stream);
1560 }
1561};
1562
1563template <typename T>
1564void writeYamlTwoDArray(Teuchos::TwoDArray<T> const& arr, std::ostream& stream)
1565{
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
1582void 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 {
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 {
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 {
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 {
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
1724void 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
1742void generalWriteDouble(double d, std::ostream& yaml)
1743{
1744 yaml << d;
1745}
1746
1747static 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
1752bool 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
Declares Teuchos::Reader.
A thin wrapper around the Teuchos Array class that allows for 2 dimensional arrays.
Simple helper functions that make it easy to read and write XML to and from a parameterlist.
A TeuchosParser Language for a subset of YAML.
Simple helper functions that make it easy to read and write Yaml to and from a parameterlist.
Functions to convert between ParameterList and YAML.
This object is held as the "value" in the Teuchos::ParameterList std::map.
bool isType() const
Test the type of the data being contained.
bool isTwoDArray() const
Test if the type of data being contained is a Teuchos::TwoDArray.
bool isArray() const
Test if the type of data being contained is a Teuchos::Array.
bool isList() const
Return whether or not the value itself is a list.
A list of parameters of arbitrary type.
const ParameterEntry & entry(ConstIterator i) const
Access to ParameterEntry (i.e., returns i->second)
ConstIterator end() const
An iterator pointing beyond the last entry.
Ordinal numParams() const
Get the number of stored parameters.
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,...
const std::string & name() const
The name of this ParameterList.
ConstIterator begin() const
An iterator pointing to the first entry.
ParameterList & setName(const std::string &name)
Set the name of *this list.
Tries to create LALR(1) parser tables for a given grammar.
Smart reference counting pointer class for automatic garbage collection.
The main class for users to read text using TeuchosParser.
Reader(ReaderTablesPtr tables_in)
Constructor: accepts an RCP to ReaderTables.
#define TEUCHOS_ASSERT(assertion_test)
This macro is throws when an assert fails.
#define TEUCHOS_TEST_FOR_EXCEPTION(throw_exception_test, Exception, msg)
Macro for throwing an exception with breakpointing to ease debugging.
The Teuchos namespace contains all of the classes, structs and enums used by Teuchos,...
TEUCHOS_DEPRECATED RCP< T > rcp(T *p, Dealloc_T dealloc, bool owns_mem)
Deprecated.