LCOV - code coverage report
Current view: top level - EnergyPlus/InputProcessing - IdfParser.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 86.0 % 499 429
Test Date: 2025-05-22 16:09:37 Functions: 88.9 % 27 24

            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       985692 : auto const icompare = [](std::string_view a, std::string_view b) { // (AUTO_OK)
      59       985692 :     return (a.length() == b.length()
      60      1495738 :                 ? std::equal(a.begin(), a.end(), b.begin(), [](char const c, char const d) { return (::tolower(c) == ::tolower(d)); })
      61       985692 :                 : 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         1364 : json IdfParser::decode(std::string_view idf, json const &schema, bool &success)
      71              : {
      72         1364 :     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         1364 : json IdfParser::decode(std::string_view idf, size_t _idf_size, json const &schema, bool &success)
      82              : {
      83         1364 :     success = true;
      84         1364 :     cur_line_num = 1;
      85         1364 :     index_into_cur_line = 0;
      86         1364 :     beginning_of_line_index = 0;
      87         1364 :     idf_size = _idf_size;
      88              : 
      89         1364 :     if (idf.empty()) {
      90            0 :         success = false;
      91            0 :         return nullptr;
      92              :     }
      93              : 
      94         1364 :     size_t index = 0;
      95         1364 :     return parse_idf(idf, index, success, schema);
      96              : }
      97              : 
      98            5 : 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            5 :     std::string encoded, extension_key;
     104            5 :     if (idf_size > 0) {
     105            5 :         encoded.reserve(idf_size);
     106              :     } else {
     107            0 :         encoded.reserve(root.size() * 1024);
     108              :     }
     109              : 
     110           29 :     for (auto obj = root.begin(); obj != root.end(); ++obj) {
     111           24 :         const auto &legacy_idd = schema["properties"][obj.key()]["legacy_idd"];
     112           24 :         const auto &legacy_idd_field = legacy_idd["fields"];
     113           24 :         auto key = legacy_idd.find("extension");
     114           24 :         if (key != legacy_idd.end()) {
     115            2 :             extension_key = key.value().get<std::string>();
     116              :         }
     117           48 :         for (auto obj_in = obj.value().begin(); obj_in != obj.value().end(); ++obj_in) {
     118           24 :             encoded += obj.key();
     119           24 :             size_t skipped_fields = 0;
     120          134 :             for (size_t i = 0; i < legacy_idd_field.size(); i++) {
     121          110 :                 std::string const &entry = legacy_idd_field[i].get<std::string>();
     122          110 :                 if (obj_in.value().find(entry) == obj_in.value().end()) {
     123           16 :                     if (entry == "name")
     124           27 :                         encoded += std::string{end_of_field} + obj_in.key();
     125              :                     else
     126            7 :                         skipped_fields++;
     127           16 :                     continue;
     128              :                 }
     129          100 :                 for (size_t j = 0; j < skipped_fields; j++)
     130            6 :                     encoded += end_of_field;
     131           94 :                 skipped_fields = 0;
     132           94 :                 encoded += end_of_field;
     133           94 :                 auto const &val = obj_in.value()[entry];
     134           94 :                 if (val.is_string()) {
     135           52 :                     encoded += val.get<std::string>();
     136           42 :                 } else if (val.is_number_integer()) {
     137           21 :                     encoded += std::to_string(val.get<int>());
     138              :                 } else {
     139           21 :                     dtoa(val.get<double>(), s);
     140           21 :                     encoded += s;
     141              :                 }
     142          110 :             }
     143              : 
     144           24 :             if (obj_in.value().find(extension_key) == obj_in.value().end()) {
     145           22 :                 encoded += end_of_object;
     146           22 :                 continue;
     147              :             }
     148              : 
     149            2 :             auto &extensions = obj_in.value()[extension_key];
     150           24 :             for (size_t extension_i = 0; extension_i < extensions.size(); extension_i++) {
     151           22 :                 auto const &cur_extension_obj = extensions[extension_i];
     152           22 :                 auto const &extensible = schema["properties"][obj.key()]["legacy_idd"]["extensibles"];
     153           52 :                 for (size_t i = 0; i < extensible.size(); i++) {
     154           30 :                     std::string const &tmp = extensible[i].get<std::string>();
     155           30 :                     if (cur_extension_obj.find(tmp) == cur_extension_obj.end()) {
     156            1 :                         skipped_fields++;
     157            1 :                         continue;
     158              :                     }
     159           30 :                     for (size_t j = 0; j < skipped_fields; j++)
     160            1 :                         encoded += end_of_field;
     161           29 :                     skipped_fields = 0;
     162           29 :                     encoded += end_of_field;
     163           29 :                     if (cur_extension_obj[tmp].is_string()) {
     164           17 :                         encoded += cur_extension_obj[tmp].get<std::string>();
     165              :                     } else {
     166           12 :                         dtoa(cur_extension_obj[tmp].get<double>(), s);
     167           12 :                         encoded += s;
     168              :                     }
     169           30 :                 }
     170              :             }
     171            2 :             encoded += end_of_object;
     172              :         }
     173              :     }
     174           10 :     return encoded;
     175            5 : }
     176              : 
     177        30848 : std::string IdfParser::normalizeObjectType(std::string const &objectType)
     178              : {
     179        30848 :     if (objectType.empty()) return std::string{};
     180        30848 :     std::string key = convertToUpper(objectType);
     181        30848 :     auto tmp_umit = objectTypeMap.find(key);
     182        30848 :     if (tmp_umit != objectTypeMap.end()) {
     183        30847 :         return tmp_umit->second;
     184              :     }
     185            1 :     return std::string{};
     186        30848 : }
     187              : 
     188         1364 : std::vector<std::string> const &IdfParser::errors()
     189              : {
     190         1364 :     return errors_;
     191              : }
     192              : 
     193         1364 : std::vector<std::string> const &IdfParser::warnings()
     194              : {
     195         1364 :     return warnings_;
     196              : }
     197              : 
     198         1340 : bool IdfParser::hasErrors()
     199              : {
     200         1340 :     return !errors_.empty();
     201              : }
     202              : 
     203         1364 : json IdfParser::parse_idf(std::string_view idf, size_t &index, bool &success, json const &schema)
     204              : {
     205         1364 :     json root;
     206              :     Token token;
     207         1364 :     auto const &schema_properties = schema["properties"];
     208              : 
     209         1364 :     objectTypeMap.reserve(schema_properties.size());
     210      1159400 :     for (auto it = schema_properties.begin(); it != schema_properties.end(); ++it) {
     211      1158036 :         std::string key = convertToUpper(it.key());
     212      1158036 :         objectTypeMap.emplace(std::move(key), it.key());
     213      1158036 :     }
     214              : 
     215         1364 :     if (idf_size > 3) {
     216              :         // UTF-8 Byte Order Mark
     217         1364 :         if (idf[0] == '\xEF' && idf[1] == '\xBB' && idf[2] == '\xBF') {
     218            1 :             index += 3;
     219            1 :             index_into_cur_line += 3;
     220              :         }
     221              :     }
     222              : 
     223         1364 :     int idfObjectCount = 0;
     224              :     while (true) {
     225        80278 :         token = look_ahead(idf, index);
     226        80278 :         if (token == Token::END) {
     227         1364 :             break;
     228        78914 :         } else if (token == Token::NONE) {
     229            0 :             success = false;
     230            0 :             return root;
     231        78914 :         } else if (token == Token::SEMICOLON) {
     232            0 :             next_token(idf, index);
     233            0 :             continue;
     234        78914 :         } 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        78914 :         } else if (token == Token::EXCLAMATION) {
     239        48066 :             eat_comment(idf, index);
     240              :         } else {
     241        30848 :             ++idfObjectCount;
     242        30848 :             std::string const parsed_obj_name = parse_string(idf, index);
     243        30848 :             std::string const obj_name = normalizeObjectType(parsed_obj_name);
     244        30848 :             if (obj_name.empty()) {
     245            2 :                 errors_.emplace_back(
     246            2 :                     fmt::format("Line: {} Index: {} - \"{}\" is not a valid Object Type.", cur_line_num, index_into_cur_line, parsed_obj_name));
     247            4 :                 while (token != Token::SEMICOLON && token != Token::END)
     248            3 :                     token = next_token(idf, index);
     249            1 :                 continue;
     250              :             }
     251              : 
     252        30847 :             bool object_success = true;
     253        30847 :             json const &obj_loc = schema_properties[obj_name];
     254        30847 :             json const &legacy_idd = obj_loc["legacy_idd"];
     255        30847 :             json obj = parse_object(idf, index, object_success, legacy_idd, obj_loc, idfObjectCount);
     256        30847 :             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        30847 :             u64toa(root[obj_name].size() + 1, s);
     269        30847 :             std::string name = fmt::format("{} {}", obj_name, s);
     270              : 
     271        30847 :             if (!obj.is_null()) {
     272        30847 :                 auto const name_iter = obj.find("name");
     273              :                 // If you find a name field, use that
     274        30847 :                 if (name_iter != obj.end()) {
     275        24119 :                     name = name_iter.value().get<std::string>();
     276        24119 :                     obj.erase(name_iter);
     277              :                 } else {
     278              :                     // Otherwise, see if it should have a name field
     279         6728 :                     auto const it = obj_loc.find("name");
     280         6728 :                     if (it != obj_loc.end()) {
     281              :                         // Let it slide, as a blank string, to be handled in the appropriate GetInput routine
     282            4 :                         name = "";
     283              :                     }
     284              :                 }
     285              :             }
     286              : 
     287        30847 :             if (root[obj_name].find(name) != root[obj_name].end()) {
     288            1 :                 errors_.emplace_back(
     289            1 :                     fmt::format(R"(Duplicate name found for object of type "{}" named "{}". Overwriting existing object.)", obj_name, name));
     290              :             }
     291              : 
     292        30847 :             root[obj_name][name] = std::move(obj);
     293        30849 :         }
     294        78914 :     }
     295              : 
     296         1364 :     return root;
     297            0 : }
     298              : 
     299        30847 : 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        30847 :     json root = json::object();
     303        30847 :     json extensible = json::object();
     304        30847 :     json array_of_extensions = json::array();
     305              :     Token token;
     306        30847 :     std::string extension_key;
     307        30847 :     size_t legacy_idd_index = 0;
     308        30847 :     size_t extensible_index = 0;
     309        30847 :     success = true;
     310        30847 :     bool was_value_parsed = false;
     311        30847 :     auto const &legacy_idd_fields_array = legacy_idd["fields"];
     312        30847 :     auto const legacy_idd_extensibles_iter = legacy_idd.find("extensibles");
     313              : 
     314        30847 :     auto const &schema_patternProperties = schema_obj_loc["patternProperties"];
     315        30847 :     std::string patternProperty;
     316        61694 :     int dot_star_present = schema_patternProperties.count(".*");
     317        30847 :     int no_whitespace_present = schema_patternProperties.count(R"(^.*\S.*$)");
     318        30847 :     if (dot_star_present) {
     319         7192 :         patternProperty = ".*";
     320        23655 :     } else if (no_whitespace_present) {
     321        23655 :         patternProperty = R"(^.*\S.*$)";
     322              :     } else {
     323            0 :         throw std::runtime_error(R"(The patternProperties value is not a valid choice (".*", "^.*\S.*$"))");
     324              :     }
     325        30847 :     auto const &schema_obj_props = schema_patternProperties[patternProperty]["properties"];
     326        30847 :     auto key = legacy_idd.find("extension");
     327              : 
     328        30847 :     json const *schema_obj_extensions = nullptr;
     329        30847 :     if (legacy_idd_extensibles_iter != legacy_idd.end()) {
     330         8229 :         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         8229 :         extension_key = key.value().get<std::string>();
     336         8229 :         schema_obj_extensions = &schema_obj_props[extension_key]["items"]["properties"];
     337              :     }
     338              : 
     339        30847 :     root["idf_order"] = idfObjectCount;
     340              : 
     341        30847 :     auto const found_min_fields = schema_obj_loc.find("min_fields");
     342              : 
     343        30847 :     index += 1;
     344              : 
     345              :     while (true) {
     346      2876380 :         token = look_ahead(idf, index);
     347      2876380 :         root["idf_max_fields"] = legacy_idd_index;
     348      2876380 :         root["idf_max_extensible_fields"] = extensible_index;
     349      2876380 :         if (token == Token::NONE) {
     350            0 :             success = false;
     351            0 :             return root;
     352      2876380 :         } else if (token == Token::END) {
     353            3 :             return root;
     354      2876377 :         } else if (token == Token::COMMA || token == Token::SEMICOLON) {
     355      1105879 :             if (!was_value_parsed) {
     356        31923 :                 int ext_size = 0;
     357        31923 :                 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         1315 :                     auto const &legacy_idd_extensibles_array = legacy_idd_extensibles_iter.value();
     362         1315 :                     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         1315 :                     extensible_index++;
     365              :                     //                    extensible[ field_name ] = "";
     366              :                 }
     367        31923 :                 if (ext_size && extensible_index % ext_size == 0) {
     368          326 :                     array_of_extensions.push_back(extensible);
     369          326 :                     extensible.clear();
     370              :                 }
     371              :             }
     372      1105879 :             legacy_idd_index++;
     373      1105879 :             was_value_parsed = false;
     374      1105879 :             next_token(idf, index);
     375      1105879 :             if (token == Token::SEMICOLON) {
     376        30844 :                 size_t min_fields = 0;
     377        30844 :                 if (found_min_fields != schema_obj_loc.end()) {
     378        13797 :                     min_fields = found_min_fields.value().get<size_t>();
     379              :                 }
     380        33924 :                 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        30844 :                 if (!extensible.empty()) {
     385          179 :                     array_of_extensions.push_back(extensible);
     386          179 :                     extensible.clear();
     387              :                 }
     388        30844 :                 root["idf_max_fields"] = legacy_idd_index;
     389        30844 :                 root["idf_max_extensible_fields"] = extensible_index;
     390        30844 :                 break;
     391              :             }
     392      2845533 :         } else if (token == Token::EXCLAMATION) {
     393       696541 :             eat_comment(idf, index);
     394      1073957 :         } else if (legacy_idd_index >= legacy_idd_fields_array.size()) {
     395       680622 :             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       680622 :             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       680622 :             auto const &legacy_idd_extensibles_array = legacy_idd_extensibles_iter.value();
     411       680622 :             size_t const size = legacy_idd_extensibles_array.size();
     412       680622 :             std::string const &field_name = legacy_idd_extensibles_array[extensible_index % size].get<std::string>();
     413       680622 :             json val = parse_value(idf, index, success, schema_obj_extensions->at(field_name));
     414       680622 :             if (!success) return root;
     415       680622 :             extensible[field_name] = std::move(val);
     416       680622 :             was_value_parsed = true;
     417       680622 :             extensible_index++;
     418       680622 :             if (extensible_index && extensible_index % size == 0) {
     419       654897 :                 array_of_extensions.push_back(extensible);
     420       654897 :                 extensible.clear();
     421              :             }
     422       680622 :         } else {
     423       393335 :             was_value_parsed = true;
     424       393335 :             std::string const &field = legacy_idd_fields_array[legacy_idd_index].get<std::string>();
     425       393335 :             auto const find_field_iter = schema_obj_props.find(field);
     426       393335 :             if (find_field_iter == schema_obj_props.end()) {
     427        24119 :                 if (field == "name") {
     428        24119 :                     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       369216 :                 json val = parse_value(idf, index, success, find_field_iter.value());
     435       369216 :                 if (!success) return root;
     436       369216 :                 root[field] = std::move(val);
     437       369216 :             }
     438       393335 :             if (!success) return root;
     439       393335 :         }
     440      2845533 :     }
     441        30844 :     if (!array_of_extensions.empty()) {
     442         7997 :         root[extension_key] = std::move(array_of_extensions);
     443         7997 :         array_of_extensions = nullptr;
     444              :     }
     445        30844 :     return root;
     446        30847 : }
     447              : 
     448       592425 : json IdfParser::parse_number(std::string_view idf, size_t &index)
     449              : {
     450       592425 :     eat_whitespace(idf, index);
     451              : 
     452       592425 :     size_t save_i = index;
     453              : 
     454       592425 :     bool running = true;
     455      5029734 :     while (running) {
     456      4437309 :         if (save_i == idf_size) {
     457            0 :             break;
     458              :         }
     459              : 
     460      4437309 :         char const c = idf[save_i];
     461      4437309 :         switch (c) {
     462       592425 :         case '!':
     463              :         case ',':
     464              :         case ';':
     465              :         case '\r':
     466              :         case '\n':
     467       592425 :             running = false;
     468       592425 :             break;
     469      3844884 :         default:
     470      3844884 :             ++save_i;
     471              :         }
     472              :     }
     473              : 
     474       592425 :     size_t diff = save_i - index;
     475       592425 :     std::string_view value = idf.substr(index, diff);
     476       592425 :     index_into_cur_line += diff;
     477       592425 :     index = save_i;
     478              : 
     479       435858 :     auto const convert_double = [&index, this](std::string_view str) -> json { // (AUTO_OK)
     480       435858 :         size_t plus_sign = 0;
     481       435858 :         if (str.front() == '+') {
     482            4 :             plus_sign = 1;
     483              :         }
     484       435858 :         auto const str_end = str.data() + str.size(); // have to do this for MSVC // (AUTO_OK)
     485              :         double val;
     486       435858 :         auto result = fast_float::from_chars(str.data() + plus_sign, str.data() + str.size(), val); // (AUTO_OK)
     487       435858 :         if (result.ec == std::errc::invalid_argument || result.ec == std::errc::result_out_of_range) {
     488            3 :             return rtrim(str);
     489       435855 :         } else if (result.ptr != str_end) {
     490          230 :             auto const initial_ptr = result.ptr; // (AUTO_OK)
     491          456 :             while (result.ptr != str_end) {
     492          233 :                 if (*result.ptr != ' ') {
     493            7 :                     break;
     494              :                 }
     495          226 :                 ++result.ptr;
     496              :             }
     497          230 :             if (result.ptr == str_end) {
     498          223 :                 index -= (str_end - initial_ptr);
     499          223 :                 this->index_into_cur_line -= (str_end - initial_ptr);
     500          223 :                 return val;
     501              :             }
     502            7 :             return rtrim(str);
     503              :         }
     504       435625 :         return val;
     505       592425 :     };
     506              : 
     507       592425 :     auto const convert_int = [&convert_double, &index, this](std::string_view str) -> json { // (AUTO_OK)
     508       592425 :         auto const str_end = str.data() + str.size();                                        // have to do this for MSVC // (AUTO_OK)
     509              :         int val;
     510       592425 :         auto result = FromChars::from_chars(str.data(), str.data() + str.size(), val); // (AUTO_OK)
     511       592425 :         if (result.ec == std::errc::result_out_of_range || result.ec == std::errc::invalid_argument) {
     512         1438 :             return convert_double(str);
     513       590987 :         } else if (result.ptr != str_end) {
     514       434433 :             if (*result.ptr == '.' || *result.ptr == 'e' || *result.ptr == 'E') {
     515       434420 :                 return convert_double(str);
     516              :             } else {
     517           13 :                 auto const initial_ptr = result.ptr; // (AUTO_OK)
     518           22 :                 while (result.ptr != str_end) {
     519           13 :                     if (*result.ptr != ' ') {
     520            4 :                         break;
     521              :                     }
     522            9 :                     ++result.ptr;
     523              :                 }
     524           13 :                 if (result.ptr == str_end) {
     525            9 :                     index -= (str_end - initial_ptr);
     526            9 :                     this->index_into_cur_line -= (str_end - initial_ptr);
     527            9 :                     return val;
     528              :                 }
     529            4 :                 return rtrim(str);
     530              :             }
     531              :         }
     532       156554 :         return val;
     533       592425 :     };
     534              : 
     535      1184850 :     return convert_int(value);
     536              : }
     537              : 
     538         5497 : json IdfParser::parse_integer(std::string_view idf, size_t &index)
     539              : {
     540         5497 :     eat_whitespace(idf, index);
     541              : 
     542         5497 :     size_t save_i = index;
     543              : 
     544         5497 :     bool running = true;
     545        20187 :     while (running) {
     546        14690 :         if (save_i == idf_size) {
     547            0 :             break;
     548              :         }
     549              : 
     550        14690 :         char const c = idf[save_i];
     551        14690 :         switch (c) {
     552         5497 :         case '!':
     553              :         case ',':
     554              :         case ';':
     555              :         case '\r':
     556              :         case '\n':
     557         5497 :             running = false;
     558         5497 :             break;
     559         9193 :         default:
     560         9193 :             ++save_i;
     561              :         }
     562              :     }
     563              : 
     564         5497 :     size_t diff = save_i - index;
     565         5497 :     std::string_view string_value = idf.substr(index, diff);
     566         5497 :     index_into_cur_line += diff;
     567         5497 :     index = save_i;
     568              : 
     569         5497 :     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         5497 :     auto result = FromChars::from_chars(string_value.data(), string_value.data() + string_value.size(), int_value); // (AUTO_OK)
     573         5497 :     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         5497 :     } else if (result.ptr != string_end) {
     577              :         // Didn't use the entire string, try again via double conversion + rounding
     578           42 :         size_t plus_sign = 0;
     579           42 :         if (string_value.front() == '+') {
     580            0 :             plus_sign = 1;
     581              :         }
     582              :         double double_value;
     583           42 :         auto fresult = fast_float::from_chars(string_value.data() + plus_sign, string_value.data() + string_value.size(), double_value); // (AUTO_OK)
     584           42 :         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           42 :         int_value = static_cast<int>(std::round(double_value));
     589              :     }
     590         5497 :     return int_value;
     591              : }
     592              : 
     593      1049842 : json IdfParser::parse_value(std::string_view idf, size_t &index, bool &success, json const &field_loc)
     594              : {
     595              :     Token token;
     596      1049842 :     auto const &field_type = field_loc.find("type");
     597      1049842 :     if (field_type != field_loc.end()) {
     598       499972 :         if (field_type.value() == "number") {
     599       397905 :             token = Token::NUMBER;
     600       102067 :         } else if (field_type.value() == "integer") {
     601         5497 :             token = Token::INTEGER;
     602              :         } else {
     603        96570 :             token = Token::STRING;
     604              :         }
     605              :     } else {
     606       549870 :         token = look_ahead(idf, index);
     607              :     }
     608              : 
     609      1049842 :     switch (token) {
     610       451942 :     case Token::STRING: {
     611       451942 :         std::string const parsed_string = parse_string(idf, index);
     612       451942 :         auto const enum_it = field_loc.find("enum");
     613       451942 :         if (enum_it != field_loc.end()) {
     614       164230 :             for (auto const &enum_str : enum_it.value()) {
     615       164211 :                 std::string const str = enum_str.get<std::string>();
     616       164211 :                 if (icompare(str, parsed_string)) {
     617        39881 :                     return str;
     618              :                 }
     619       164211 :             }
     620       412042 :         } else if (icompare(parsed_string, "Autosize") || icompare(parsed_string, "Autocalculate")) {
     621         6806 :             auto const default_it = field_loc.find("default");
     622         3403 :             auto const anyOf_it = field_loc.find("anyOf");
     623              : 
     624         3403 :             if (anyOf_it == field_loc.end()) {
     625            4 :                 errors_.emplace_back(
     626            4 :                     fmt::format("Line: {} Index: {} - Field cannot be Autosize or Autocalculate", cur_line_num, index_into_cur_line));
     627            2 :                 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         3401 :             if (default_it != field_loc.end()) {
     632         3574 :                 return field_loc.at("anyOf")[1]["enum"][1];
     633              :             } else {
     634         3228 :                 return field_loc.at("anyOf")[1]["enum"][0];
     635              :             }
     636              :         }
     637       408658 :         return parsed_string;
     638       451942 :     }
     639       592403 :     case Token::NUMBER: {
     640       592403 :         return parse_number(idf, index);
     641              :     }
     642         5497 :     case Token::INTEGER: {
     643         5497 :         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              : }
     656              : 
     657       506914 : std::string IdfParser::parse_string(std::string_view idf, size_t &index)
     658              : {
     659       506914 :     eat_whitespace(idf, index);
     660              : 
     661       506914 :     std::string str;
     662              : 
     663              :     while (true) {
     664      7495309 :         if (index == idf_size) {
     665            3 :             break;
     666              :         }
     667              : 
     668      7495306 :         char c = idf[index];
     669      7495306 :         increment_both_index(index, index_into_cur_line);
     670      7495306 :         if (c == ',' || c == ';' || c == '!') {
     671       506911 :             decrement_both_index(index, index_into_cur_line);
     672       506911 :             break;
     673              :         } else {
     674      6988395 :             str += c;
     675              :         }
     676      6988395 :     }
     677              : 
     678      1013828 :     return rtrim(str);
     679       506914 : }
     680              : 
     681     52082008 : void IdfParser::increment_both_index(size_t &index, size_t &line_index)
     682              : {
     683     52082008 :     index++;
     684     52082008 :     line_index++;
     685     52082008 : }
     686              : 
     687       506911 : void IdfParser::decrement_both_index(size_t &index, size_t &line_index)
     688              : {
     689       506911 :     index--;
     690       506911 :     line_index--;
     691       506911 : }
     692              : 
     693      5717262 : void IdfParser::eat_whitespace(std::string_view idf, size_t &index)
     694              : {
     695     22714454 :     while (index < idf_size) {
     696     22713085 :         switch (idf[index]) {
     697     16846857 :         case ' ':
     698              :         case '\r':
     699              :         case '\t':
     700     16846857 :             increment_both_index(index, index_into_cur_line);
     701     16846857 :             continue;
     702       150335 :         case '\n':
     703       150335 :             increment_both_index(index, cur_line_num);
     704       150335 :             beginning_of_line_index = index;
     705       150335 :             index_into_cur_line = 0;
     706       150335 :             continue;
     707      5715893 :         default:
     708      5715893 :             return;
     709              :         }
     710              :     }
     711              : }
     712              : 
     713       744611 : void IdfParser::eat_comment(std::string_view idf, size_t &index)
     714              : {
     715              :     while (true) {
     716     22978455 :         if (index == idf_size) break;
     717     22978455 :         if (idf[index] == '\n') {
     718       744611 :             increment_both_index(index, cur_line_num);
     719       744611 :             index_into_cur_line = 0;
     720       744611 :             beginning_of_line_index = index;
     721       744611 :             break;
     722              :         }
     723     22233844 :         increment_both_index(index, index_into_cur_line);
     724              :     }
     725       744611 : }
     726              : 
     727      3506535 : IdfParser::Token IdfParser::look_ahead(std::string_view idf, size_t index)
     728              : {
     729      3506535 :     size_t save_index = index;
     730      3506535 :     size_t save_line_num = cur_line_num;
     731      3506535 :     size_t save_line_index = index_into_cur_line;
     732      3506535 :     Token token = next_token(idf, save_index);
     733      3506535 :     cur_line_num = save_line_num;
     734      3506535 :     index_into_cur_line = save_line_index;
     735      3506535 :     return token;
     736              : }
     737              : 
     738      4612424 : IdfParser::Token IdfParser::next_token(std::string_view idf, size_t &index)
     739              : {
     740      4612424 :     eat_whitespace(idf, index);
     741              : 
     742      4612424 :     if (index == idf_size) {
     743         1369 :         return Token::END;
     744              :     }
     745              : 
     746      4611055 :     char const c = idf[index];
     747      4611055 :     increment_both_index(index, index_into_cur_line);
     748      4611055 :     switch (c) {
     749       744609 :     case '!':
     750       744609 :         return Token::EXCLAMATION;
     751      2150073 :     case ',':
     752      2150073 :         return Token::COMMA;
     753        61691 :     case ';':
     754        61691 :         return Token::SEMICOLON;
     755      1654682 :     default:
     756              :         static constexpr std::string_view numeric(".-+0123456789");
     757      1654682 :         if (numeric.find_first_of(c) != std::string::npos) {
     758       793401 :             return Token::NUMBER;
     759              :         }
     760       861281 :         return Token::STRING;
     761              :     }
     762              :     decrement_both_index(index, index_into_cur_line);
     763              :     return Token::NONE;
     764              : }
     765              : 
     766            0 : IdfParser::Token IdfParser::next_limited_token(std::string_view idf, size_t &index)
     767              : {
     768            0 :     if (index == idf_size) {
     769            0 :         return Token::END;
     770              :     }
     771              : 
     772            0 :     char const c = idf[index];
     773            0 :     increment_both_index(index, index_into_cur_line);
     774            0 :     switch (c) {
     775            0 :     case '!':
     776            0 :         return Token::EXCLAMATION;
     777            0 :     case ',':
     778            0 :         return Token::COMMA;
     779            0 :     case ';':
     780            0 :         return Token::SEMICOLON;
     781            0 :     default:
     782            0 :         return Token::NONE;
     783              :     }
     784              : }
     785              : 
     786       506928 : std::string IdfParser::rtrim(std::string_view str)
     787              : {
     788              :     static constexpr std::string_view whitespace(" \t\0", 3);
     789       506928 :     if (str.empty()) {
     790            1 :         return std::string{};
     791              :     }
     792       506927 :     size_t const index = str.find_last_not_of(whitespace);
     793       506927 :     if (index == std::string::npos) {
     794            0 :         return std::string{};
     795       506927 :     } else if (index + 1 < str.length()) {
     796          897 :         return std::string{str.substr(0, index + 1)};
     797              :     }
     798      1013256 :     return std::string{str};
     799              : }
        

Generated by: LCOV version 2.0-1