27 constexpr int64_t
UINT32_LIMIT = std::numeric_limits<uint32_t>::max();
35 std::span<const std::string_view> allowed, std::string_view path) {
38 std::find(allowed.begin(), allowed.end(), field.
key.
text) != allowed.end();
40 return std::unexpected(std::format(
"{}: 知らない欄 {}", path,
ToDebugText(field.
key)));
47 std::string_view path) {
50 return std::unexpected(std::format(
"{}: 欄 {} がない", path, field));
57 return std::unexpected(std::format(
"{}: 表で書く", path));
64 int64_t minimum, int64_t maximum) {
69 return std::unexpected(std::format(
"{}: 整数(±2^53 まで)で書く", path));
71 const auto integer =
static_cast<int64_t
>(value.
number);
72 if (integer < minimum || integer > maximum)
73 return std::unexpected(std::format(
"{}: {} は範囲 {}〜{} の外", path, integer, minimum, maximum));
79 std::string_view path, int64_t minimum, int64_t maximum) {
82 return std::unexpected(value.error());
84 return ReadInteger(**value, std::format(
"{}.{}", path, field), minimum, maximum);
89 std::span<const std::string_view> allowed, std::string_view path) {
99 const std::string& path) {
100 static constexpr std::string_view FIELDS[] = {
"atomic_mass_mg_per_mol"};
101 if (
auto record =
CheckRecord(value, FIELDS, path); !record)
102 return std::unexpected(record.error());
106 return std::unexpected(mass.error());
108 return ElementDefinition{.name = name, .atomicMass =
static_cast<uint32_t
>(*mass)};
114 const std::string& path) {
116 return std::unexpected(table.error());
118 std::vector<ElementCount> composition;
121 return std::unexpected(std::format(
"{}: 鍵は元素の名前", path));
126 return std::unexpected(count.error());
128 composition.push_back({.element = field.
key.
text, .count =
static_cast<uint32_t
>(*count)});
135 const std::string& path) {
136 static constexpr std::string_view FIELDS[] = {
"composition",
"formation_enthalpy_j_per_mol",
137 "heat_capacity_mj_per_mol_k",
138 "thermal_conductivity_mw_per_m_k"};
139 if (
auto record =
CheckRecord(value, FIELDS, path); !record)
140 return std::unexpected(record.error());
142 const auto compositionValue =
RequireField(value,
"composition", path);
143 if (!compositionValue)
144 return std::unexpected(compositionValue.error());
146 auto composition =
ReadComposition(**compositionValue, path +
".composition");
148 return std::unexpected(composition.error());
154 for (
const auto* result : {&enthalpy, &heatCapacity, &conductivity}) {
156 return std::unexpected(result->error());
160 .composition = std::move(*composition),
161 .formationEnthalpy = *enthalpy,
162 .heatCapacity =
static_cast<uint32_t
>(*heatCapacity),
163 .thermalConductivity =
static_cast<uint32_t
>(*conductivity)};
169 std::expected<std::vector<RuleTerm>, std::string>
ReadTerms(
const ScriptValue& value,
const std::string& path) {
171 return std::unexpected(table.error());
173 std::vector<RuleTerm> terms;
176 return std::unexpected(std::format(
"{}: 鍵は物質の名前", path));
181 return std::unexpected(coefficient.error());
183 terms.push_back({.species = field.
key.
text,
184 .coefficient =
static_cast<uint32_t
>(*coefficient),
185 .firstOrder =
false});
193 const std::string& path) {
194 if (orders ==
nullptr) {
196 term.firstOrder =
true;
205 const auto term = std::find_if(reactants.begin(), reactants.end(), [&](
const RuleTerm& reactant) {
206 return field.key.IsString() && reactant.species == field.key.text;
208 if (term == reactants.end())
209 return std::unexpected(std::format(
"{}: {} は反応物ではない", path,
ToDebugText(field.
key)));
213 return std::unexpected(order.error());
215 term->firstOrder = *order == 1;
223 const std::string& path) {
227 return std::unexpected(std::format(
"{}: {}", path, parsed.error()));
234 return std::unexpected(std::format(
"{}(10 進の文字列 \"2.8e19\" でもよい)", integer.error()));
239 std::expected<sim::ArrheniusRate, std::string>
ReadRate(
const ScriptValue& value,
const std::string& path) {
240 static constexpr std::string_view FIELDS[] = {
"a",
"activation_energy_j_per_mol"};
241 if (
auto record =
CheckRecord(value, FIELDS, path); !record)
242 return std::unexpected(record.error());
244 const auto preExponentialValue =
RequireField(value,
"a", path);
245 if (!preExponentialValue)
246 return std::unexpected(preExponentialValue.error());
250 return std::unexpected(preExponential.error());
252 const auto activation =
ReadIntegerField(value,
"activation_energy_j_per_mol", path, 0,
255 return std::unexpected(activation.error());
258 .preExponentialExponent10 = preExponential->exponent10,
259 .activationEnergy = *activation};
265 for (
const auto& [field, terms] : {std::pair{
"reactants", &rule.
reactants}, {
"products", &rule.
products}}) {
266 const auto termsValue =
RequireField(value, field, path);
268 return std::unexpected(termsValue.error());
270 auto read =
ReadTerms(**termsValue, std::format(
"{}.{}", path, field));
272 return std::unexpected(read.error());
274 *terms = std::move(*read);
281 const std::string& path) {
282 static constexpr std::string_view FIELDS[] = {
"reactants",
"products",
"rate",
"orders",
283 "reaction_enthalpy_j_per_mol"};
284 if (
auto record =
CheckRecord(value, FIELDS, path); !record)
285 return std::unexpected(record.error());
289 return std::unexpected(terms.error());
291 const auto rateValue =
RequireField(value,
"rate", path);
293 return std::unexpected(rateValue.error());
295 const auto rate =
ReadRate(**rateValue, path +
".rate");
297 return std::unexpected(rate.error());
300 if (
const ScriptValue* declared = value.
Find(
"reaction_enthalpy_j_per_mol")) {
301 const auto enthalpy =
ReadInteger(*declared, path +
".reaction_enthalpy_j_per_mol",
304 return std::unexpected(enthalpy.error());
315 template <
typename Definition,
typename Reader>
317 Reader read, std::vector<Definition>& out) {
318 const auto found = packages.
tables.find(std::string(category));
319 if (found == packages.
tables.end())
322 for (
const auto& [key, entry] : found->second) {
323 const std::string path = std::format(
"{}.{}", category, key);
324 auto definition = read(key, entry.value, path);
326 return std::unexpected(std::format(
"パッケージ {}: {}", entry.package, definition.error()));
328 out.push_back(std::move(*definition));
337 std::expected<DecimalNumber, std::string>
DigitsToDecimal(std::string digits, int64_t exponent10) {
338 const size_t first = digits.find_first_not_of(
'0');
339 if (first == std::string::npos)
340 return std::unexpected(
"0 より大きい数で書く");
342 digits.erase(0, first);
343 while (digits.back() ==
'0') {
354 uint64_t mantissa = 0;
355 for (
const char digit : digits)
356 mantissa = (mantissa * 10) +
static_cast<uint64_t
>(digit -
'0');
358 return DecimalNumber{.mantissa = mantissa, .exponent10 =
static_cast<int32_t
>(exponent10)};
362 return character >=
'0' && character <=
'9';
367 const bool negative = !text.empty() && text.front() ==
'-';
368 if (!text.empty() && (text.front() ==
'-' || text.front() ==
'+'))
369 text.remove_prefix(1);
371 if (text.empty() || text.size() > 4 || !std::ranges::all_of(text,
IsDigit))
372 return std::unexpected(
"指数の書き方の誤り");
375 for (
const char digit : text)
376 written = (written * 10) + (digit -
'0');
378 return negative ? -written : written;
383 std::expected<DecimalNumber, std::string>
ParseDecimal(std::string_view text) {
384 const std::string error = std::format(
"10 進の数(例 \"2.8e19\")で書く: \"{}\"", text);
386 int64_t exponent10 = 0;
388 bool seenPoint =
false;
389 for (; position < text.size() && text[position] !=
'e' && text[position] !=
'E'; ++position) {
390 const char character = text[position];
391 if (character ==
'.' && !seenPoint) {
396 if (!IsDigit(character))
397 return std::unexpected(error);
399 digits.push_back(character);
400 exponent10 -= seenPoint ? 1 : 0;
404 return std::unexpected(error);
406 if (position < text.size()) {
407 const auto written = ParseExponent(text.substr(position + 1));
409 return std::unexpected(error);
411 exponent10 += *written;
414 auto decimal = DigitsToDecimal(std::move(digits), exponent10);
416 return std::unexpected(std::format(
"{}: \"{}\"", decimal.error(), text));
424 return std::unexpected(result.error());
427 return std::unexpected(result.error());
430 return std::unexpected(result.error());
std::expected< int64_t, std::string > ParseExponent(std::string_view text)
constexpr int64_t UINT32_LIMIT
std::expected< ElementDefinition, std::string > ReadElement(const std::string &name, const ScriptValue &value, const std::string &path)
std::expected< void, std::string > ReadRuleTerms(const ScriptValue &value, const std::string &path, RuleDefinition &rule)
std::expected< DecimalNumber, std::string > ReadPreExponential(const ScriptValue &value, const std::string &path)
std::expected< RuleDefinition, std::string > ReadRule(const std::string &name, const ScriptValue &value, const std::string &path)
std::expected< std::vector< RuleTerm >, std::string > ReadTerms(const ScriptValue &value, const std::string &path)
std::expected< void, std::string > CheckFields(const ScriptValue &table, std::span< const std::string_view > allowed, std::string_view path)
std::expected< void, std::string > RequireTable(const ScriptValue &value, std::string_view path)
std::expected< const ScriptValue *, std::string > RequireField(const ScriptValue &table, std::string_view field, std::string_view path)
std::expected< void, std::string > ReadOrders(const ScriptValue *orders, std::vector< RuleTerm > &reactants, const std::string &path)
std::expected< sim::ArrheniusRate, std::string > ReadRate(const ScriptValue &value, const std::string &path)
std::expected< void, std::string > CheckRecord(const ScriptValue &value, std::span< const std::string_view > allowed, std::string_view path)
constexpr int64_t DECIMAL_EXPONENT_MAX
std::expected< SpeciesDefinition, std::string > ReadSpecies(const std::string &name, const ScriptValue &value, const std::string &path)
std::expected< DecimalNumber, std::string > DigitsToDecimal(std::string digits, int64_t exponent10)
std::expected< void, std::string > ReadCategory(const PackageSetResult &packages, std::string_view category, Reader read, std::vector< Definition > &out)
std::expected< int64_t, std::string > ReadInteger(const ScriptValue &value, std::string_view path, int64_t minimum, int64_t maximum)
std::expected< int64_t, std::string > ReadIntegerField(const ScriptValue &table, std::string_view field, std::string_view path, int64_t minimum, int64_t maximum)
bool IsDigit(char character)
constexpr size_t DECIMAL_DIGITS_MAX
constexpr int64_t EXACT_INTEGER_LIMIT
std::expected< std::vector< ElementCount >, std::string > ReadComposition(const ScriptValue &value, const std::string &path)
std::string ToDebugText(const ScriptValue &value)
std::expected< ReactionTableDefinition, std::string > ReadReactionTableDefinition(const PackageSetResult &packages)
std::expected< DecimalNumber, std::string > ParseDecimal(std::string_view text)
constexpr std::string_view REACTION_ELEMENTS_CATEGORY
constexpr std::string_view REACTION_SPECIES_CATEGORY
constexpr std::string_view REACTION_RULES_CATEGORY
std::map< std::string, std::map< std::string, TableEntry > > tables
std::vector< ScriptField > fields
const ScriptValue * Find(std::string_view key) const
std::vector< ElementDefinition > elements
std::vector< SpeciesDefinition > species
std::vector< RuleDefinition > rules
std::vector< RuleTerm > reactants
std::vector< RuleTerm > products
std::optional< int64_t > declaredReactionEnthalpy