Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2025, 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 3645107 : auto const icompare = [](std::string_view a, std::string_view b) { // (AUTO_OK)
59 3645107 : return (a.length() == b.length()
60 8817315 : ? std::equal(a.begin(), a.end(), b.begin(), [](char const c, char const d) { return (::tolower(c) == ::tolower(d)); })
61 3645107 : : 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 800 : json IdfParser::decode(std::string_view idf, json const &schema, bool &success)
71 : {
72 800 : 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 800 : json IdfParser::decode(std::string_view idf, size_t _idf_size, json const &schema, bool &success)
82 : {
83 800 : success = true;
84 800 : cur_line_num = 1;
85 800 : index_into_cur_line = 0;
86 800 : beginning_of_line_index = 0;
87 800 : idf_size = _idf_size;
88 :
89 800 : if (idf.empty()) {
90 0 : success = false;
91 0 : return nullptr;
92 : }
93 :
94 800 : size_t index = 0;
95 800 : 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 : }
128 0 : continue;
129 : }
130 0 : for (size_t j = 0; j < skipped_fields; j++) {
131 0 : encoded += end_of_field;
132 : }
133 0 : skipped_fields = 0;
134 0 : encoded += end_of_field;
135 0 : auto const &val = obj_in.value()[entry];
136 0 : if (val.is_string()) {
137 0 : encoded += val.get<std::string>();
138 0 : } else if (val.is_number_integer()) {
139 0 : encoded += std::to_string(val.get<int>());
140 : } else {
141 0 : dtoa(val.get<double>(), s);
142 0 : encoded += s;
143 : }
144 0 : }
145 :
146 0 : if (obj_in.value().find(extension_key) == obj_in.value().end()) {
147 0 : encoded += end_of_object;
148 0 : continue;
149 : }
150 :
151 0 : auto &extensions = obj_in.value()[extension_key];
152 0 : for (size_t extension_i = 0; extension_i < extensions.size(); extension_i++) {
153 0 : auto const &cur_extension_obj = extensions[extension_i];
154 0 : auto const &extensible = schema["properties"][obj.key()]["legacy_idd"]["extensibles"];
155 0 : for (size_t i = 0; i < extensible.size(); i++) {
156 0 : std::string const &tmp = extensible[i].get<std::string>();
157 0 : if (cur_extension_obj.find(tmp) == cur_extension_obj.end()) {
158 0 : skipped_fields++;
159 0 : continue;
160 : }
161 0 : for (size_t j = 0; j < skipped_fields; j++) {
162 0 : encoded += end_of_field;
163 : }
164 0 : skipped_fields = 0;
165 0 : encoded += end_of_field;
166 0 : if (cur_extension_obj[tmp].is_string()) {
167 0 : encoded += cur_extension_obj[tmp].get<std::string>();
168 : } else {
169 0 : dtoa(cur_extension_obj[tmp].get<double>(), s);
170 0 : encoded += s;
171 : }
172 0 : }
173 : }
174 0 : encoded += end_of_object;
175 0 : }
176 0 : }
177 0 : return encoded;
178 0 : }
179 :
180 303276 : std::string IdfParser::normalizeObjectType(std::string const &objectType)
181 : {
182 303276 : if (objectType.empty()) {
183 0 : return std::string{};
184 : }
185 303276 : std::string key = convertToUpper(objectType);
186 303276 : auto tmp_umit = objectTypeMap.find(key);
187 303276 : if (tmp_umit != objectTypeMap.end()) {
188 303276 : return tmp_umit->second;
189 : }
190 0 : return std::string{};
191 303276 : }
192 :
193 801 : std::vector<std::string> const &IdfParser::errors()
194 : {
195 801 : return errors_;
196 : }
197 :
198 801 : std::vector<std::string> const &IdfParser::warnings()
199 : {
200 801 : return warnings_;
201 : }
202 :
203 801 : bool IdfParser::hasErrors()
204 : {
205 801 : return !errors_.empty();
206 : }
207 :
208 800 : json IdfParser::parse_idf(std::string_view idf, size_t &index, bool &success, json const &schema)
209 : {
210 800 : json root;
211 : Token token;
212 800 : auto const &schema_properties = schema["properties"];
213 :
214 800 : objectTypeMap.reserve(schema_properties.size());
215 680000 : for (auto it = schema_properties.begin(); it != schema_properties.end(); ++it) {
216 679200 : std::string key = convertToUpper(it.key());
217 679200 : objectTypeMap.emplace(std::move(key), it.key());
218 680000 : }
219 :
220 800 : if (idf_size > 3) {
221 : // UTF-8 Byte Order Mark
222 800 : if (idf[0] == '\xEF' && idf[1] == '\xBB' && idf[2] == '\xBF') {
223 0 : index += 3;
224 0 : index_into_cur_line += 3;
225 : }
226 : }
227 :
228 800 : int idfObjectCount = 0;
229 : while (true) {
230 718190 : token = look_ahead(idf, index);
231 718190 : if (token == Token::END) {
232 800 : break;
233 717390 : } else if (token == Token::NONE) {
234 0 : success = false;
235 0 : return root;
236 717390 : } else if (token == Token::SEMICOLON) {
237 0 : next_token(idf, index);
238 0 : continue;
239 717390 : } else if (token == Token::COMMA) {
240 0 : errors_.emplace_back(fmt::format("Line: {} Index: {} - Extraneous comma found.", cur_line_num, index_into_cur_line));
241 0 : success = false;
242 0 : return root;
243 717390 : } else if (token == Token::EXCLAMATION) {
244 414114 : eat_comment(idf, index);
245 : } else {
246 303276 : ++idfObjectCount;
247 303276 : std::string const parsed_obj_name = parse_string(idf, index);
248 303276 : std::string const obj_name = normalizeObjectType(parsed_obj_name);
249 303276 : if (obj_name.empty()) {
250 0 : errors_.emplace_back(
251 0 : fmt::format("Line: {} Index: {} - \"{}\" is not a valid Object Type.", cur_line_num, index_into_cur_line, parsed_obj_name));
252 0 : while (token != Token::SEMICOLON && token != Token::END) {
253 0 : token = next_token(idf, index);
254 : }
255 0 : continue;
256 : }
257 :
258 303276 : bool object_success = true;
259 303276 : json const &obj_loc = schema_properties[obj_name];
260 303276 : json const &legacy_idd = obj_loc["legacy_idd"];
261 303276 : json obj = parse_object(idf, index, object_success, legacy_idd, obj_loc, idfObjectCount);
262 303276 : if (!object_success) {
263 0 : auto found_index = idf.find_first_of('\n', beginning_of_line_index);
264 0 : std::string line;
265 0 : if (found_index != std::string::npos) {
266 0 : line = idf.substr(beginning_of_line_index, found_index - beginning_of_line_index - 1);
267 : }
268 0 : errors_.emplace_back(
269 0 : fmt::format("Line: {} Index: {} - Error parsing \"{}\". Error in following line.", cur_line_num, index_into_cur_line, obj_name));
270 0 : errors_.emplace_back(fmt::format("~~~ {}", line));
271 0 : success = false;
272 0 : continue;
273 0 : }
274 303276 : u64toa(root[obj_name].size() + 1, s);
275 303276 : std::string name = fmt::format("{} {}", obj_name, s);
276 :
277 303276 : if (!obj.is_null()) {
278 303276 : auto const name_iter = obj.find("name");
279 : // If you find a name field, use that
280 303276 : if (name_iter != obj.end()) {
281 244641 : name = name_iter.value().get<std::string>();
282 244641 : obj.erase(name_iter);
283 : } else {
284 : // Otherwise, see if it should have a name field
285 58635 : auto const it = obj_loc.find("name");
286 58635 : if (it != obj_loc.end()) {
287 : // Let it slide, as a blank string, to be handled in the appropriate GetInput routine
288 1 : name = "";
289 : }
290 58635 : }
291 303276 : }
292 :
293 303276 : if (root[obj_name].find(name) != root[obj_name].end()) {
294 0 : errors_.emplace_back(
295 0 : fmt::format(R"(Duplicate name found for object of type "{}" named "{}". Overwriting existing object.)", obj_name, name));
296 : }
297 :
298 303276 : root[obj_name][name] = std::move(obj);
299 303276 : }
300 717390 : }
301 :
302 800 : return root;
303 0 : }
304 :
305 303276 : json IdfParser::parse_object(
306 : std::string_view idf, size_t &index, bool &success, json const &legacy_idd, json const &schema_obj_loc, int idfObjectCount)
307 : {
308 303276 : json root = json::object();
309 303276 : json extensible = json::object();
310 303276 : json array_of_extensions = json::array();
311 : Token token;
312 303276 : std::string extension_key;
313 303276 : size_t legacy_idd_index = 0;
314 303276 : size_t extensible_index = 0;
315 303276 : success = true;
316 303276 : bool was_value_parsed = false;
317 303276 : auto const &legacy_idd_fields_array = legacy_idd["fields"];
318 303276 : auto const legacy_idd_extensibles_iter = legacy_idd.find("extensibles");
319 :
320 303276 : auto const &schema_patternProperties = schema_obj_loc["patternProperties"];
321 303276 : std::string patternProperty;
322 606552 : int dot_star_present = schema_patternProperties.count(".*");
323 303276 : int no_whitespace_present = schema_patternProperties.count(R"(^.*\S.*$)");
324 303276 : if (dot_star_present) {
325 62795 : patternProperty = ".*";
326 240481 : } else if (no_whitespace_present) {
327 240481 : patternProperty = R"(^.*\S.*$)";
328 : } else {
329 0 : throw std::runtime_error(R"(The patternProperties value is not a valid choice (".*", "^.*\S.*$"))");
330 : }
331 303276 : auto const &schema_obj_props = schema_patternProperties[patternProperty]["properties"];
332 303276 : auto key = legacy_idd.find("extension");
333 :
334 303276 : json const *schema_obj_extensions = nullptr;
335 303276 : if (legacy_idd_extensibles_iter != legacy_idd.end()) {
336 103349 : if (key == legacy_idd.end()) {
337 0 : errors_.emplace_back("\"extension\" key not found in schema. Need to add to list in modify_schema.py.");
338 0 : success = false;
339 0 : return root;
340 : }
341 103349 : extension_key = key.value().get<std::string>();
342 103349 : schema_obj_extensions = &schema_obj_props[extension_key]["items"]["properties"];
343 : }
344 :
345 303276 : root["idf_order"] = idfObjectCount;
346 :
347 303276 : auto const found_min_fields = schema_obj_loc.find("min_fields");
348 :
349 303276 : index += 1;
350 :
351 : while (true) {
352 11435413 : token = look_ahead(idf, index);
353 11435413 : root["idf_max_fields"] = legacy_idd_index;
354 11435413 : root["idf_max_extensible_fields"] = extensible_index;
355 11435413 : if (token == Token::NONE) {
356 0 : success = false;
357 0 : return root;
358 11435413 : } else if (token == Token::END) {
359 0 : return root;
360 11435413 : } else if (token == Token::COMMA || token == Token::SEMICOLON) {
361 4309913 : if (!was_value_parsed) {
362 328237 : int ext_size = 0;
363 328237 : if (legacy_idd_index < legacy_idd_fields_array.size()) {
364 : // std::string_view field_name = legacy_idd_fields_array[ legacy_idd_index ];
365 : // root[ field_name ] = "";
366 : } else {
367 10294 : auto const &legacy_idd_extensibles_array = legacy_idd_extensibles_iter.value();
368 10294 : ext_size = static_cast<int>(legacy_idd_extensibles_array.size());
369 : // std::string_view field_name = legacy_idd_extensibles_array[ extensible_index % ext_size ];
370 10294 : extensible_index++;
371 : // extensible[ field_name ] = "";
372 : }
373 328237 : if (ext_size && extensible_index % ext_size == 0) {
374 5115 : array_of_extensions.push_back(extensible);
375 5115 : extensible.clear();
376 : }
377 : }
378 4309913 : legacy_idd_index++;
379 4309913 : was_value_parsed = false;
380 4309913 : next_token(idf, index);
381 4309913 : if (token == Token::SEMICOLON) {
382 303276 : size_t min_fields = 0;
383 303276 : if (found_min_fields != schema_obj_loc.end()) {
384 167453 : min_fields = found_min_fields.value().get<size_t>();
385 : }
386 330906 : for (; legacy_idd_index < min_fields; legacy_idd_index++) {
387 : // std::string_view field_name = legacy_idd_fields_array[ legacy_idd_index ];
388 : // root[ field_name ] = "";
389 : }
390 303276 : if (!extensible.empty()) {
391 192 : array_of_extensions.push_back(extensible);
392 192 : extensible.clear();
393 : }
394 303276 : root["idf_max_fields"] = legacy_idd_index;
395 303276 : root["idf_max_extensible_fields"] = extensible_index;
396 303276 : break;
397 : }
398 11132137 : } else if (token == Token::EXCLAMATION) {
399 3143823 : eat_comment(idf, index);
400 3981677 : } else if (legacy_idd_index >= legacy_idd_fields_array.size()) {
401 1812303 : if (legacy_idd_extensibles_iter == legacy_idd.end()) {
402 0 : errors_.emplace_back(
403 0 : fmt::format("Line: {} Index: {} - Object contains more field values than maximum number of IDD fields and is not extensible.",
404 0 : cur_line_num,
405 0 : index_into_cur_line));
406 0 : success = false;
407 0 : return root;
408 : }
409 1812303 : if (schema_obj_extensions == nullptr) {
410 0 : errors_.emplace_back(fmt::format("Line: {} Index: {} - Object does not have extensible fields but should. Likely a parsing error.",
411 0 : cur_line_num,
412 0 : index_into_cur_line));
413 0 : success = false;
414 0 : return root;
415 : }
416 1812303 : auto const &legacy_idd_extensibles_array = legacy_idd_extensibles_iter.value();
417 1812303 : size_t const size = legacy_idd_extensibles_array.size();
418 1812303 : std::string const &field_name = legacy_idd_extensibles_array[extensible_index % size].get<std::string>();
419 1812303 : json val = parse_value(idf, index, success, schema_obj_extensions->at(field_name));
420 1812303 : if (!success) {
421 0 : return root;
422 : }
423 1812303 : extensible[field_name] = std::move(val);
424 1812303 : was_value_parsed = true;
425 1812303 : extensible_index++;
426 1812303 : if (extensible_index && extensible_index % size == 0) {
427 1339381 : array_of_extensions.push_back(extensible);
428 1339381 : extensible.clear();
429 : }
430 1812303 : } else {
431 2169374 : was_value_parsed = true;
432 2169374 : std::string const &field = legacy_idd_fields_array[legacy_idd_index].get<std::string>();
433 2169374 : auto const find_field_iter = schema_obj_props.find(field);
434 2169374 : if (find_field_iter == schema_obj_props.end()) {
435 244641 : if (field == "name") {
436 244641 : root[field] = parse_string(idf, index);
437 : } else {
438 0 : u64toa(cur_line_num, s);
439 0 : errors_.emplace_back(fmt::format("Line: {} - Field \"{}\" was not found.", s, field));
440 : }
441 : } else {
442 1924733 : json val = parse_value(idf, index, success, find_field_iter.value());
443 1924733 : if (!success) {
444 0 : return root;
445 : }
446 1924733 : root[field] = std::move(val);
447 1924733 : }
448 2169374 : if (!success) {
449 0 : return root;
450 : }
451 2169374 : }
452 11132137 : }
453 303276 : if (!array_of_extensions.empty()) {
454 102964 : root[extension_key] = std::move(array_of_extensions);
455 102964 : array_of_extensions = nullptr;
456 : }
457 303276 : return root;
458 303276 : }
459 :
460 2377797 : json IdfParser::parse_number(std::string_view idf, size_t &index)
461 : {
462 2377797 : eat_whitespace(idf, index);
463 :
464 2377797 : size_t save_i = index;
465 :
466 2377797 : bool running = true;
467 20251078 : while (running) {
468 17873281 : if (save_i == idf_size) {
469 0 : break;
470 : }
471 :
472 17873281 : char const c = idf[save_i];
473 17873281 : switch (c) {
474 2377797 : case '!':
475 : case ',':
476 : case ';':
477 : case '\r':
478 : case '\n':
479 2377797 : running = false;
480 2377797 : break;
481 15495484 : default:
482 15495484 : ++save_i;
483 : }
484 : }
485 :
486 2377797 : size_t diff = save_i - index;
487 2377797 : std::string_view value = idf.substr(index, diff);
488 2377797 : index_into_cur_line += diff;
489 2377797 : index = save_i;
490 :
491 2205112 : auto const convert_double = [&index, this](std::string_view str) -> json { // (AUTO_OK)
492 2205112 : size_t plus_sign = 0;
493 2205112 : if (str.front() == '+') {
494 5 : plus_sign = 1;
495 : }
496 2205112 : auto const str_end = str.data() + str.size(); // have to do this for MSVC // (AUTO_OK)
497 : double val;
498 2205112 : auto result = fast_float::from_chars(str.data() + plus_sign, str.data() + str.size(), val); // (AUTO_OK)
499 2205112 : if (result.ec == std::errc::invalid_argument || result.ec == std::errc::result_out_of_range) {
500 0 : return rtrim(str);
501 2205112 : } else if (result.ptr != str_end) {
502 21 : auto const initial_ptr = result.ptr; // (AUTO_OK)
503 42 : while (result.ptr != str_end) {
504 21 : if (*result.ptr != ' ') {
505 0 : break;
506 : }
507 21 : ++result.ptr;
508 : }
509 21 : if (result.ptr == str_end) {
510 21 : index -= (str_end - initial_ptr);
511 21 : this->index_into_cur_line -= (str_end - initial_ptr);
512 21 : return val;
513 : }
514 0 : return rtrim(str);
515 : }
516 2205091 : return val;
517 2377797 : };
518 :
519 2377797 : auto const convert_int = [&convert_double, &index, this](std::string_view str) -> json { // (AUTO_OK)
520 2377797 : auto const str_end = str.data() + str.size(); // have to do this for MSVC // (AUTO_OK)
521 : int val;
522 2377797 : auto result = FromChars::from_chars(str.data(), str.data() + str.size(), val); // (AUTO_OK)
523 2377797 : if (result.ec == std::errc::result_out_of_range || result.ec == std::errc::invalid_argument) {
524 3139 : return convert_double(str);
525 2374658 : } else if (result.ptr != str_end) {
526 2201973 : if (*result.ptr == '.' || *result.ptr == 'e' || *result.ptr == 'E') {
527 2201973 : return convert_double(str);
528 : } else {
529 0 : auto const initial_ptr = result.ptr; // (AUTO_OK)
530 0 : while (result.ptr != str_end) {
531 0 : if (*result.ptr != ' ') {
532 0 : break;
533 : }
534 0 : ++result.ptr;
535 : }
536 0 : if (result.ptr == str_end) {
537 0 : index -= (str_end - initial_ptr);
538 0 : this->index_into_cur_line -= (str_end - initial_ptr);
539 0 : return val;
540 : }
541 0 : return rtrim(str);
542 : }
543 : }
544 172685 : return val;
545 2377797 : };
546 :
547 4755594 : return convert_int(value);
548 : }
549 :
550 37502 : json IdfParser::parse_integer(std::string_view idf, size_t &index)
551 : {
552 37502 : eat_whitespace(idf, index);
553 :
554 37502 : size_t save_i = index;
555 :
556 37502 : bool running = true;
557 122242 : while (running) {
558 84740 : if (save_i == idf_size) {
559 0 : break;
560 : }
561 :
562 84740 : char const c = idf[save_i];
563 84740 : switch (c) {
564 37502 : case '!':
565 : case ',':
566 : case ';':
567 : case '\r':
568 : case '\n':
569 37502 : running = false;
570 37502 : break;
571 47238 : default:
572 47238 : ++save_i;
573 : }
574 : }
575 :
576 37502 : size_t diff = save_i - index;
577 37502 : std::string_view string_value = idf.substr(index, diff);
578 37502 : index_into_cur_line += diff;
579 37502 : index = save_i;
580 :
581 37502 : auto const string_end = string_value.data() + string_value.size(); // have to do this for MSVC // (AUTO_OK)
582 : int int_value;
583 : // Try using from_chars
584 37502 : auto result = FromChars::from_chars(string_value.data(), string_value.data() + string_value.size(), int_value); // (AUTO_OK)
585 37502 : if (result.ec == std::errc::result_out_of_range || result.ec == std::errc::invalid_argument) {
586 : // Failure, return the string
587 0 : return rtrim(string_value);
588 37502 : } else if (result.ptr != string_end) {
589 : // Didn't use the entire string, try again via double conversion + rounding
590 686 : size_t plus_sign = 0;
591 686 : if (string_value.front() == '+') {
592 0 : plus_sign = 1;
593 : }
594 : double double_value;
595 686 : auto fresult = fast_float::from_chars(string_value.data() + plus_sign, string_value.data() + string_value.size(), double_value); // (AUTO_OK)
596 686 : if (fresult.ec == std::errc::invalid_argument || fresult.ec == std::errc::result_out_of_range) {
597 : // Failure, return the string
598 0 : return rtrim(string_value);
599 : }
600 686 : int_value = static_cast<int>(std::round(double_value));
601 : }
602 37502 : return int_value;
603 : }
604 :
605 3737036 : json IdfParser::parse_value(std::string_view idf, size_t &index, bool &success, json const &field_loc)
606 : {
607 : Token token;
608 3737036 : auto const &field_type = field_loc.find("type");
609 3737036 : if (field_type != field_loc.end()) {
610 3350249 : if (field_type.value() == "number") {
611 2208232 : token = Token::NUMBER;
612 1142017 : } else if (field_type.value() == "integer") {
613 37502 : token = Token::INTEGER;
614 : } else {
615 1104515 : token = Token::STRING;
616 : }
617 : } else {
618 386787 : token = look_ahead(idf, index);
619 : }
620 :
621 3737036 : switch (token) {
622 1321737 : case Token::STRING: {
623 1321737 : std::string const parsed_string = parse_string(idf, index);
624 1321737 : auto const enum_it = field_loc.find("enum");
625 1321737 : if (enum_it != field_loc.end()) {
626 1855432 : for (auto const &enum_str : enum_it.value()) {
627 1855432 : std::string const str = enum_str.get<std::string>();
628 1855432 : if (icompare(str, parsed_string)) {
629 403674 : return str;
630 : }
631 2662780 : }
632 918063 : } else if (icompare(parsed_string, "Autosize") || icompare(parsed_string, "Autocalculate")) {
633 157460 : auto const default_it = field_loc.find("default");
634 78730 : auto const anyOf_it = field_loc.find("anyOf");
635 :
636 78730 : if (anyOf_it == field_loc.end()) {
637 0 : errors_.emplace_back(
638 0 : fmt::format("Line: {} Index: {} - Field cannot be Autosize or Autocalculate", cur_line_num, index_into_cur_line));
639 0 : return parsed_string;
640 : }
641 : // The following is hacky because it abuses knowing the consistent generated structure
642 : // in the future this might not hold true for the array indexes.
643 78730 : if (default_it != field_loc.end()) {
644 112798 : return field_loc.at("anyOf")[1]["enum"][1];
645 : } else {
646 44662 : return field_loc.at("anyOf")[1]["enum"][0];
647 : }
648 78730 : }
649 839333 : return parsed_string;
650 1321737 : }
651 2377797 : case Token::NUMBER: {
652 2377797 : return parse_number(idf, index);
653 : }
654 37502 : case Token::INTEGER: {
655 37502 : return parse_integer(idf, index);
656 : }
657 0 : case Token::NONE:
658 : case Token::END:
659 : case Token::EXCLAMATION:
660 : case Token::COMMA:
661 : case Token::SEMICOLON:
662 : default:
663 0 : break;
664 : }
665 0 : success = false;
666 0 : return nullptr;
667 3737036 : }
668 :
669 1869654 : std::string IdfParser::parse_string(std::string_view idf, size_t &index)
670 : {
671 1869654 : eat_whitespace(idf, index);
672 :
673 1869654 : std::string str;
674 :
675 : while (true) {
676 35274130 : if (index == idf_size) {
677 0 : break;
678 : }
679 :
680 35274130 : char c = idf[index];
681 35274130 : increment_both_index(index, index_into_cur_line);
682 35274130 : if (c == ',' || c == ';' || c == '!') {
683 1869654 : decrement_both_index(index, index_into_cur_line);
684 1869654 : break;
685 : } else {
686 33404476 : str += c;
687 : }
688 33404476 : }
689 :
690 3739308 : return rtrim(str);
691 1869654 : }
692 :
693 262886612 : void IdfParser::increment_both_index(size_t &index, size_t &line_index)
694 : {
695 262886612 : index++;
696 262886612 : line_index++;
697 262886612 : }
698 :
699 1869654 : void IdfParser::decrement_both_index(size_t &index, size_t &line_index)
700 : {
701 1869654 : index--;
702 1869654 : line_index--;
703 1869654 : }
704 :
705 21135256 : void IdfParser::eat_whitespace(std::string_view idf, size_t &index)
706 : {
707 101352196 : while (index < idf_size) {
708 101351396 : switch (idf[index]) {
709 78845832 : case ' ':
710 : case '\r':
711 : case '\t':
712 78845832 : increment_both_index(index, index_into_cur_line);
713 78845832 : continue;
714 1371108 : case '\n':
715 1371108 : increment_both_index(index, cur_line_num);
716 1371108 : beginning_of_line_index = index;
717 1371108 : index_into_cur_line = 0;
718 1371108 : continue;
719 21134456 : default:
720 21134456 : return;
721 : }
722 : }
723 : }
724 :
725 3557937 : void IdfParser::eat_comment(std::string_view idf, size_t &index)
726 : {
727 : while (true) {
728 130546042 : if (index == idf_size) {
729 3 : break;
730 : }
731 130546039 : if (idf[index] == '\n') {
732 3557934 : increment_both_index(index, cur_line_num);
733 3557934 : index_into_cur_line = 0;
734 3557934 : beginning_of_line_index = index;
735 3557934 : break;
736 : }
737 126988105 : increment_both_index(index, index_into_cur_line);
738 : }
739 3557937 : }
740 :
741 12540390 : IdfParser::Token IdfParser::look_ahead(std::string_view idf, size_t index)
742 : {
743 12540390 : size_t save_index = index;
744 12540390 : size_t save_line_num = cur_line_num;
745 12540390 : size_t save_line_index = index_into_cur_line;
746 12540390 : Token token = next_token(idf, save_index);
747 12540390 : cur_line_num = save_line_num;
748 12540390 : index_into_cur_line = save_line_index;
749 12540390 : return token;
750 : }
751 :
752 16850303 : IdfParser::Token IdfParser::next_token(std::string_view idf, size_t &index)
753 : {
754 16850303 : eat_whitespace(idf, index);
755 :
756 16850303 : if (index == idf_size) {
757 800 : return Token::END;
758 : }
759 :
760 16849503 : char const c = idf[index];
761 16849503 : increment_both_index(index, index_into_cur_line);
762 16849503 : switch (c) {
763 3557937 : case '!':
764 3557937 : return Token::EXCLAMATION;
765 8013274 : case ',':
766 8013274 : return Token::COMMA;
767 606552 : case ';':
768 606552 : return Token::SEMICOLON;
769 4671740 : default:
770 : static constexpr std::string_view numeric(".-+0123456789");
771 4671740 : if (numeric.find_first_of(c) != std::string::npos) {
772 2598359 : return Token::NUMBER;
773 : }
774 2073381 : return Token::STRING;
775 : }
776 : decrement_both_index(index, index_into_cur_line);
777 : return Token::NONE;
778 : }
779 :
780 0 : IdfParser::Token IdfParser::next_limited_token(std::string_view idf, size_t &index)
781 : {
782 0 : if (index == idf_size) {
783 0 : return Token::END;
784 : }
785 :
786 0 : char const c = idf[index];
787 0 : increment_both_index(index, index_into_cur_line);
788 0 : switch (c) {
789 0 : case '!':
790 0 : return Token::EXCLAMATION;
791 0 : case ',':
792 0 : return Token::COMMA;
793 0 : case ';':
794 0 : return Token::SEMICOLON;
795 0 : default:
796 0 : return Token::NONE;
797 : }
798 : }
799 :
800 1869654 : std::string IdfParser::rtrim(std::string_view str)
801 : {
802 : static constexpr std::string_view whitespace(" \t\0", 3);
803 1869654 : if (str.empty()) {
804 0 : return std::string{};
805 : }
806 1869654 : size_t const index = str.find_last_not_of(whitespace);
807 1869654 : if (index == std::string::npos) {
808 0 : return std::string{};
809 1869654 : } else if (index + 1 < str.length()) {
810 219 : return std::string{str.substr(0, index + 1)};
811 : }
812 3739162 : return std::string{str};
813 : }
|