LCOV - code coverage report
Current view: top level - EnergyPlus/InputProcessing - IdfParser.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 334 482 69.3 %
Date: 2023-01-17 19:17:23 Functions: 23 27 85.2 %

          Line data    Source code
       1             : // EnergyPlus, Copyright (c) 1996-2023, 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     3356073 : auto const icompare = [](std::string_view a, std::string_view b) {
      59     3356073 :     return (a.length() == b.length()
      60     8117654 :                 ? std::equal(a.begin(), a.end(), b.begin(), [](char const c, char const d) { return (::tolower(c) == ::tolower(d)); })
      61             :                 : false);
      62     3356073 : };
      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         770 : json IdfParser::decode(std::string_view idf, json const &schema, bool &success)
      71             : {
      72         770 :     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         770 : json IdfParser::decode(std::string_view idf, size_t _idf_size, json const &schema, bool &success)
      82             : {
      83         770 :     success = true;
      84         770 :     cur_line_num = 1;
      85         770 :     index_into_cur_line = 0;
      86         770 :     beginning_of_line_index = 0;
      87         770 :     idf_size = _idf_size;
      88             : 
      89         770 :     if (idf.empty()) {
      90           0 :         success = false;
      91           0 :         return nullptr;
      92             :     }
      93             : 
      94         770 :     size_t index = 0;
      95         770 :     return parse_idf(idf, index, success, schema);
      96             : }
      97             : 
      98           0 : std::string IdfParser::encode(json const &root, json const &schema)
      99             : {
     100             :     static constexpr std::string_view end_of_field(",\n  ", 4);
     101             :     static constexpr std::string_view end_of_object(";\n\n", 3);
     102             : 
     103           0 :     std::string encoded, extension_key;
     104           0 :     if (idf_size > 0) {
     105           0 :         encoded.reserve(idf_size);
     106             :     } else {
     107           0 :         encoded.reserve(root.size() * 1024);
     108             :     }
     109             : 
     110           0 :     for (auto obj = root.begin(); obj != root.end(); ++obj) {
     111           0 :         const auto &legacy_idd = schema["properties"][obj.key()]["legacy_idd"];
     112           0 :         const auto &legacy_idd_field = legacy_idd["fields"];
     113           0 :         auto key = legacy_idd.find("extension");
     114           0 :         if (key != legacy_idd.end()) {
     115           0 :             extension_key = key.value().get<std::string>();
     116             :         }
     117           0 :         for (auto obj_in = obj.value().begin(); obj_in != obj.value().end(); ++obj_in) {
     118           0 :             encoded += obj.key();
     119           0 :             size_t skipped_fields = 0;
     120           0 :             for (size_t i = 0; i < legacy_idd_field.size(); i++) {
     121           0 :                 std::string const &entry = legacy_idd_field[i].get<std::string>();
     122           0 :                 if (obj_in.value().find(entry) == obj_in.value().end()) {
     123           0 :                     if (entry == "name")
     124           0 :                         encoded += std::string{end_of_field} + obj_in.key();
     125             :                     else
     126           0 :                         skipped_fields++;
     127           0 :                     continue;
     128             :                 }
     129           0 :                 for (size_t j = 0; j < skipped_fields; j++)
     130           0 :                     encoded += end_of_field;
     131           0 :                 skipped_fields = 0;
     132           0 :                 encoded += end_of_field;
     133           0 :                 auto const &val = obj_in.value()[entry];
     134           0 :                 if (val.is_string()) {
     135           0 :                     encoded += val.get<std::string>();
     136           0 :                 } else if (val.is_number_integer()) {
     137           0 :                     encoded += std::to_string(val.get<int>());
     138             :                 } else {
     139           0 :                     dtoa(val.get<double>(), s);
     140           0 :                     encoded += s;
     141             :                 }
     142             :             }
     143             : 
     144           0 :             if (obj_in.value().find(extension_key) == obj_in.value().end()) {
     145           0 :                 encoded += end_of_object;
     146           0 :                 continue;
     147             :             }
     148             : 
     149           0 :             auto &extensions = obj_in.value()[extension_key];
     150           0 :             for (size_t extension_i = 0; extension_i < extensions.size(); extension_i++) {
     151           0 :                 auto const &cur_extension_obj = extensions[extension_i];
     152           0 :                 auto const &extensible = schema["properties"][obj.key()]["legacy_idd"]["extensibles"];
     153           0 :                 for (size_t i = 0; i < extensible.size(); i++) {
     154           0 :                     std::string const &tmp = extensible[i].get<std::string>();
     155           0 :                     if (cur_extension_obj.find(tmp) == cur_extension_obj.end()) {
     156           0 :                         skipped_fields++;
     157           0 :                         continue;
     158             :                     }
     159           0 :                     for (size_t j = 0; j < skipped_fields; j++)
     160           0 :                         encoded += end_of_field;
     161           0 :                     skipped_fields = 0;
     162           0 :                     encoded += end_of_field;
     163           0 :                     if (cur_extension_obj[tmp].is_string()) {
     164           0 :                         encoded += cur_extension_obj[tmp].get<std::string>();
     165             :                     } else {
     166           0 :                         dtoa(cur_extension_obj[tmp].get<double>(), s);
     167           0 :                         encoded += s;
     168             :                     }
     169             :                 }
     170             :             }
     171           0 :             encoded += end_of_object;
     172             :         }
     173             :     }
     174           0 :     return encoded;
     175             : }
     176             : 
     177      282602 : std::string IdfParser::normalizeObjectType(std::string const &objectType)
     178             : {
     179      282602 :     if (objectType.empty()) return std::string{};
     180      565204 :     auto key = convertToUpper(objectType);
     181      565204 :     auto tmp_umit = objectTypeMap.find(key);
     182      282602 :     if (tmp_umit != objectTypeMap.end()) {
     183      282602 :         return tmp_umit->second;
     184             :     }
     185           0 :     return std::string{};
     186             : }
     187             : 
     188         771 : std::vector<std::string> const &IdfParser::errors()
     189             : {
     190         771 :     return errors_;
     191             : }
     192             : 
     193         771 : std::vector<std::string> const &IdfParser::warnings()
     194             : {
     195         771 :     return warnings_;
     196             : }
     197             : 
     198         771 : bool IdfParser::hasErrors()
     199             : {
     200         771 :     return !errors_.empty();
     201             : }
     202             : 
     203         770 : json IdfParser::parse_idf(std::string_view idf, size_t &index, bool &success, json const &schema)
     204             : {
     205         770 :     json root;
     206             :     Token token;
     207         770 :     auto const &schema_properties = schema["properties"];
     208             : 
     209         770 :     objectTypeMap.reserve(schema_properties.size());
     210      643720 :     for (auto it = schema_properties.begin(); it != schema_properties.end(); ++it) {
     211     1285900 :         std::string key = convertToUpper(it.key());
     212      642950 :         objectTypeMap.emplace(std::move(key), it.key());
     213             :     }
     214             : 
     215         770 :     if (idf_size > 3) {
     216             :         // UTF-8 Byte Order Mark
     217         770 :         if (idf[0] == '\xEF' && idf[1] == '\xBB' && idf[2] == '\xBF') {
     218           0 :             index += 3;
     219           0 :             index_into_cur_line += 3;
     220             :         }
     221             :     }
     222             : 
     223         770 :     int idfObjectCount = 0;
     224             :     while (true) {
     225      671916 :         token = look_ahead(idf, index);
     226      671916 :         if (token == Token::END) {
     227         770 :             break;
     228      671146 :         } else if (token == Token::NONE) {
     229           0 :             success = false;
     230           0 :             return root;
     231      671146 :         } else if (token == Token::SEMICOLON) {
     232           0 :             next_token(idf, index);
     233           0 :             continue;
     234      671146 :         } 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      671146 :         } else if (token == Token::EXCLAMATION) {
     239      388544 :             eat_comment(idf, index);
     240             :         } else {
     241      282602 :             ++idfObjectCount;
     242      565204 :             auto const parsed_obj_name = parse_string(idf, index);
     243      565204 :             auto const obj_name = normalizeObjectType(parsed_obj_name);
     244      282602 :             if (obj_name.empty()) {
     245           0 :                 errors_.emplace_back(
     246           0 :                     fmt::format("Line: {} Index: {} - \"{}\" is not a valid Object Type.", cur_line_num, index_into_cur_line, parsed_obj_name));
     247           0 :                 while (token != Token::SEMICOLON && token != Token::END)
     248           0 :                     token = next_token(idf, index);
     249           0 :                 continue;
     250             :             }
     251             : 
     252      282602 :             bool object_success = true;
     253      282602 :             json const &obj_loc = schema_properties[obj_name];
     254      282602 :             json const &legacy_idd = obj_loc["legacy_idd"];
     255      565204 :             json obj = parse_object(idf, index, object_success, legacy_idd, obj_loc, idfObjectCount);
     256      282602 :             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             :             }
     268      282602 :             u64toa(root[obj_name].size() + 1, s);
     269      847806 :             std::string name = fmt::format("{} {}", obj_name, s);
     270             : 
     271      282602 :             if (!obj.is_null()) {
     272      565204 :                 auto const &name_iter = obj.find("name");
     273             :                 // If you find a name field, use that
     274      282602 :                 if (name_iter != obj.end()) {
     275      227292 :                     name = name_iter.value().get<std::string>();
     276      227292 :                     obj.erase(name_iter);
     277             :                 } else {
     278             :                     // Otherwise, see if it should have a name field
     279      110620 :                     auto const &it = obj_loc.find("name");
     280       55310 :                     if (it != obj_loc.end()) {
     281             :                         // Let it slide, as a blank string, to be handled in the appropriate GetInput routine
     282           1 :                         name = "";
     283             :                     }
     284             :                 }
     285             :             }
     286             : 
     287      282602 :             if (root[obj_name].find(name) != root[obj_name].end()) {
     288           0 :                 errors_.emplace_back(
     289           0 :                     fmt::format(R"(Duplicate name found for object of type "{}" named "{}". Overwriting existing object.)", obj_name, name));
     290             :             }
     291             : 
     292      282602 :             root[obj_name][name] = std::move(obj);
     293             :         }
     294      671146 :     }
     295             : 
     296         770 :     return root;
     297             : }
     298             : 
     299      282602 : 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      282602 :     json root = json::object();
     303      565204 :     json extensible = json::object();
     304      565204 :     json array_of_extensions = json::array();
     305             :     Token token;
     306      565204 :     std::string extension_key;
     307      282602 :     size_t legacy_idd_index = 0;
     308      282602 :     size_t extensible_index = 0;
     309      282602 :     success = true;
     310      282602 :     bool was_value_parsed = false;
     311      282602 :     auto const &legacy_idd_fields_array = legacy_idd["fields"];
     312      565204 :     auto const &legacy_idd_extensibles_iter = legacy_idd.find("extensibles");
     313             : 
     314      282602 :     auto const &schema_patternProperties = schema_obj_loc["patternProperties"];
     315      565204 :     std::string patternProperty;
     316      282602 :     int dot_star_present = schema_patternProperties.count(".*");
     317      282602 :     int no_whitespace_present = schema_patternProperties.count(R"(^.*\S.*$)");
     318      282602 :     if (dot_star_present) {
     319       58820 :         patternProperty = ".*";
     320      223782 :     } else if (no_whitespace_present) {
     321      223782 :         patternProperty = R"(^.*\S.*$)";
     322             :     } else {
     323           0 :         throw std::runtime_error(R"(The patternProperties value is not a valid choice (".*", "^.*\S.*$"))");
     324             :     }
     325      282602 :     auto const &schema_obj_props = schema_patternProperties[patternProperty]["properties"];
     326      565204 :     auto key = legacy_idd.find("extension");
     327             : 
     328      282602 :     json const *schema_obj_extensions = nullptr;
     329      282602 :     if (legacy_idd_extensibles_iter != legacy_idd.end()) {
     330       95754 :         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       95754 :         extension_key = key.value().get<std::string>();
     336       95754 :         schema_obj_extensions = &schema_obj_props[extension_key]["items"]["properties"];
     337             :     }
     338             : 
     339      282602 :     root["idf_order"] = idfObjectCount;
     340             : 
     341      565204 :     auto const &found_min_fields = schema_obj_loc.find("min_fields");
     342             : 
     343      282602 :     index += 1;
     344             : 
     345             :     while (true) {
     346    10800470 :         token = look_ahead(idf, index);
     347    10800470 :         root["idf_max_fields"] = legacy_idd_index;
     348    10800470 :         root["idf_max_extensible_fields"] = extensible_index;
     349    10800470 :         if (token == Token::NONE) {
     350           0 :             success = false;
     351           0 :             return root;
     352    10800470 :         } else if (token == Token::END) {
     353           0 :             return root;
     354    10800470 :         } else if (token == Token::COMMA || token == Token::SEMICOLON) {
     355     4067701 :             if (!was_value_parsed) {
     356      298087 :                 int ext_size = 0;
     357      298087 :                 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        9249 :                     auto const &legacy_idd_extensibles_array = legacy_idd_extensibles_iter.value();
     362        9249 :                     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        9249 :                     extensible_index++;
     365             :                     //                    extensible[ field_name ] = "";
     366             :                 }
     367      298087 :                 if (ext_size && extensible_index % ext_size == 0) {
     368        4598 :                     array_of_extensions.push_back(extensible);
     369        4598 :                     extensible.clear();
     370             :                 }
     371             :             }
     372     4067701 :             legacy_idd_index++;
     373     4067701 :             was_value_parsed = false;
     374     4067701 :             next_token(idf, index);
     375     4067701 :             if (token == Token::SEMICOLON) {
     376      282602 :                 size_t min_fields = 0;
     377      282602 :                 if (found_min_fields != schema_obj_loc.end()) {
     378      155480 :                     min_fields = found_min_fields.value().get<size_t>();
     379             :                 }
     380      309750 :                 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      282602 :                 if (!extensible.empty()) {
     385         176 :                     array_of_extensions.push_back(extensible);
     386         176 :                     extensible.clear();
     387             :                 }
     388      282602 :                 root["idf_max_fields"] = legacy_idd_index;
     389      282602 :                 root["idf_max_extensible_fields"] = extensible_index;
     390      282602 :                 break;
     391     3785099 :             }
     392     6732769 :         } else if (token == Token::EXCLAMATION) {
     393     2963155 :             eat_comment(idf, index);
     394     3769614 :         } else if (legacy_idd_index >= legacy_idd_fields_array.size()) {
     395     1745170 :             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             :                                 cur_line_num,
     399           0 :                                 index_into_cur_line));
     400           0 :                 success = false;
     401           0 :                 return root;
     402             :             }
     403     1745170 :             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             :                                                  cur_line_num,
     406           0 :                                                  index_into_cur_line));
     407           0 :                 success = false;
     408           0 :                 return root;
     409             :             }
     410     1745170 :             auto const &legacy_idd_extensibles_array = legacy_idd_extensibles_iter.value();
     411     1745170 :             auto const size = legacy_idd_extensibles_array.size();
     412     3490340 :             std::string const &field_name = legacy_idd_extensibles_array[extensible_index % size].get<std::string>();
     413     3490340 :             auto val = parse_value(idf, index, success, schema_obj_extensions->at(field_name));
     414     1745170 :             if (!success) return root;
     415     1745170 :             extensible[field_name] = std::move(val);
     416     1745170 :             was_value_parsed = true;
     417     1745170 :             extensible_index++;
     418     1745170 :             if (extensible_index && extensible_index % size == 0) {
     419     1305079 :                 array_of_extensions.push_back(extensible);
     420     1305079 :                 extensible.clear();
     421             :             }
     422             :         } else {
     423     2024444 :             was_value_parsed = true;
     424     4048888 :             std::string const &field = legacy_idd_fields_array[legacy_idd_index].get<std::string>();
     425     4048888 :             auto const &find_field_iter = schema_obj_props.find(field);
     426     2024444 :             if (find_field_iter == schema_obj_props.end()) {
     427      227292 :                 if (field == "name") {
     428      227292 :                     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     3594304 :                 auto val = parse_value(idf, index, success, find_field_iter.value());
     435     1797152 :                 if (!success) return root;
     436     1797152 :                 root[field] = std::move(val);
     437             :             }
     438     2024444 :             if (!success) return root;
     439             :         }
     440    10517868 :     }
     441      282602 :     if (!array_of_extensions.empty()) {
     442       95439 :         root[extension_key] = std::move(array_of_extensions);
     443       95439 :         array_of_extensions = nullptr;
     444             :     }
     445      282602 :     return root;
     446             : }
     447             : 
     448     2288484 : json IdfParser::parse_number(std::string_view idf, size_t &index)
     449             : {
     450     2288484 :     eat_whitespace(idf, index);
     451             : 
     452     2288484 :     size_t save_i = index;
     453             : 
     454     2288484 :     bool running = true;
     455    36756650 :     while (running) {
     456    17234083 :         if (save_i == idf_size) {
     457           0 :             break;
     458             :         }
     459             : 
     460    17234083 :         char const c = idf[save_i];
     461    17234083 :         switch (c) {
     462     2288484 :         case '!':
     463             :         case ',':
     464             :         case ';':
     465             :         case '\r':
     466             :         case '\n':
     467     2288484 :             running = false;
     468     2288484 :             break;
     469    14945599 :         default:
     470    14945599 :             ++save_i;
     471             :         }
     472             :     }
     473             : 
     474     2288484 :     auto diff = save_i - index;
     475     2288484 :     auto value = idf.substr(index, diff);
     476     2288484 :     index_into_cur_line += diff;
     477     2288484 :     index = save_i;
     478             : 
     479     2125946 :     auto const convert_double = [&index, this](std::string_view str) -> json {
     480     2125942 :         size_t plus_sign = 0;
     481     2125942 :         if (str.front() == '+') {
     482           1 :             plus_sign = 1;
     483             :         }
     484     2125942 :         auto const str_end = str.data() + str.size(); // have to do this for MSVC
     485             :         double val;
     486     2125942 :         auto result = fast_float::from_chars(str.data() + plus_sign, str.data() + str.size(), val);
     487     2125942 :         if (result.ec == std::errc::invalid_argument || result.ec == std::errc::result_out_of_range) {
     488           0 :             return rtrim(str);
     489     2125942 :         } else if (result.ptr != str_end) {
     490           1 :             auto const initial_ptr = result.ptr;
     491           3 :             while (result.ptr != str_end) {
     492           1 :                 if (*result.ptr != ' ') {
     493           0 :                     break;
     494             :                 }
     495           1 :                 ++result.ptr;
     496             :             }
     497           1 :             if (result.ptr == str_end) {
     498           2 :                 index -= (str_end - initial_ptr);
     499           2 :                 this->index_into_cur_line -= (str_end - initial_ptr);
     500           1 :                 return val;
     501             :             }
     502           0 :             return rtrim(str);
     503             :         }
     504     2125941 :         return val;
     505     2288484 :     };
     506             : 
     507     4414426 :     auto const convert_int = [&convert_double, &index, this](std::string_view str) -> json {
     508     2288484 :         auto const str_end = str.data() + str.size(); // have to do this for MSVC
     509             :         int val;
     510     2288484 :         auto result = FromChars::from_chars(str.data(), str.data() + str.size(), val);
     511     2288484 :         if (result.ec == std::errc::result_out_of_range || result.ec == std::errc::invalid_argument) {
     512        3101 :             return convert_double(str);
     513     2285383 :         } else if (result.ptr != str_end) {
     514     2122841 :             if (*result.ptr == '.' || *result.ptr == 'e' || *result.ptr == 'E') {
     515     2122841 :                 return convert_double(str);
     516             :             } else {
     517           0 :                 auto const initial_ptr = result.ptr;
     518           0 :                 while (result.ptr != str_end) {
     519           0 :                     if (*result.ptr != ' ') {
     520           0 :                         break;
     521             :                     }
     522           0 :                     ++result.ptr;
     523             :                 }
     524           0 :                 if (result.ptr == str_end) {
     525           0 :                     index -= (str_end - initial_ptr);
     526           0 :                     this->index_into_cur_line -= (str_end - initial_ptr);
     527           0 :                     return val;
     528             :                 }
     529           0 :                 return rtrim(str);
     530             :             }
     531             :         }
     532      162542 :         return val;
     533     2288484 :     };
     534             : 
     535     2288484 :     return convert_int(value);
     536             : }
     537             : 
     538       34531 : json IdfParser::parse_integer(std::string_view idf, size_t &index)
     539             : {
     540       34531 :     eat_whitespace(idf, index);
     541             : 
     542       34531 :     size_t save_i = index;
     543             : 
     544       34531 :     bool running = true;
     545      191007 :     while (running) {
     546       78238 :         if (save_i == idf_size) {
     547           0 :             break;
     548             :         }
     549             : 
     550       78238 :         char const c = idf[save_i];
     551       78238 :         switch (c) {
     552       34531 :         case '!':
     553             :         case ',':
     554             :         case ';':
     555             :         case '\r':
     556             :         case '\n':
     557       34531 :             running = false;
     558       34531 :             break;
     559       43707 :         default:
     560       43707 :             ++save_i;
     561             :         }
     562             :     }
     563             : 
     564       34531 :     auto diff = save_i - index;
     565       34531 :     auto string_value = idf.substr(index, diff);
     566       34531 :     index_into_cur_line += diff;
     567       34531 :     index = save_i;
     568             : 
     569       34531 :     auto const string_end = string_value.data() + string_value.size(); // have to do this for MSVC
     570             :     int int_value;
     571             :     // Try using from_chars
     572       34531 :     auto result = FromChars::from_chars(string_value.data(), string_value.data() + string_value.size(), int_value);
     573       34531 :     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       34531 :     } else if (result.ptr != string_end) {
     577             :         // Didn't use the entire string, try again via double conversion + rounding
     578         673 :         size_t plus_sign = 0;
     579         673 :         if (string_value.front() == '+') {
     580           0 :             plus_sign = 1;
     581             :         }
     582             :         double double_value;
     583         673 :         auto fresult = fast_float::from_chars(string_value.data() + plus_sign, string_value.data() + string_value.size(), double_value);
     584         673 :         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         673 :         int_value = static_cast<int>(std::round(double_value));
     589             :     }
     590       34531 :     return int_value;
     591             : }
     592             : 
     593     3542322 : json IdfParser::parse_value(std::string_view idf, size_t &index, bool &success, json const &field_loc)
     594             : {
     595             :     Token token;
     596     7084644 :     auto const &field_type = field_loc.find("type");
     597     3542322 :     if (field_type != field_loc.end()) {
     598     3180395 :         if (field_type.value() == "number") {
     599     2127607 :             token = Token::NUMBER;
     600     1052788 :         } else if (field_type.value() == "integer") {
     601       34531 :             token = Token::INTEGER;
     602             :         } else {
     603     1018257 :             token = Token::STRING;
     604             :         }
     605             :     } else {
     606      361927 :         token = look_ahead(idf, index);
     607             :     }
     608             : 
     609     3542322 :     switch (token) {
     610     1219307 :     case Token::STRING: {
     611     2438614 :         auto const parsed_string = parse_string(idf, index);
     612     2438614 :         auto const &enum_it = field_loc.find("enum");
     613     1219307 :         if (enum_it != field_loc.end()) {
     614     1708624 :             for (auto const &enum_str : enum_it.value()) {
     615     3042372 :                 auto const &str = enum_str.get<std::string>();
     616     1708624 :                 if (icompare(str, parsed_string)) {
     617      374876 :                     return str;
     618             :                 }
     619             :             }
     620      844431 :         } else if (icompare(parsed_string, "Autosize") || icompare(parsed_string, "Autocalculate")) {
     621      141948 :             auto const &default_it = field_loc.find("default");
     622      141948 :             auto const &anyOf_it = field_loc.find("anyOf");
     623             : 
     624       70974 :             if (anyOf_it == field_loc.end()) {
     625           0 :                 errors_.emplace_back(
     626           0 :                     fmt::format("Line: {} Index: {} - Field cannot be Autosize or Autocalculate", cur_line_num, index_into_cur_line));
     627           0 :                 return parsed_string;
     628             :             }
     629             :             // The following is hacky because it abuses knowing the consistent generated structure
     630             :             // in the future this might not hold true for the array indexes.
     631       70974 :             if (default_it != field_loc.end()) {
     632       51423 :                 return field_loc.at("anyOf")[1]["enum"][1];
     633             :             } else {
     634       19551 :                 return field_loc.at("anyOf")[1]["enum"][0];
     635             :             }
     636             :         }
     637      773457 :         return parsed_string;
     638             :     }
     639     2288484 :     case Token::NUMBER: {
     640     2288484 :         return parse_number(idf, index);
     641             :     }
     642       34531 :     case Token::INTEGER: {
     643       34531 :         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     1729201 : std::string IdfParser::parse_string(std::string_view idf, size_t &index)
     658             : {
     659     1729201 :     eat_whitespace(idf, index);
     660             : 
     661     3458402 :     std::string str;
     662             :     char c;
     663             : 
     664             :     while (true) {
     665    62939445 :         if (index == idf_size) {
     666           0 :             break;
     667             :         }
     668             : 
     669    32334323 :         c = idf[index];
     670    32334323 :         increment_both_index(index, index_into_cur_line);
     671    32334323 :         if (c == ',' || c == ';' || c == '!') {
     672     1729201 :             decrement_both_index(index, index_into_cur_line);
     673     1729201 :             break;
     674             :         } else {
     675    30605122 :             str += c;
     676             :         }
     677             :     }
     678             : 
     679     3458402 :     return rtrim(str);
     680             : }
     681             : 
     682   246292819 : void IdfParser::increment_both_index(size_t &index, size_t &line_index)
     683             : {
     684   246292819 :     index++;
     685   246292819 :     line_index++;
     686   246292819 : }
     687             : 
     688     1729201 : void IdfParser::decrement_both_index(size_t &index, size_t &line_index)
     689             : {
     690     1729201 :     index--;
     691     1729201 :     line_index--;
     692     1729201 : }
     693             : 
     694    95710393 : void IdfParser::eat_whitespace(std::string_view idf, size_t &index)
     695             : {
     696   171466556 :     while (index < idf_size) {
     697   171465786 :         switch (idf[index]) {
     698    74471271 :         case ' ':
     699             :         case '\r':
     700             :         case '\t':
     701    74471271 :             increment_both_index(index, index_into_cur_line);
     702    74471271 :             continue;
     703     1284892 :         case '\n':
     704     1284892 :             increment_both_index(index, cur_line_num);
     705     1284892 :             beginning_of_line_index = index;
     706     1284892 :             index_into_cur_line = 0;
     707     1284892 :             continue;
     708    19953460 :         default:
     709    19953460 :             return;
     710             :         }
     711             :     }
     712             : }
     713             : 
     714   122301090 : void IdfParser::eat_comment(std::string_view idf, size_t &index)
     715             : {
     716             :     while (true) {
     717   241250481 :         if (index == idf_size) break;
     718   122301089 :         if (idf[index] == '\n') {
     719     3351698 :             increment_both_index(index, cur_line_num);
     720     3351698 :             index_into_cur_line = 0;
     721     3351698 :             beginning_of_line_index = index;
     722     3351698 :             break;
     723             :         }
     724   118949391 :         increment_both_index(index, index_into_cur_line);
     725             :     }
     726     3351699 : }
     727             : 
     728    11834313 : IdfParser::Token IdfParser::look_ahead(std::string_view idf, size_t index)
     729             : {
     730    11834313 :     size_t save_index = index;
     731    11834313 :     size_t save_line_num = cur_line_num;
     732    11834313 :     size_t save_line_index = index_into_cur_line;
     733    11834313 :     Token token = next_token(idf, save_index);
     734    11834313 :     cur_line_num = save_line_num;
     735    11834313 :     index_into_cur_line = save_line_index;
     736    11834313 :     return token;
     737             : }
     738             : 
     739    15902014 : IdfParser::Token IdfParser::next_token(std::string_view idf, size_t &index)
     740             : {
     741    15902014 :     eat_whitespace(idf, index);
     742             : 
     743    15902014 :     if (index == idf_size) {
     744         770 :         return Token::END;
     745             :     }
     746             : 
     747    15901244 :     char const c = idf[index];
     748    15901244 :     increment_both_index(index, index_into_cur_line);
     749    15901244 :     switch (c) {
     750     3351699 :     case '!':
     751     3351699 :         return Token::EXCLAMATION;
     752     7570198 :     case ',':
     753     7570198 :         return Token::COMMA;
     754      565204 :     case ';':
     755      565204 :         return Token::SEMICOLON;
     756     4414143 :     default:
     757             :         static constexpr std::string_view numeric(".-+0123456789");
     758     4414143 :         if (numeric.find_first_of(c) != std::string::npos) {
     759     2494300 :             return Token::NUMBER;
     760             :         }
     761     1919843 :         return Token::STRING;
     762             :     }
     763             :     decrement_both_index(index, index_into_cur_line);
     764             :     return Token::NONE;
     765             : }
     766             : 
     767           0 : IdfParser::Token IdfParser::next_limited_token(std::string_view idf, size_t &index)
     768             : {
     769           0 :     if (index == idf_size) {
     770           0 :         return Token::END;
     771             :     }
     772             : 
     773           0 :     char const c = idf[index];
     774           0 :     increment_both_index(index, index_into_cur_line);
     775           0 :     switch (c) {
     776           0 :     case '!':
     777           0 :         return Token::EXCLAMATION;
     778           0 :     case ',':
     779           0 :         return Token::COMMA;
     780           0 :     case ';':
     781           0 :         return Token::SEMICOLON;
     782           0 :     default:
     783           0 :         return Token::NONE;
     784             :     }
     785             : }
     786             : 
     787     1729201 : std::string IdfParser::rtrim(std::string_view str)
     788             : {
     789             :     static constexpr std::string_view whitespace(" \t\0", 3);
     790     1729201 :     if (str.empty()) {
     791           0 :         return std::string{};
     792             :     }
     793     1729201 :     auto const index = str.find_last_not_of(whitespace);
     794     1729201 :     if (index == std::string::npos) {
     795           0 :         return std::string{};
     796     1729201 :     } else if (index + 1 < str.length()) {
     797          29 :         return std::string{str.substr(0, index + 1)};
     798             :     }
     799     1729172 :     return std::string{str};
     800             : }

Generated by: LCOV version 1.13