Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2024, The Board of Trustees of the University of Illinois,
2 : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
3 : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
4 : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
5 : // contributors. All rights reserved.
6 : //
7 : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
8 : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
9 : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
10 : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
11 : // derivative works, and perform publicly and display publicly, and to permit others to do so.
12 : //
13 : // Redistribution and use in source and binary forms, with or without modification, are permitted
14 : // provided that the following conditions are met:
15 : //
16 : // (1) Redistributions of source code must retain the above copyright notice, this list of
17 : // conditions and the following disclaimer.
18 : //
19 : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
20 : // conditions and the following disclaimer in the documentation and/or other materials
21 : // provided with the distribution.
22 : //
23 : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
24 : // the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
25 : // used to endorse or promote products derived from this software without specific prior
26 : // written permission.
27 : //
28 : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
29 : // without changes from the version obtained under this License, or (ii) Licensee makes a
30 : // reference solely to the software portion of its product, Licensee must refer to the
31 : // software as "EnergyPlus version X" software, where "X" is the version number Licensee
32 : // obtained under this License and may not use a different name for the software. Except as
33 : // specifically required in this Section (4), Licensee shall not use in a company name, a
34 : // product name, in advertising, publicity, or other promotional activities any name, trade
35 : // name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
36 : // similar designation, without the U.S. Department of Energy's prior written consent.
37 : //
38 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39 : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
40 : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41 : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43 : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : // POSSIBILITY OF SUCH DAMAGE.
47 :
48 : #include <EnergyPlus/FromChars.hh>
49 : #include <EnergyPlus/InputProcessing/IdfParser.hh>
50 : #include <cmath>
51 : #include <fast_float/fast_float.h>
52 : #include <fmt/format.h>
53 : #include <milo/dtoa.h>
54 : #include <milo/itoa.h>
55 :
56 : using json = nlohmann::json;
57 :
58 3513250 : auto const icompare = [](std::string_view a, std::string_view b) { // (AUTO_OK)
59 3513250 : return (a.length() == b.length()
60 8521245 : ? std::equal(a.begin(), a.end(), b.begin(), [](char const c, char const d) { return (::tolower(c) == ::tolower(d)); })
61 3513250 : : false);
62 : };
63 :
64 0 : json IdfParser::decode(std::string_view idf, json const &schema)
65 : {
66 0 : bool success = true;
67 0 : return decode(idf, idf.size(), schema, success);
68 : }
69 :
70 795 : json IdfParser::decode(std::string_view idf, json const &schema, bool &success)
71 : {
72 795 : return decode(idf, idf.size(), schema, success);
73 : }
74 :
75 0 : json IdfParser::decode(std::string_view idf, size_t _idf_size, json const &schema)
76 : {
77 0 : bool success = true;
78 0 : return decode(idf, _idf_size, schema, success);
79 : }
80 :
81 795 : json IdfParser::decode(std::string_view idf, size_t _idf_size, json const &schema, bool &success)
82 : {
83 795 : success = true;
84 795 : cur_line_num = 1;
85 795 : index_into_cur_line = 0;
86 795 : beginning_of_line_index = 0;
87 795 : idf_size = _idf_size;
88 :
89 795 : if (idf.empty()) {
90 0 : success = false;
91 0 : return nullptr;
92 : }
93 :
94 795 : size_t index = 0;
95 795 : return parse_idf(idf, index, success, schema);
96 : }
97 :
98 0 : std::string IdfParser::encode(json const &root, json const &schema)
99 : {
100 : static constexpr std::string_view end_of_field(",\n ", 4);
101 : static constexpr std::string_view end_of_object(";\n\n", 3);
102 :
103 0 : std::string encoded, extension_key;
104 0 : if (idf_size > 0) {
105 0 : encoded.reserve(idf_size);
106 : } else {
107 0 : encoded.reserve(root.size() * 1024);
108 : }
109 :
110 0 : for (auto obj = root.begin(); obj != root.end(); ++obj) {
111 0 : const auto &legacy_idd = schema["properties"][obj.key()]["legacy_idd"];
112 0 : const auto &legacy_idd_field = legacy_idd["fields"];
113 0 : auto key = legacy_idd.find("extension");
114 0 : if (key != legacy_idd.end()) {
115 0 : extension_key = key.value().get<std::string>();
116 : }
117 0 : for (auto obj_in = obj.value().begin(); obj_in != obj.value().end(); ++obj_in) {
118 0 : encoded += obj.key();
119 0 : size_t skipped_fields = 0;
120 0 : for (size_t i = 0; i < legacy_idd_field.size(); i++) {
121 0 : std::string const &entry = legacy_idd_field[i].get<std::string>();
122 0 : if (obj_in.value().find(entry) == obj_in.value().end()) {
123 0 : if (entry == "name")
124 0 : encoded += std::string{end_of_field} + obj_in.key();
125 : else
126 0 : skipped_fields++;
127 0 : continue;
128 : }
129 0 : for (size_t j = 0; j < skipped_fields; j++)
130 0 : encoded += end_of_field;
131 0 : skipped_fields = 0;
132 0 : encoded += end_of_field;
133 0 : auto const &val = obj_in.value()[entry];
134 0 : if (val.is_string()) {
135 0 : encoded += val.get<std::string>();
136 0 : } else if (val.is_number_integer()) {
137 0 : encoded += std::to_string(val.get<int>());
138 : } else {
139 0 : dtoa(val.get<double>(), s);
140 0 : encoded += s;
141 : }
142 0 : }
143 :
144 0 : if (obj_in.value().find(extension_key) == obj_in.value().end()) {
145 0 : encoded += end_of_object;
146 0 : continue;
147 : }
148 :
149 0 : auto &extensions = obj_in.value()[extension_key];
150 0 : for (size_t extension_i = 0; extension_i < extensions.size(); extension_i++) {
151 0 : auto const &cur_extension_obj = extensions[extension_i];
152 0 : auto const &extensible = schema["properties"][obj.key()]["legacy_idd"]["extensibles"];
153 0 : for (size_t i = 0; i < extensible.size(); i++) {
154 0 : std::string const &tmp = extensible[i].get<std::string>();
155 0 : if (cur_extension_obj.find(tmp) == cur_extension_obj.end()) {
156 0 : skipped_fields++;
157 0 : continue;
158 : }
159 0 : for (size_t j = 0; j < skipped_fields; j++)
160 0 : encoded += end_of_field;
161 0 : skipped_fields = 0;
162 0 : encoded += end_of_field;
163 0 : if (cur_extension_obj[tmp].is_string()) {
164 0 : encoded += cur_extension_obj[tmp].get<std::string>();
165 : } else {
166 0 : dtoa(cur_extension_obj[tmp].get<double>(), s);
167 0 : encoded += s;
168 : }
169 0 : }
170 : }
171 0 : encoded += end_of_object;
172 0 : }
173 0 : }
174 0 : return encoded;
175 0 : }
176 :
177 294489 : std::string IdfParser::normalizeObjectType(std::string const &objectType)
178 : {
179 294489 : if (objectType.empty()) return std::string{};
180 294489 : std::string key = convertToUpper(objectType);
181 294489 : auto tmp_umit = objectTypeMap.find(key);
182 294489 : if (tmp_umit != objectTypeMap.end()) {
183 294489 : return tmp_umit->second;
184 : }
185 0 : return std::string{};
186 294489 : }
187 :
188 796 : std::vector<std::string> const &IdfParser::errors()
189 : {
190 796 : return errors_;
191 : }
192 :
193 796 : std::vector<std::string> const &IdfParser::warnings()
194 : {
195 796 : return warnings_;
196 : }
197 :
198 796 : bool IdfParser::hasErrors()
199 : {
200 796 : return !errors_.empty();
201 : }
202 :
203 795 : json IdfParser::parse_idf(std::string_view idf, size_t &index, bool &success, json const &schema)
204 : {
205 795 : json root;
206 : Token token;
207 795 : auto const &schema_properties = schema["properties"];
208 :
209 795 : objectTypeMap.reserve(schema_properties.size());
210 674955 : for (auto it = schema_properties.begin(); it != schema_properties.end(); ++it) {
211 674160 : std::string key = convertToUpper(it.key());
212 674160 : objectTypeMap.emplace(std::move(key), it.key());
213 674955 : }
214 :
215 795 : if (idf_size > 3) {
216 : // UTF-8 Byte Order Mark
217 795 : if (idf[0] == '\xEF' && idf[1] == '\xBB' && idf[2] == '\xBF') {
218 0 : index += 3;
219 0 : index_into_cur_line += 3;
220 : }
221 : }
222 :
223 795 : int idfObjectCount = 0;
224 : while (true) {
225 699400 : token = look_ahead(idf, index);
226 699400 : if (token == Token::END) {
227 795 : break;
228 698605 : } else if (token == Token::NONE) {
229 0 : success = false;
230 0 : return root;
231 698605 : } else if (token == Token::SEMICOLON) {
232 0 : next_token(idf, index);
233 0 : continue;
234 698605 : } else if (token == Token::COMMA) {
235 0 : errors_.emplace_back(fmt::format("Line: {} Index: {} - Extraneous comma found.", cur_line_num, index_into_cur_line));
236 0 : success = false;
237 0 : return root;
238 698605 : } else if (token == Token::EXCLAMATION) {
239 404116 : eat_comment(idf, index);
240 : } else {
241 294489 : ++idfObjectCount;
242 294489 : std::string const parsed_obj_name = parse_string(idf, index);
243 294489 : std::string const obj_name = normalizeObjectType(parsed_obj_name);
244 294489 : if (obj_name.empty()) {
245 0 : errors_.emplace_back(
246 0 : fmt::format("Line: {} Index: {} - \"{}\" is not a valid Object Type.", cur_line_num, index_into_cur_line, parsed_obj_name));
247 0 : while (token != Token::SEMICOLON && token != Token::END)
248 0 : token = next_token(idf, index);
249 0 : continue;
250 : }
251 :
252 294489 : bool object_success = true;
253 294489 : json const &obj_loc = schema_properties[obj_name];
254 294489 : json const &legacy_idd = obj_loc["legacy_idd"];
255 294489 : json obj = parse_object(idf, index, object_success, legacy_idd, obj_loc, idfObjectCount);
256 294489 : if (!object_success) {
257 0 : auto found_index = idf.find_first_of('\n', beginning_of_line_index);
258 0 : std::string line;
259 0 : if (found_index != std::string::npos) {
260 0 : line = idf.substr(beginning_of_line_index, found_index - beginning_of_line_index - 1);
261 : }
262 0 : errors_.emplace_back(
263 0 : fmt::format("Line: {} Index: {} - Error parsing \"{}\". Error in following line.", cur_line_num, index_into_cur_line, obj_name));
264 0 : errors_.emplace_back(fmt::format("~~~ {}", line));
265 0 : success = false;
266 0 : continue;
267 0 : }
268 294489 : u64toa(root[obj_name].size() + 1, s);
269 294489 : std::string name = fmt::format("{} {}", obj_name, s);
270 :
271 294489 : if (!obj.is_null()) {
272 294489 : auto const name_iter = obj.find("name");
273 : // If you find a name field, use that
274 294489 : if (name_iter != obj.end()) {
275 236767 : name = name_iter.value().get<std::string>();
276 236767 : obj.erase(name_iter);
277 : } else {
278 : // Otherwise, see if it should have a name field
279 57722 : auto const it = obj_loc.find("name");
280 57722 : if (it != obj_loc.end()) {
281 : // Let it slide, as a blank string, to be handled in the appropriate GetInput routine
282 1 : name = "";
283 : }
284 57722 : }
285 294489 : }
286 :
287 294489 : if (root[obj_name].find(name) != root[obj_name].end()) {
288 0 : errors_.emplace_back(
289 0 : fmt::format(R"(Duplicate name found for object of type "{}" named "{}". Overwriting existing object.)", obj_name, name));
290 : }
291 :
292 294489 : root[obj_name][name] = std::move(obj);
293 294489 : }
294 698605 : }
295 :
296 795 : return root;
297 0 : }
298 :
299 294489 : json IdfParser::parse_object(
300 : std::string_view idf, size_t &index, bool &success, json const &legacy_idd, json const &schema_obj_loc, int idfObjectCount)
301 : {
302 294489 : json root = json::object();
303 294489 : json extensible = json::object();
304 294489 : json array_of_extensions = json::array();
305 : Token token;
306 294489 : std::string extension_key;
307 294489 : size_t legacy_idd_index = 0;
308 294489 : size_t extensible_index = 0;
309 294489 : success = true;
310 294489 : bool was_value_parsed = false;
311 294489 : auto const &legacy_idd_fields_array = legacy_idd["fields"];
312 294489 : auto const legacy_idd_extensibles_iter = legacy_idd.find("extensibles");
313 :
314 294489 : auto const &schema_patternProperties = schema_obj_loc["patternProperties"];
315 294489 : std::string patternProperty;
316 294489 : int dot_star_present = schema_patternProperties.count(".*");
317 294489 : int no_whitespace_present = schema_patternProperties.count(R"(^.*\S.*$)");
318 294489 : if (dot_star_present) {
319 61289 : patternProperty = ".*";
320 233200 : } else if (no_whitespace_present) {
321 233200 : patternProperty = R"(^.*\S.*$)";
322 : } else {
323 0 : throw std::runtime_error(R"(The patternProperties value is not a valid choice (".*", "^.*\S.*$"))");
324 : }
325 294489 : auto const &schema_obj_props = schema_patternProperties[patternProperty]["properties"];
326 294489 : auto key = legacy_idd.find("extension");
327 :
328 294489 : json const *schema_obj_extensions = nullptr;
329 294489 : if (legacy_idd_extensibles_iter != legacy_idd.end()) {
330 100144 : if (key == legacy_idd.end()) {
331 0 : errors_.emplace_back("\"extension\" key not found in schema. Need to add to list in modify_schema.py.");
332 0 : success = false;
333 0 : return root;
334 : }
335 100144 : extension_key = key.value().get<std::string>();
336 100144 : schema_obj_extensions = &schema_obj_props[extension_key]["items"]["properties"];
337 : }
338 :
339 294489 : root["idf_order"] = idfObjectCount;
340 :
341 294489 : auto const found_min_fields = schema_obj_loc.find("min_fields");
342 :
343 294489 : index += 1;
344 :
345 : while (true) {
346 11157042 : token = look_ahead(idf, index);
347 11157042 : root["idf_max_fields"] = legacy_idd_index;
348 11157042 : root["idf_max_extensible_fields"] = extensible_index;
349 11157042 : if (token == Token::NONE) {
350 0 : success = false;
351 0 : return root;
352 11157042 : } else if (token == Token::END) {
353 0 : return root;
354 11157042 : } else if (token == Token::COMMA || token == Token::SEMICOLON) {
355 4204233 : if (!was_value_parsed) {
356 313174 : int ext_size = 0;
357 313174 : if (legacy_idd_index < legacy_idd_fields_array.size()) {
358 : // std::string_view field_name = legacy_idd_fields_array[ legacy_idd_index ];
359 : // root[ field_name ] = "";
360 : } else {
361 9704 : auto const &legacy_idd_extensibles_array = legacy_idd_extensibles_iter.value();
362 9704 : ext_size = static_cast<int>(legacy_idd_extensibles_array.size());
363 : // std::string_view field_name = legacy_idd_extensibles_array[ extensible_index % ext_size ];
364 9704 : extensible_index++;
365 : // extensible[ field_name ] = "";
366 : }
367 313174 : if (ext_size && extensible_index % ext_size == 0) {
368 4820 : array_of_extensions.push_back(extensible);
369 4820 : extensible.clear();
370 : }
371 : }
372 4204233 : legacy_idd_index++;
373 4204233 : was_value_parsed = false;
374 4204233 : next_token(idf, index);
375 4204233 : if (token == Token::SEMICOLON) {
376 294489 : size_t min_fields = 0;
377 294489 : if (found_min_fields != schema_obj_loc.end()) {
378 162314 : min_fields = found_min_fields.value().get<size_t>();
379 : }
380 321040 : for (; legacy_idd_index < min_fields; legacy_idd_index++) {
381 : // std::string_view field_name = legacy_idd_fields_array[ legacy_idd_index ];
382 : // root[ field_name ] = "";
383 : }
384 294489 : if (!extensible.empty()) {
385 192 : array_of_extensions.push_back(extensible);
386 192 : extensible.clear();
387 : }
388 294489 : root["idf_max_fields"] = legacy_idd_index;
389 294489 : root["idf_max_extensible_fields"] = extensible_index;
390 294489 : break;
391 : }
392 10862553 : } else if (token == Token::EXCLAMATION) {
393 3061749 : eat_comment(idf, index);
394 3891060 : } else if (legacy_idd_index >= legacy_idd_fields_array.size()) {
395 1784196 : if (legacy_idd_extensibles_iter == legacy_idd.end()) {
396 0 : errors_.emplace_back(
397 0 : fmt::format("Line: {} Index: {} - Object contains more field values than maximum number of IDD fields and is not extensible.",
398 0 : cur_line_num,
399 0 : index_into_cur_line));
400 0 : success = false;
401 0 : return root;
402 : }
403 1784196 : if (schema_obj_extensions == nullptr) {
404 0 : errors_.emplace_back(fmt::format("Line: {} Index: {} - Object does not have extensible fields but should. Likely a parsing error.",
405 0 : cur_line_num,
406 0 : index_into_cur_line));
407 0 : success = false;
408 0 : return root;
409 : }
410 1784196 : auto const &legacy_idd_extensibles_array = legacy_idd_extensibles_iter.value();
411 1784196 : size_t const size = legacy_idd_extensibles_array.size();
412 1784196 : std::string const &field_name = legacy_idd_extensibles_array[extensible_index % size].get<std::string>();
413 1784196 : json val = parse_value(idf, index, success, schema_obj_extensions->at(field_name));
414 1784196 : if (!success) return root;
415 1784196 : extensible[field_name] = std::move(val);
416 1784196 : was_value_parsed = true;
417 1784196 : extensible_index++;
418 1784196 : if (extensible_index && extensible_index % size == 0) {
419 1323917 : array_of_extensions.push_back(extensible);
420 1323917 : extensible.clear();
421 : }
422 1784196 : } else {
423 2106864 : was_value_parsed = true;
424 2106864 : std::string const &field = legacy_idd_fields_array[legacy_idd_index].get<std::string>();
425 2106864 : auto const find_field_iter = schema_obj_props.find(field);
426 2106864 : if (find_field_iter == schema_obj_props.end()) {
427 236767 : if (field == "name") {
428 236767 : root[field] = parse_string(idf, index);
429 : } else {
430 0 : u64toa(cur_line_num, s);
431 0 : errors_.emplace_back(fmt::format("Line: {} - Field \"{}\" was not found.", s, field));
432 : }
433 : } else {
434 1870097 : json val = parse_value(idf, index, success, find_field_iter.value());
435 1870097 : if (!success) return root;
436 1870097 : root[field] = std::move(val);
437 1870097 : }
438 2106864 : if (!success) return root;
439 2106864 : }
440 10862553 : }
441 294489 : if (!array_of_extensions.empty()) {
442 99768 : root[extension_key] = std::move(array_of_extensions);
443 99768 : array_of_extensions = nullptr;
444 : }
445 294489 : return root;
446 294489 : }
447 :
448 2342534 : json IdfParser::parse_number(std::string_view idf, size_t &index)
449 : {
450 2342534 : eat_whitespace(idf, index);
451 :
452 2342534 : size_t save_i = index;
453 :
454 2342534 : bool running = true;
455 19938698 : while (running) {
456 17596164 : if (save_i == idf_size) {
457 0 : break;
458 : }
459 :
460 17596164 : char const c = idf[save_i];
461 17596164 : switch (c) {
462 2342534 : case '!':
463 : case ',':
464 : case ';':
465 : case '\r':
466 : case '\n':
467 2342534 : running = false;
468 2342534 : break;
469 15253630 : default:
470 15253630 : ++save_i;
471 : }
472 : }
473 :
474 2342534 : size_t diff = save_i - index;
475 2342534 : std::string_view value = idf.substr(index, diff);
476 2342534 : index_into_cur_line += diff;
477 2342534 : index = save_i;
478 :
479 2173157 : auto const convert_double = [&index, this](std::string_view str) -> json { // (AUTO_OK)
480 2173136 : size_t plus_sign = 0;
481 2173136 : if (str.front() == '+') {
482 5 : plus_sign = 1;
483 : }
484 2173136 : auto const str_end = str.data() + str.size(); // have to do this for MSVC // (AUTO_OK)
485 : double val;
486 2173136 : auto result = fast_float::from_chars(str.data() + plus_sign, str.data() + str.size(), val); // (AUTO_OK)
487 2173136 : if (result.ec == std::errc::invalid_argument || result.ec == std::errc::result_out_of_range) {
488 0 : return rtrim(str);
489 2173136 : } else if (result.ptr != str_end) {
490 21 : auto const initial_ptr = result.ptr; // (AUTO_OK)
491 42 : while (result.ptr != str_end) {
492 21 : if (*result.ptr != ' ') {
493 0 : break;
494 : }
495 21 : ++result.ptr;
496 : }
497 21 : if (result.ptr == str_end) {
498 21 : index -= (str_end - initial_ptr);
499 21 : this->index_into_cur_line -= (str_end - initial_ptr);
500 21 : return val;
501 : }
502 0 : return rtrim(str);
503 : }
504 2173115 : return val;
505 2342534 : };
506 :
507 4515670 : auto const convert_int = [&convert_double, &index, this](std::string_view str) -> json { // (AUTO_OK)
508 2342534 : auto const str_end = str.data() + str.size(); // have to do this for MSVC // (AUTO_OK)
509 : int val;
510 2342534 : auto result = FromChars::from_chars(str.data(), str.data() + str.size(), val); // (AUTO_OK)
511 2342534 : if (result.ec == std::errc::result_out_of_range || result.ec == std::errc::invalid_argument) {
512 3120 : return convert_double(str);
513 2339414 : } else if (result.ptr != str_end) {
514 2170016 : if (*result.ptr == '.' || *result.ptr == 'e' || *result.ptr == 'E') {
515 2170016 : return convert_double(str);
516 : } else {
517 0 : auto const initial_ptr = result.ptr; // (AUTO_OK)
518 0 : while (result.ptr != str_end) {
519 0 : if (*result.ptr != ' ') {
520 0 : break;
521 : }
522 0 : ++result.ptr;
523 : }
524 0 : if (result.ptr == str_end) {
525 0 : index -= (str_end - initial_ptr);
526 0 : this->index_into_cur_line -= (str_end - initial_ptr);
527 0 : return val;
528 : }
529 0 : return rtrim(str);
530 : }
531 : }
532 169398 : return val;
533 2342534 : };
534 :
535 4685068 : return convert_int(value);
536 : }
537 :
538 36004 : json IdfParser::parse_integer(std::string_view idf, size_t &index)
539 : {
540 36004 : eat_whitespace(idf, index);
541 :
542 36004 : size_t save_i = index;
543 :
544 36004 : bool running = true;
545 117459 : while (running) {
546 81455 : if (save_i == idf_size) {
547 0 : break;
548 : }
549 :
550 81455 : char const c = idf[save_i];
551 81455 : switch (c) {
552 36004 : case '!':
553 : case ',':
554 : case ';':
555 : case '\r':
556 : case '\n':
557 36004 : running = false;
558 36004 : break;
559 45451 : default:
560 45451 : ++save_i;
561 : }
562 : }
563 :
564 36004 : size_t diff = save_i - index;
565 36004 : std::string_view string_value = idf.substr(index, diff);
566 36004 : index_into_cur_line += diff;
567 36004 : index = save_i;
568 :
569 36004 : auto const string_end = string_value.data() + string_value.size(); // have to do this for MSVC // (AUTO_OK)
570 : int int_value;
571 : // Try using from_chars
572 36004 : auto result = FromChars::from_chars(string_value.data(), string_value.data() + string_value.size(), int_value); // (AUTO_OK)
573 36004 : if (result.ec == std::errc::result_out_of_range || result.ec == std::errc::invalid_argument) {
574 : // Failure, return the string
575 0 : return rtrim(string_value);
576 36004 : } else if (result.ptr != string_end) {
577 : // Didn't use the entire string, try again via double conversion + rounding
578 681 : size_t plus_sign = 0;
579 681 : if (string_value.front() == '+') {
580 0 : plus_sign = 1;
581 : }
582 : double double_value;
583 681 : auto fresult = fast_float::from_chars(string_value.data() + plus_sign, string_value.data() + string_value.size(), double_value); // (AUTO_OK)
584 681 : if (fresult.ec == std::errc::invalid_argument || fresult.ec == std::errc::result_out_of_range) {
585 : // Failure, return the string
586 0 : return rtrim(string_value);
587 : }
588 681 : int_value = static_cast<int>(std::round(double_value));
589 : }
590 36004 : return int_value;
591 : }
592 :
593 3654293 : json IdfParser::parse_value(std::string_view idf, size_t &index, bool &success, json const &field_loc)
594 : {
595 : Token token;
596 3654293 : auto const &field_type = field_loc.find("type");
597 3654293 : if (field_type != field_loc.end()) {
598 3279505 : if (field_type.value() == "number") {
599 2176512 : token = Token::NUMBER;
600 1102993 : } else if (field_type.value() == "integer") {
601 36004 : token = Token::INTEGER;
602 : } else {
603 1066989 : token = Token::STRING;
604 : }
605 : } else {
606 374788 : token = look_ahead(idf, index);
607 : }
608 :
609 3654293 : switch (token) {
610 1275755 : case Token::STRING: {
611 1275755 : std::string const parsed_string = parse_string(idf, index);
612 1275755 : auto const enum_it = field_loc.find("enum");
613 1275755 : if (enum_it != field_loc.end()) {
614 1791575 : for (auto const &enum_str : enum_it.value()) {
615 1791575 : std::string const str = enum_str.get<std::string>();
616 1791575 : if (icompare(str, parsed_string)) {
617 392922 : return str;
618 : }
619 2577419 : }
620 882833 : } else if (icompare(parsed_string, "Autosize") || icompare(parsed_string, "Autocalculate")) {
621 74968 : auto const default_it = field_loc.find("default");
622 74968 : auto const anyOf_it = field_loc.find("anyOf");
623 :
624 74968 : if (anyOf_it == field_loc.end()) {
625 0 : errors_.emplace_back(
626 0 : fmt::format("Line: {} Index: {} - Field cannot be Autosize or Autocalculate", cur_line_num, index_into_cur_line));
627 0 : return parsed_string;
628 : }
629 : // The following is hacky because it abuses knowing the consistent generated structure
630 : // in the future this might not hold true for the array indexes.
631 74968 : if (default_it != field_loc.end()) {
632 108788 : return field_loc.at("anyOf")[1]["enum"][1];
633 : } else {
634 41148 : return field_loc.at("anyOf")[1]["enum"][0];
635 : }
636 74968 : }
637 807865 : return parsed_string;
638 1275755 : }
639 2342534 : case Token::NUMBER: {
640 2342534 : return parse_number(idf, index);
641 : }
642 36004 : case Token::INTEGER: {
643 36004 : return parse_integer(idf, index);
644 : }
645 0 : case Token::NONE:
646 : case Token::END:
647 : case Token::EXCLAMATION:
648 : case Token::COMMA:
649 : case Token::SEMICOLON:
650 : default:
651 0 : break;
652 : }
653 0 : success = false;
654 0 : return nullptr;
655 3654293 : }
656 :
657 1807011 : std::string IdfParser::parse_string(std::string_view idf, size_t &index)
658 : {
659 1807011 : eat_whitespace(idf, index);
660 :
661 1807011 : std::string str;
662 : char c;
663 :
664 : while (true) {
665 33817336 : if (index == idf_size) {
666 0 : break;
667 : }
668 :
669 33817336 : c = idf[index];
670 33817336 : increment_both_index(index, index_into_cur_line);
671 33817336 : if (c == ',' || c == ';' || c == '!') {
672 1807011 : decrement_both_index(index, index_into_cur_line);
673 1807011 : break;
674 : } else {
675 32010325 : str += c;
676 : }
677 : }
678 :
679 3614022 : return rtrim(str);
680 1807011 : }
681 :
682 255370962 : void IdfParser::increment_both_index(size_t &index, size_t &line_index)
683 : {
684 255370962 : index++;
685 255370962 : line_index++;
686 255370962 : }
687 :
688 1807011 : void IdfParser::decrement_both_index(size_t &index, size_t &line_index)
689 : {
690 1807011 : index--;
691 1807011 : line_index--;
692 1807011 : }
693 :
694 20621012 : void IdfParser::eat_whitespace(std::string_view idf, size_t &index)
695 : {
696 98836645 : while (index < idf_size) {
697 98835850 : switch (idf[index]) {
698 76881722 : case ' ':
699 : case '\r':
700 : case '\t':
701 76881722 : increment_both_index(index, index_into_cur_line);
702 76881722 : continue;
703 1333911 : case '\n':
704 1333911 : increment_both_index(index, cur_line_num);
705 1333911 : beginning_of_line_index = index;
706 1333911 : index_into_cur_line = 0;
707 1333911 : continue;
708 20620217 : default:
709 20620217 : return;
710 : }
711 : }
712 : }
713 :
714 126903328 : void IdfParser::eat_comment(std::string_view idf, size_t &index)
715 : {
716 : while (true) {
717 126903328 : if (index == idf_size) break;
718 126903325 : if (idf[index] == '\n') {
719 3465862 : increment_both_index(index, cur_line_num);
720 3465862 : index_into_cur_line = 0;
721 3465862 : beginning_of_line_index = index;
722 3465862 : break;
723 : }
724 123437463 : increment_both_index(index, index_into_cur_line);
725 : }
726 3465865 : }
727 :
728 12231230 : IdfParser::Token IdfParser::look_ahead(std::string_view idf, size_t index)
729 : {
730 12231230 : size_t save_index = index;
731 12231230 : size_t save_line_num = cur_line_num;
732 12231230 : size_t save_line_index = index_into_cur_line;
733 12231230 : Token token = next_token(idf, save_index);
734 12231230 : cur_line_num = save_line_num;
735 12231230 : index_into_cur_line = save_line_index;
736 12231230 : return token;
737 : }
738 :
739 16435463 : IdfParser::Token IdfParser::next_token(std::string_view idf, size_t &index)
740 : {
741 16435463 : eat_whitespace(idf, index);
742 :
743 16435463 : if (index == idf_size) {
744 795 : return Token::END;
745 : }
746 :
747 16434668 : char const c = idf[index];
748 16434668 : increment_both_index(index, index_into_cur_line);
749 16434668 : switch (c) {
750 3465865 : case '!':
751 3465865 : return Token::EXCLAMATION;
752 7819488 : case ',':
753 7819488 : return Token::COMMA;
754 588978 : case ';':
755 588978 : return Token::SEMICOLON;
756 4560337 : default:
757 : static constexpr std::string_view numeric(".-+0123456789");
758 4560337 : if (numeric.find_first_of(c) != std::string::npos) {
759 2556233 : return Token::NUMBER;
760 : }
761 2004104 : return Token::STRING;
762 : }
763 : decrement_both_index(index, index_into_cur_line);
764 : return Token::NONE;
765 : }
766 :
767 0 : IdfParser::Token IdfParser::next_limited_token(std::string_view idf, size_t &index)
768 : {
769 0 : if (index == idf_size) {
770 0 : return Token::END;
771 : }
772 :
773 0 : char const c = idf[index];
774 0 : increment_both_index(index, index_into_cur_line);
775 0 : switch (c) {
776 0 : case '!':
777 0 : return Token::EXCLAMATION;
778 0 : case ',':
779 0 : return Token::COMMA;
780 0 : case ';':
781 0 : return Token::SEMICOLON;
782 0 : default:
783 0 : return Token::NONE;
784 : }
785 : }
786 :
787 1807011 : std::string IdfParser::rtrim(std::string_view str)
788 : {
789 : static constexpr std::string_view whitespace(" \t\0", 3);
790 1807011 : if (str.empty()) {
791 0 : return std::string{};
792 : }
793 1807011 : size_t const index = str.find_last_not_of(whitespace);
794 1807011 : if (index == std::string::npos) {
795 0 : return std::string{};
796 1807011 : } else if (index + 1 < str.length()) {
797 73 : return std::string{str.substr(0, index + 1)};
798 : }
799 1806938 : return std::string{str};
800 : }
|