8 #include "cantera/base/yaml.h"
10 #ifdef CT_USE_DEMANGLE
11 #include <boost/core/demangle.hpp>
14 #include <boost/algorithm/string.hpp>
17 #include <unordered_set>
19 namespace ba = boost::algorithm;
23 std::mutex yaml_cache_mutex;
25 bool isFloat(
const std::string& val)
29 std::string str = ba::trim_copy(val);
37 if (ch ==
'+' || ch ==
'-') {
39 if (str.size() == 1) {
43 for (
size_t i = istart; i < str.size(); i++) {
46 }
else if (ch ==
'.') {
54 }
else if (ch ==
'e' || ch ==
'E') {
56 if (numExp > 1 || i == str.size() - 1) {
60 if (ch ==
'+' || ch ==
'-') {
61 if (i + 1 == str.size() - 1) {
73 bool isInt(
const std::string& val)
75 std::string str = ba::trim_copy(val);
81 if (ch ==
'+' || ch ==
'-') {
83 if (str.size() == 1) {
87 for (
size_t i = istart; i < str.size(); i++) {
88 if (!isdigit(str[i])) {
95 bool isBool(
const std::string& val) {
96 std::string str = ba::trim_copy(val);
97 return (val ==
"true" || val ==
"True" || val ==
"false" || val ==
"False");
100 enum class Type : char {
110 Type operator|(Type lhs, Type rhs)
112 return Type(
static_cast<char>(lhs) |
static_cast<char>(rhs));
115 Type elementTypes(
const YAML::Node& node)
118 Type types = Type::Unknown;
119 for (
const auto& el : node) {
121 types = types | Type::Map;
122 }
else if (el.IsSequence()) {
123 types = types | Type::Sequence;
124 }
else if (el.IsScalar()) {
125 std::string nodestr = el.as<std::string>();
126 if (isInt(nodestr)) {
127 types = types | Type::Integer;
128 }
else if (isFloat(nodestr)) {
129 types = types | Type::Double;
130 }
else if (isBool(nodestr)) {
131 types = types | Type::Bool;
133 types = types | Type::String;
154 target.
setLoc(node.Mark().line, node.Mark().column);
155 if (node.IsSequence()) {
157 target[
"items"] = node.as<
AnyValue>();
159 }
else if (!node.IsMap()) {
160 std::string text = YAML::Dump(node);
161 if (text.size() > 300) {
165 "YAML node is not a map. Node begins with:\n'''\n{}\n'''", text);
167 for (
const auto& child : node) {
168 std::string key = child.first.as<std::string>();
169 const auto& loc = child.second.Mark();
170 target[key].
setLoc(loc.line, loc.column);
171 if (child.second.IsMap()) {
172 target[key] = child.second.as<
AnyMap>();
174 target[key] = child.second.as<
AnyValue>();
175 target[key].setKey(key);
189 target.
setLoc(node.Mark().line, node.Mark().column);
190 if (node.IsScalar()) {
192 std::string nodestr = node.as<std::string>();
193 if (node.Tag() ==
"!") {
198 }
else if (isInt(nodestr)) {
200 target = node.
as<
long int>();
201 }
catch (YAML::BadConversion&) {
205 target = node.
as<
double>();
207 }
else if (isFloat(nodestr)) {
209 }
else if (isBool(nodestr)) {
210 target = node.
as<
bool>();
215 }
else if (node.IsSequence()) {
217 Type types = elementTypes(node);
218 if (types == Type::Integer) {
219 target = node.
as<std::vector<long int>>();
220 }
else if (types == (Type::Integer | Type::Double) || types == Type::Double) {
222 }
else if (types == Type::String) {
223 target = node.
as<std::vector<std::string>>();
224 }
else if (types == Type::Bool) {
225 target = node.
as<std::vector<bool>>();
226 }
else if (types == Type::Map) {
227 target = node.
as<std::vector<AnyMap>>();
228 }
else if (types == Type::Sequence) {
230 Type subtypes = Type::Unknown;
231 for (
const auto& el : node) {
232 subtypes = subtypes | elementTypes(el);
234 if (subtypes == Type::Integer) {
235 target = node.
as<std::vector<std::vector<long int>>>();
236 }
else if (subtypes == (Type::Integer | Type::Double) || subtypes == Type::Double) {
237 target = node.
as<std::vector<std::vector<double>>>();
238 }
else if (types == Type::String) {
239 target = node.
as<std::vector<std::vector<std::string>>>();
240 }
else if (types == Type::Bool) {
241 target = node.
as<std::vector<std::vector<bool>>>();
243 target = node.
as<std::vector<AnyValue>>();
247 target = node.
as<std::vector<AnyValue>>();
250 }
else if (node.IsMap()) {
253 }
else if (node.IsNull()) {
267 {
typeid(double).name(),
"double"},
268 {
typeid(
long int).name(),
"long int"},
269 {
typeid(std::string).name(),
"string"},
270 {
typeid(std::vector<double>).name(),
"vector<double>"},
271 {
typeid(
AnyMap).name(),
"AnyMap"},
274 std::unordered_map<std::string, std::pair<AnyMap, int>>
AnyMap::s_cache;
302 , m_value(new boost::any{})
303 , m_equals(eq_comparer<size_t>)
306 AnyValue::~AnyValue() =
default;
308 AnyValue::AnyValue(
AnyValue const& other)
311 , m_value(new boost::any{*other.
m_value})
312 , m_equals(other.m_equals)
316 AnyValue::AnyValue(
AnyValue&& other)
318 , m_key(std::move(other.m_key))
319 , m_value(std::move(other.m_value))
320 , m_equals(std::move(other.m_equals))
325 if (
this == &other) {
328 AnyBase::operator=(*
this);
331 m_equals = other.m_equals;
336 if (
this == &other) {
339 AnyBase::operator=(std::move(other));
342 m_equals = std::move(other.m_equals);
346 bool AnyValue::operator==(
const AnyValue& other)
const
351 bool AnyValue::operator!=(
const AnyValue& other)
const
358 return as<AnyMap>()[key];
363 return as<AnyMap>().at(key);
367 return (is<AnyMap>() && as<AnyMap>().
hasKey(key));
381 }
else if (
is<std::vector<AnyValue>>()) {
382 for (
auto& item : asVector<AnyValue>()) {
385 }
else if (
is<std::vector<AnyMap>>()) {
386 for (
auto& item : asVector<AnyMap>()) {
393 return demangle(
type());
397 return is<double>() || is<long int>() || is<std::string>() || is<bool>();
402 AnyValue::AnyValue(
const std::string& value)
403 : m_value(new boost::any{value})
404 , m_equals(eq_comparer<std::string>)
407 AnyValue::AnyValue(
const char* value)
408 : m_value(new boost::any{std::string(value)})
409 , m_equals(eq_comparer<std::string>)
412 AnyValue &AnyValue::operator=(
const std::string &value) {
414 m_equals = eq_comparer<std::string>;
418 AnyValue &AnyValue::operator=(
const char *value) {
420 m_equals = eq_comparer<std::string>;
425 return as<std::string>();
428 bool AnyValue::operator==(
const std::string& other)
const
430 if (
m_value->type() ==
typeid(std::string)) {
431 return boost::any_cast<std::string>(*
m_value) == other;
437 bool AnyValue::operator!=(
const std::string& other)
const
439 return !(*
this == other);
442 bool operator==(
const std::string& lhs,
const AnyValue& rhs)
447 bool operator!=(
const std::string& lhs,
const AnyValue& rhs)
454 AnyValue::AnyValue(
double value)
455 : m_value(new boost::any{value})
456 , m_equals(eq_comparer<double>)
459 AnyValue &AnyValue::operator=(
double value) {
461 m_equals = eq_comparer<double>;
473 bool AnyValue::operator==(
const double& other)
const
475 if (
m_value->type() ==
typeid(
double)) {
476 return boost::any_cast<double>(*
m_value) == other;
477 }
else if (
m_value->type() ==
typeid(
long int)) {
478 return boost::any_cast<long int>(*
m_value) == other;
484 bool AnyValue::operator!=(
const double& other)
const
486 return !(*
this == other);
489 bool operator==(
const double& lhs,
const AnyValue& rhs)
494 bool operator!=(
const double& lhs,
const AnyValue& rhs)
501 AnyValue::AnyValue(
bool value)
502 : m_value(new boost::any{value})
503 , m_equals(eq_comparer<bool>)
506 AnyValue &AnyValue::operator=(
bool value) {
508 m_equals = eq_comparer<bool>;
522 AnyValue::AnyValue(
long int value)
523 : m_value(new boost::any{value})
524 , m_equals(eq_comparer<long int>)
527 AnyValue::AnyValue(
int value)
528 : m_value(new boost::any{
static_cast<long int>(value)})
529 , m_equals(eq_comparer<long int>)
532 AnyValue &AnyValue::operator=(
long int value) {
534 m_equals = eq_comparer<long int>;
538 AnyValue &AnyValue::operator=(
int value) {
539 *
m_value =
static_cast<long int>(value);
540 m_equals = eq_comparer<long int>;
545 return as<long int>();
549 return as<long int>();
552 bool AnyValue::operator==(
const long int& other)
const
554 if (
m_value->type() ==
typeid(
long int)) {
555 return boost::any_cast<long int>(*
m_value) == other;
556 }
else if (
m_value->type() ==
typeid(
double)) {
557 return boost::any_cast<double>(*
m_value) == other;
563 bool AnyValue::operator!=(
const long int& other)
const
565 return !(*
this == other);
568 bool AnyValue::operator==(
const int& other)
const
570 return *
this ==
static_cast<long int>(other);
573 bool AnyValue::operator!=(
const int& other)
const
575 return *
this !=
static_cast<long int>(other);
578 bool operator==(
const long int& lhs,
const AnyValue& rhs)
583 bool operator!=(
const long int& lhs,
const AnyValue& rhs)
588 bool operator==(
const int& lhs,
const AnyValue& rhs)
593 bool operator!=(
const int& lhs,
const AnyValue& rhs)
600 AnyValue::AnyValue(
const AnyMap& value)
601 : m_value(new boost::any{value})
602 , m_equals(eq_comparer<AnyMap>)
607 m_equals = eq_comparer<AnyMap>;
613 m_equals = eq_comparer<AnyMap>;
618 const std::string& name)
const
620 std::unordered_map<std::string, const AnyMap*> mapped;
621 for (
const auto& item : asVector<AnyMap>()) {
622 auto key = item[name].asString();
623 if (mapped.count(key)) {
625 "Duplicate key '{}'", key);
627 mapped.emplace(std::make_pair(key, &item));
632 std::unordered_map<std::string, AnyMap*>
AnyValue::asMap(
const std::string& name)
634 std::unordered_map<std::string, AnyMap*> mapped;
635 for (
auto& item : asVector<AnyMap>()) {
636 auto key = item.at(name).asString();
637 if (mapped.count(key)) {
639 "Duplicate key '{}'", key);
641 mapped.emplace(std::make_pair(key, &item));
648 if (
is<std::vector<AnyMap>>()) {
650 return asVector<AnyMap>().
at(0);
652 for (
auto& item : asVector<AnyMap>()) {
653 if (item.hasKey(key) && item[key] == value) {
658 "List does not contain a map where '{}' = '{}'", key, value);
659 }
else if (is<AnyMap>()) {
660 if (value ==
"" || (
hasKey(key) && as<AnyMap>()[key] == value)) {
664 "Map does not contain a key where '{}' = '{}'", key, value);
666 }
else if (is<void>()) {
668 "Key '{}' not found",
m_key);
671 "Element is not a mapping or list of mappings");
678 if (
is<std::vector<AnyMap>>()) {
680 return asVector<AnyMap>().
at(0);
682 for (
auto& item : asVector<AnyMap>()) {
683 if (item.hasKey(key) && item[key] == value) {
689 auto& vec = asVector<AnyMap>();
692 vec.push_back(std::move(child));
696 "List does not contain a map where '{}' = '{}'", key, value);
698 }
else if (is<AnyMap>()) {
699 if (value ==
"" || (
hasKey(key) && as<AnyMap>()[key] == value)) {
703 newChild[key] = value;
704 std::vector<AnyMap> nodes{std::move(as<AnyMap>()), std::move(newChild)};
705 operator=(std::move(nodes));
706 return asVector<AnyMap>().back();
709 "Map does not contain a key where '{}' = '{}'", key, value);
711 }
else if (is<void>() && create) {
714 operator=(std::move(child));
716 }
else if (is<void>()) {
718 "Key '{}' not found",
m_key);
721 "Element is not a mapping or list of mappings");
727 if (
is<std::vector<AnyMap>>()) {
731 for (
auto& item : asVector<AnyMap>()) {
732 if (item.hasKey(key) && item[key] == value) {
737 }
else if (is<AnyMap>()) {
738 if (value ==
"" || (
hasKey(key) && as<AnyMap>()[key] == value)) {
752 as<AnyMap>().applyUnits(units);
753 }
else if (
is<std::vector<AnyMap>>()) {
754 auto& list = as<std::vector<AnyMap>>();
755 if (list.size() && list[0].hasKey(
"units") && list[0].size() == 1) {
759 newUnits.
setDefaults(list[0][
"units"].asMap<std::string>());
760 list[0].m_data.erase(
"units");
761 for (
auto& item : list) {
763 if (item.size() == 1 && item.hasKey(
"units")) {
765 "Found units entry as not the first item in a list.");
767 item.applyUnits(newUnits);
770 list.erase(list.begin());
773 for (
auto& item : list) {
775 if (item.size() == 1 && item.hasKey(
"units")) {
777 "Found units entry as not the first item in a list.");
779 item.applyUnits(units);
786 std::string AnyValue::demangle(
const std::type_info& type)
const
791 #ifdef CT_USE_DEMANGLE
792 return boost::core::demangle(
type.name());
802 const std::vector<AnyValue>& AnyValue::asVector<AnyValue>(
size_t nMin,
size_t nMax)
const
804 if (!
is<std::vector<AnyValue>>()) {
805 std::vector<AnyValue> v;
806 if (
is<std::vector<double>>()) {
807 for (
const auto& el : asVector<double>()) {
811 }
else if (
is<std::vector<long int>>()) {
812 for (
const auto& el : asVector<long int>()) {
816 }
else if (
is<std::vector<std::string>>()) {
817 for (
const auto& el : asVector<std::string>()) {
825 const auto& vv = as<std::vector<AnyValue>>();
826 m_equals = eq_comparer<std::vector<AnyValue>>;
827 checkSize(vv, nMin, nMax);
832 std::vector<AnyValue>& AnyValue::asVector<AnyValue>(
size_t nMin,
size_t nMax)
834 auto& v =
const_cast<std::vector<AnyValue>&
>(
835 const_cast<const AnyValue*
>(
this)->asVector<AnyValue>());
836 checkSize(v, nMin, nMax);
841 const std::vector<double>& AnyValue::asVector<double>(
size_t nMin,
size_t nMax)
const
843 if (
is<std::vector<long int>>()) {
844 std::vector<double> v;
845 for (
const auto& el : asVector<long int>()) {
850 const auto& vv = as<std::vector<double>>();
851 m_equals = eq_comparer<std::vector<double>>;
852 checkSize(vv, nMin, nMax);
857 std::vector<double>& AnyValue::asVector<double>(
size_t nMin,
size_t nMax)
859 if (
is<std::vector<long int>>()) {
860 std::vector<double> v;
861 for (
const auto& el : asVector<long int>()) {
866 auto& vv = as<std::vector<double>>();
867 m_equals = eq_comparer<std::vector<double>>;
868 checkSize(vv, nMin, nMax);
873 const std::vector<vector_fp>& AnyValue::asVector<vector_fp>(
size_t nMin,
size_t nMax)
const
875 if (
is<std::vector<std::vector<long int>>>()) {
876 std::vector<vector_fp> v;
877 for (
const auto& outer :
asVector<std::vector<long int>>()) {
879 for (
const auto& inner : outer) {
880 v.back().push_back(inner);
885 const auto& vv = as<std::vector<vector_fp>>();
886 m_equals = eq_comparer<std::vector<vector_fp>>;
887 checkSize(vv, nMin, nMax);
892 std::vector<vector_fp>& AnyValue::asVector<vector_fp>(
size_t nMin,
size_t nMax)
894 if (
is<std::vector<std::vector<long int>>>()) {
895 std::vector<vector_fp> v;
896 for (
const auto& outer :
asVector<std::vector<long int>>()) {
898 for (
const auto& inner : outer) {
899 v.back().push_back(inner);
904 auto& vv = as<std::vector<vector_fp>>();
905 m_equals = eq_comparer<std::vector<vector_fp>>;
906 checkSize(vv, nMin, nMax);
911 const std::vector<AnyMap>& AnyValue::asVector<AnyMap>(
size_t nMin,
size_t nMax)
const
914 std::vector<AnyMap> v;
915 v.push_back(std::move(as<AnyMap>()));
917 }
else if (
is<std::vector<AnyValue>>() && asVector<AnyValue>().empty()) {
918 *
m_value = std::vector<AnyMap>();
920 const auto& vv = as<std::vector<AnyMap>>();
921 checkSize(vv, nMin, nMax);
926 std::vector<AnyMap>& AnyValue::asVector<AnyMap>(
size_t nMin,
size_t nMax)
929 std::vector<AnyMap> v;
930 v.push_back(std::move(as<AnyMap>()));
932 }
else if (
is<std::vector<AnyValue>>() && asVector<AnyValue>().empty()) {
933 *
m_value = std::vector<AnyMap>();
935 auto& vv = as<std::vector<AnyMap>>();
936 checkSize(vv, nMin, nMax);
944 const auto& iter =
m_data.find(key);
945 if (iter ==
m_data.end()) {
968 }
catch (std::out_of_range&) {
970 "Key '{}' not found.\nExisting keys: {}", key,
keys_str());
978 }
catch (std::out_of_range&) {
980 "Key '{}' not found.\nExisting keys: {}", key,
keys_str());
1001 fmt::memory_buffer b;
1002 auto iter = this->
begin();
1003 if (iter != this->
end()) {
1004 format_to(b,
"{}", iter->first);
1007 while (iter != this->
end()) {
1008 format_to(b,
", {}", iter->first);
1011 return to_string(b);
1017 for (
auto& item :
m_data) {
1030 (*m_metadata)[key] = value;
1036 return (
hasKey(key)) ?
m_data.at(key).asBool() : default_;
1041 return (
hasKey(key)) ?
m_data.at(key).asDouble() : default_;
1046 return (
hasKey(key)) ?
m_data.at(key).asInt() : default_;
1050 const std::string& default_)
const
1052 return (
hasKey(key)) ?
m_data.at(key).asString() : default_;
1066 double default_)
const
1076 size_t nMin,
size_t nMax)
const
1078 return units().
convert(
at(key).asVector<AnyValue>(nMin, nMax), dest);
1081 AnyMap::Iterator::Iterator(
1082 const std::unordered_map<std::string, AnyValue>::const_iterator& start,
1083 const std::unordered_map<std::string, AnyValue>::const_iterator& stop)
1087 while (m_iter != m_stop
1088 && ba::starts_with(m_iter->first,
"__")
1089 && ba::ends_with(m_iter->first,
"__")) {
1097 while (m_iter != m_stop
1098 && ba::starts_with(m_iter->first,
"__")
1099 && ba::ends_with(m_iter->first,
"__")) {
1105 bool AnyMap::operator==(
const AnyMap& other)
const
1109 for (
auto& item : *
this) {
1110 if (!other.
hasKey(item.first)) {
1115 for (
auto & item : other) {
1116 if (!
hasKey(item.first) || item.second !=
at(item.first)) {
1123 bool AnyMap::operator!=(
const AnyMap& other)
const
1135 for (
auto& item :
m_data) {
1136 item.second.applyUnits(
m_units);
1143 YAML::Node node = YAML::Load(yaml);
1144 amap = node.as<
AnyMap>();
1145 }
catch (YAML::Exception& err) {
1147 fake.
setLoc(err.mark.line, err.mark.column);
1157 const std::string& parent_name)
1159 std::string fullName;
1161 size_t islash = parent_name.find_last_of(
"/\\");
1162 if (islash !=
npos) {
1163 std::string parent_path = parent_name.substr(0, islash);
1164 if (std::ifstream(parent_path +
"/" + name).good()) {
1165 fullName = parent_path +
"/" + name;
1169 if (fullName.empty()) {
1175 int mtime = get_modified_time(fullName);
1176 std::unique_lock<std::mutex> lock(yaml_cache_mutex);
1177 auto iter =
s_cache.find(fullName);
1178 if (iter !=
s_cache.end() && iter->second.second == mtime) {
1179 return iter->second.first;
1182 if (!std::ifstream(fullName).good()) {
1183 throw CanteraError(
"AnyMap::fromYamlFile",
"Input file '{}' not found "
1184 "on the Cantera search path.", name);
1188 auto& cache_item =
s_cache[fullName];
1189 cache_item.second = mtime;
1191 YAML::Node node = YAML::LoadFile(fullName);
1192 cache_item.first = node.as<
AnyMap>();
1195 }
catch (YAML::Exception& err) {
1198 fake.
setLoc(err.mark.line, err.mark.column);
1205 cache_item.first[
"__file__"] = fullName;
1207 if (cache_item.first.hasKey(
"deprecated")) {
1212 return cache_item.first;
1224 void formatInputFile(fmt::memory_buffer& b,
const shared_ptr<AnyMap>& metadata,
1225 const std::string& filename,
int lineno,
int column,
int lineno2=-1,
int column2=-1)
1227 if (lineno2 == -1) {
1232 format_to(b,
"| Line |\n");
1233 if (!metadata->hasKey(
"file-contents")) {
1235 std::stringstream buffer;
1236 buffer << infile.rdbuf();
1237 (*metadata)[
"file-contents"] = buffer.str();
1242 std::stringstream contents((*metadata)[
"file-contents"].asString());
1243 while (std::getline(contents, line)) {
1244 if (i == lineno || i == lineno2) {
1245 format_to(b,
"> {: 5d} > {}\n", i+1, line);
1246 format_to(b,
"{:>{}}\n",
"^", column + 11);
1248 }
else if ((lineno + 4 > i && lineno < i + 6) ||
1249 (lineno2 + 4 > i && lineno2 < i + 6)) {
1250 if (lastShown >= 0 && i - lastShown > 1) {
1251 format_to(b,
"...\n");
1253 format_to(b,
"| {: 5d} | {}\n", i+1, line);
1261 std::string InputFileError::formatError(
const std::string& message,
1262 int lineno,
int column,
1263 const shared_ptr<AnyMap>& metadata)
1268 std::string filename = metadata->getString(
"filename",
"input string");
1270 fmt::memory_buffer b;
1271 format_to(b,
"Error on line {} of {}:\n{}\n", lineno+1, filename, message);
1272 formatInputFile(b, metadata, filename, lineno, column);
1273 return to_string(b);
1276 std::string InputFileError::formatError2(
const std::string& message,
1277 int line1,
int column1,
1278 const shared_ptr<AnyMap>& metadata1,
1279 int line2,
int column2,
1280 const shared_ptr<AnyMap>& metadata2)
1282 if (!metadata1 || !metadata2) {
1285 std::string filename1 = metadata1->getString(
"filename",
"input string");
1286 std::string filename2 = metadata2->getString(
"filename",
"input string");
1288 fmt::memory_buffer b;
1289 if (filename1 == filename2) {
1290 format_to(b,
"Error on lines {} and {} of {}:\n",
1291 std::min(line1, line2) + 1, std::max(line1, line2) + 1,
1293 format_to(b,
"{}\n", message);
1294 formatInputFile(b, metadata1, filename1, line1, column1, line2, column2);
1296 format_to(b,
"Error on line {} of {} and line {} of {}:\n{}\n",
1297 line1+1, filename1, line2+1, filename2, message);
1298 formatInputFile(b, metadata1, filename1, line1, column1);
1300 formatInputFile(b, metadata2, filename2, line2, column2);
1303 return to_string(b);