LCOV - code coverage report
Current view: top level - EnergyPlus/InputProcessing - IdfParser.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 68.4 % 510 349
Test Date: 2025-06-02 07:23:51 Functions: 85.2 % 27 23

            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              : }
        

Generated by: LCOV version 2.0-1